Skip to content
Open
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
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

| Component | Status | Details |
|-----------|--------|---------|
| **Smithy Spec** | 209 operations | Single source of truth for all APIs |
| **Smithy Spec** | 217 operations | Single source of truth for all APIs |
| **Go SDK** | Production-ready | Full generated client + service wrappers |
| **TypeScript SDK** | Production-ready | 45 generated services, openapi-fetch based |
| **Ruby SDK** | Production-ready | 45 generated services |
Expand All @@ -31,7 +31,7 @@ Smithy Spec → OpenAPI → Generated Client → Service Layer → User
| **Kotlin** | Ktor via `BaseService` | `sdk/src/commonMain/kotlin/.../generated/services/*.kt` |
| **Python** | httpx via `HttpClient` | `src/basecamp/generated/services/*.py` |

All 209 operations across the ~45-service per-SDK layer are generated. Hand-written code is limited to infrastructure:
All 217 operations across the ~45-service per-SDK layer are generated. Hand-written code is limited to infrastructure:

| Purpose | TypeScript | Ruby | Swift | Kotlin | Python |
|---------|-----------|------|-------|--------|--------|
Expand Down
6 changes: 3 additions & 3 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ END

### behavior-model.json Retry Patterns

All 209 operations in `behavior-model.json` use `retry_on: [429, 503]`. Three `(max, base_delay_ms)` patterns exist:
All 217 operations in `behavior-model.json` use `retry_on: [429, 503]`. Three `(max, base_delay_ms)` patterns exist:
- `(2, 1000)` — most create operations
- `(3, 1000)` — most read/update/delete operations
- `(3, 2000)` — `CreateAttachment`, `CreateCampfireUpload` (file uploads)
Expand Down Expand Up @@ -1548,9 +1548,9 @@ Every operation has a `retry` block, including non-idempotent POSTs. For non-ide

### Operation Counts

- Total operations: 209
- Total operations: 217
- Idempotent: 69 (flagged with `idempotent: true`)
- Non-idempotent: 140 (no `idempotent` field, or not present)
- Non-idempotent: 148 (no `idempotent` field, or not present)
- All operations use `retry_on: [429, 503]`

---
Expand Down
120 changes: 120 additions & 0 deletions behavior-model.json
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,126 @@
]
}
},
"GetEverythingBoosts": {
"readonly": true,
"pagination": {
"style": "link",
"maxPageSize": 50
},
"retry": {
"max": 3,
"base_delay_ms": 1000,
"backoff": "exponential",
"retry_on": [
429,
503
]
}
},
"GetEverythingCheckins": {
"readonly": true,
"pagination": {
"style": "link",
"maxPageSize": 50
},
"retry": {
"max": 3,
"base_delay_ms": 1000,
"backoff": "exponential",
"retry_on": [
429,
503
]
}
},
"GetEverythingComments": {
"readonly": true,
"pagination": {
"style": "link",
"maxPageSize": 50
},
"retry": {
"max": 3,
"base_delay_ms": 1000,
"backoff": "exponential",
"retry_on": [
429,
503
]
}
},
"GetEverythingFiles": {
"readonly": true,
"pagination": {
"style": "link",
"maxPageSize": 50
},
"retry": {
"max": 3,
"base_delay_ms": 1000,
"backoff": "exponential",
"retry_on": [
429,
503
]
}
},
"GetEverythingForwards": {
"readonly": true,
"pagination": {
"style": "link",
"maxPageSize": 50
},
"retry": {
"max": 3,
"base_delay_ms": 1000,
"backoff": "exponential",
"retry_on": [
429,
503
]
}
},
"GetEverythingMessages": {
"readonly": true,
"pagination": {
"style": "link",
"maxPageSize": 50
},
"retry": {
"max": 3,
"base_delay_ms": 1000,
"backoff": "exponential",
"retry_on": [
429,
503
]
}
},
"GetEverythingOverdueCards": {
"readonly": true,
"retry": {
"max": 3,
"base_delay_ms": 1000,
"backoff": "exponential",
"retry_on": [
429,
503
]
}
},
"GetEverythingOverdueTodos": {
"readonly": true,
"retry": {
"max": 3,
"base_delay_ms": 1000,
"backoff": "exponential",
"retry_on": [
429,
503
]
}
},
"GetForward": {
"readonly": true,
"retry": {
Expand Down
32 changes: 32 additions & 0 deletions conformance/runner/go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,38 @@ func executeOperation(ctx context.Context, account *basecamp.AccountClient, tc T
}
return operationResult{err: nil}

case "GetEverythingMessages":
_, err := account.Everything().Messages(ctx, 0)
return operationResult{err: err}

case "GetEverythingComments":
_, err := account.Everything().Comments(ctx, 0)
return operationResult{err: err}

case "GetEverythingCheckins":
_, err := account.Everything().Checkins(ctx, 0)
return operationResult{err: err}

case "GetEverythingForwards":
_, err := account.Everything().Forwards(ctx, 0)
return operationResult{err: err}

case "GetEverythingBoosts":
_, err := account.Everything().Boosts(ctx, 0)
return operationResult{err: err}

case "GetEverythingFiles":
_, err := account.Everything().Files(ctx, 0, nil)
return operationResult{err: err}

case "GetEverythingOverdueTodos":
_, err := account.Everything().OverdueTodos(ctx)
return operationResult{err: err}

case "GetEverythingOverdueCards":
_, err := account.Everything().OverdueCards(ctx)
return operationResult{err: err}

default:
return operationResult{
err: fmt.Errorf("unknown operation: %s", tc.Operation),
Expand Down
16 changes: 16 additions & 0 deletions conformance/runner/python/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,22 @@ def __call__(self, operation: str, *, path_params: dict, query_params: dict, bod
return self._account.tools.enable(tool_id=path_params["toolId"])
case "UploadsDownload":
return self._account.uploads.download(upload_id=path_params["uploadId"])
case "GetEverythingMessages":
return self._account.everything.get_everything_messages()
case "GetEverythingComments":
return self._account.everything.get_everything_comments()
case "GetEverythingCheckins":
return self._account.everything.get_everything_checkins()
case "GetEverythingForwards":
return self._account.everything.get_everything_forwards()
case "GetEverythingBoosts":
return self._account.everything.get_everything_boosts()
case "GetEverythingFiles":
return self._account.everything.get_everything_files()
case "GetEverythingOverdueTodos":
return self._account.everything.get_everything_overdue_todos()
case "GetEverythingOverdueCards":
return self._account.everything.get_everything_overdue_cards()
case _:
raise ValueError(f"Unknown operation: {operation}")

Expand Down
16 changes: 16 additions & 0 deletions conformance/runner/ruby/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,22 @@ def call(operation, path_params: {}, query_params: {}, body: nil, path: "")
todo_id: path_params["todoId"],
**todo_write_kwargs(body)
)
when "GetEverythingMessages"
@account.everything.get_everything_messages.to_a
when "GetEverythingComments"
@account.everything.get_everything_comments.to_a
when "GetEverythingCheckins"
@account.everything.get_everything_checkins.to_a
when "GetEverythingForwards"
@account.everything.get_everything_forwards.to_a
when "GetEverythingBoosts"
@account.everything.get_everything_boosts.to_a
when "GetEverythingFiles"
@account.everything.get_everything_files.to_a
when "GetEverythingOverdueTodos"
@account.everything.get_everything_overdue_todos
when "GetEverythingOverdueCards"
@account.everything.get_everything_overdue_cards
else
raise "Unknown operation: #{operation}"
end
Expand Down
36 changes: 36 additions & 0 deletions conformance/runner/typescript/live-dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,42 @@ export const LIVE_OPERATIONS: Record<string, DispatchSpec> = {
},
},

// Everything aggregates — flat family (one canary per group). The paginated
// roots pass maxItems so live mode samples each account-wide feed instead of
// following every Link page; the overdue lists are unpaginated (no options).
GetEverythingMessages: {
fixtures: [],
call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingMessages({ maxItems: 5 }) }),
},
GetEverythingComments: {
fixtures: [],
call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingComments({ maxItems: 5 }) }),
},
GetEverythingCheckins: {
fixtures: [],
call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingCheckins({ maxItems: 5 }) }),
},
GetEverythingForwards: {
fixtures: [],
call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingForwards({ maxItems: 5 }) }),
},
GetEverythingBoosts: {
fixtures: [],
call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingBoosts({ maxItems: 5 }) }),
},
GetEverythingFiles: {
fixtures: [],
call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingFiles({ maxItems: 5 }) }),
},
GetEverythingOverdueTodos: {
fixtures: [],
call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingOverdueTodos() }),
},
GetEverythingOverdueCards: {
fixtures: [],
call: async (ctx) => ({ resolvedIds: {}, result: await ctx.client.everything.everythingOverdueCards() }),
},

GetMyProfile: {
fixtures: [],
call: async (ctx) => {
Expand Down
32 changes: 32 additions & 0 deletions conformance/runner/typescript/runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,38 @@ async function executeOperation(
await client.reports.personProgress(Number(params.personId));
break;

case "GetEverythingMessages":
await client.everything.everythingMessages();
break;

case "GetEverythingComments":
await client.everything.everythingComments();
break;

case "GetEverythingCheckins":
await client.everything.everythingCheckins();
break;

case "GetEverythingForwards":
await client.everything.everythingForwards();
break;

case "GetEverythingBoosts":
await client.everything.everythingBoosts();
break;

case "GetEverythingFiles":
await client.everything.everythingFiles();
break;

case "GetEverythingOverdueTodos":
await client.everything.everythingOverdueTodos();
break;

case "GetEverythingOverdueCards":
await client.everything.everythingOverdueCards();
break;

case "GetTool":
await client.tools.get(Number(params.toolId));
break;
Expand Down
Loading
Loading