Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
435 commits
Select commit Hold shift + click to select a range
eeac98d
fix(kanban): isolate memory operations
olliethedev Aug 27, 2026
5953147
fix(kanban): guard column order updates
olliethedev Aug 27, 2026
4ae7249
docs(kanban): document compound move permissions
olliethedev Aug 27, 2026
2387024
fix(kanban): reject undefined column order
olliethedev Aug 27, 2026
45ba8fb
fix(kanban): close lifecycle and cache gaps
olliethedev Aug 28, 2026
c09e998
fix(kanban): rotate caches during identity refetch
olliethedev Aug 28, 2026
5d1475d
chore(kanban): refresh registry lifecycle types
olliethedev Aug 28, 2026
7813dda
fix(kanban): isolate failed identity queries
olliethedev Aug 28, 2026
b27d37c
perf(kanban): avoid hydrating tasks in board lists
olliethedev Aug 28, 2026
326e92b
style(kanban): format list query regression
olliethedev Aug 28, 2026
e41fd2b
fix(kanban): export board summary getter
olliethedev Aug 28, 2026
0c80706
Merge pull request #203 from better-stack-ai/feat/189-kanban-authoriz…
olliethedev Aug 28, 2026
2f3c517
feat(media): migrate authorization to typed operations (#190)
olliethedev Aug 28, 2026
31cbb8a
fix(media): harden authorization boundaries
olliethedev Aug 28, 2026
06a8502
fix(media): enforce exact selection and move facts
olliethedev Aug 28, 2026
29a8d52
test(media): cover library route authorization
olliethedev Aug 28, 2026
aff210b
test(media): restore route console spies
olliethedev Aug 28, 2026
8b78826
fix(media): isolate refetches and scaffold transactions
olliethedev Aug 28, 2026
fbdfe84
fix(media): normalize CAS results and current-list refreshes
olliethedev Aug 28, 2026
e1b3870
fix(media): harden concurrent cache refreshes
olliethedev Aug 28, 2026
5c26761
fix(codegen): leave placeholder auth unconfigured
olliethedev Aug 28, 2026
3611521
fix(media): commit identity cache ownership
olliethedev Aug 28, 2026
7b3a521
fix(media): close unmount refresh window
olliethedev Aug 28, 2026
cb1894b
fix(media): document transactions and complete folder facts
olliethedev Aug 28, 2026
4e2552e
fix(media): hydrate the complete folder tree
olliethedev Aug 28, 2026
50e5692
fix(media): secure recursive folder deletion
olliethedev Aug 28, 2026
b7e10ff
fix(media): recheck recursive delete invariants
olliethedev Aug 28, 2026
75b92c6
fix(media): preserve tenant scope in internal writes
olliethedev Aug 28, 2026
cfe161c
test(media): cover tenant-bound upload initialization
olliethedev Aug 28, 2026
2d7cd1a
fix(media): report folder loader failures
olliethedev Aug 28, 2026
f1fd04e
Merge pull request #204 from better-stack-ai/feat/190-media-authoriza…
olliethedev Aug 28, 2026
c625710
feat(ai-chat): add typed operation authorization
olliethedev Aug 28, 2026
9f33dab
fix(ai-chat): harden authorization lifecycle
olliethedev Aug 28, 2026
c740d31
fix(ai-chat): validate public tool continuations
olliethedev Aug 28, 2026
38ba16e
fix(ai-chat): harden authorization races
olliethedev Aug 28, 2026
54736aa
fix(codegen): forward AI chat SSR headers
olliethedev Aug 28, 2026
3870af4
test(ai-chat): align framework flows with access modes
olliethedev Aug 28, 2026
196e246
test(ai-chat): make tool denial explicit
olliethedev Aug 28, 2026
c6370b5
fix(ai-chat): preserve hook denial status
olliethedev Aug 28, 2026
6ae2bde
fix(ai-chat): recover failed history sync
olliethedev Aug 28, 2026
25008e0
chore: update shadcn registry [skip ci]
github-actions[bot] Aug 28, 2026
a2d7543
fix(ai-chat): isolate history compatibility writes
olliethedev Aug 28, 2026
844a339
fix(ai-chat): require isolated stream persistence
olliethedev Aug 28, 2026
7d4b657
fix(ai-chat): serialize memory stream rollbacks
olliethedev Aug 28, 2026
3e2acdf
fix(ai-chat): serialize memory history transactions
olliethedev Aug 28, 2026
2b9472c
fix(ai-chat): serialize memory history writes
olliethedev Aug 28, 2026
e2b3ba7
fix(ai-chat): allow reentrant memory hooks
olliethedev Aug 28, 2026
a3d6117
fix(ai-chat): isolate shared memory adapter writes
olliethedev Aug 28, 2026
2362457
Merge pull request #205 from better-stack-ai/feat/191-ai-chat-operati…
olliethedev Aug 28, 2026
9df9d8b
feat(stack): validate explicit endpoint inventory
olliethedev Aug 28, 2026
23d4318
fix(stack): close endpoint inventory gaps
olliethedev Aug 28, 2026
7815ca7
fix(stack): bind endpoints to operation transports
olliethedev Aug 28, 2026
7b05d34
fix(stack): make operation routes own execution
olliethedev Aug 28, 2026
3859a3b
fix(stack): preserve operation route validation
olliethedev Aug 28, 2026
7dfa53d
test(stack): cover route validation boundaries
olliethedev Aug 28, 2026
1b99929
Merge pull request #206 from better-stack-ai/feat/192-explicit-endpoi…
olliethedev Aug 28, 2026
99bf97c
feat(auth): finalize v3 authorization contract
olliethedev Aug 28, 2026
c4e5e05
fix(auth): close generated route bypasses
olliethedev Aug 28, 2026
49cea8b
fix(auth): retire provider-specific scaffolding
olliethedev Aug 28, 2026
e72e776
docs(auth): clarify lifecycle and singleton contracts
olliethedev Aug 28, 2026
ab00222
docs(auth): bind server authorization in integration guide
olliethedev Aug 28, 2026
ff39997
fix(auth): migrate remaining raw API consumers
olliethedev Aug 28, 2026
b82768a
Merge pull request #207 from better-stack-ai/feat/193-final-authoriza…
olliethedev Aug 28, 2026
48f6fcb
feat(stack): add symmetric stack constructors (#208)
olliethedev Aug 28, 2026
2f6b4d8
test(stack): keep constructor fixtures knip-clean
olliethedev Aug 28, 2026
e80c282
test(stack): verify public constructor inference
olliethedev Aug 28, 2026
1644f3f
test(stack): allow artifact compilers under CI load
olliethedev Aug 28, 2026
47d5b0e
Merge pull request #226 from better-stack-ai/feat/208-symmetric-stack…
olliethedev Aug 28, 2026
41ac31b
feat(stack): resolve shared client runtime
olliethedev Aug 28, 2026
d565eb9
fix(stack): typecheck runtime fixture before build
olliethedev Aug 28, 2026
f1796ff
fix(stack): harden client runtime projection
olliethedev Aug 28, 2026
4e7d407
fix(stack): validate endpoint records and credentials
olliethedev Aug 28, 2026
6f25617
fix(stack): preserve prototype-like plugin keys
olliethedev Aug 28, 2026
c732a75
fix(stack): ignore inherited endpoint values
olliethedev Aug 28, 2026
4d1ccae
fix(stack): ignore inherited runtime discriminators
olliethedev Aug 28, 2026
ad297a9
fix(stack): ignore inherited transport configuration
olliethedev Aug 28, 2026
a465458
fix(stack): isolate resolved runtime prototypes
olliethedev Aug 28, 2026
0b4ff31
Merge pull request #227 from better-stack-ai/feat/209-resolved-client…
olliethedev Aug 28, 2026
18cbe93
feat(stack): infer plugin ids and provider overrides
olliethedev Aug 28, 2026
12deae4
fix(stack): harden canonical plugin ids
olliethedev Aug 28, 2026
e6136e6
Merge pull request #228 from better-stack-ai/feat/210-plugin-id-infer…
olliethedev Aug 28, 2026
a8d249b
fix(stack): bind resources to resolved query client
olliethedev Aug 28, 2026
3dc63e4
feat(stack): normalize backend plugin factories
olliethedev Aug 28, 2026
9569d7b
feat(stack): migrate Blog to resolved client runtime
olliethedev Aug 28, 2026
805df36
test(stack): complete backend factory coverage
olliethedev Aug 28, 2026
383358b
docs(stack): finish backend factory migration
olliethedev Aug 28, 2026
a44af4e
fix(codegen): bridge mixed client runtime migration
olliethedev Aug 28, 2026
acb02c2
docs(stack): use canonical plugin id in example
olliethedev Aug 28, 2026
9c881ca
fix(codegen): type mixed provider overrides
olliethedev Aug 28, 2026
153d446
fix(codegen): preserve React Router hydration cache
olliethedev Aug 28, 2026
91a9c9f
Merge pull request #229 from better-stack-ai/feat/217-backend-factories
olliethedev Aug 28, 2026
d159aa6
fix(codegen): adapt Next page client resolver
olliethedev Aug 28, 2026
6d446a9
feat(stack): normalize AI Chat lifecycle hooks
olliethedev Aug 28, 2026
169afc5
docs: record AI Chat lifecycle migration
olliethedev Aug 28, 2026
935e247
chore: update Blog registry artifact
olliethedev Aug 28, 2026
2d264ed
fix(blog): harden resolved runtime migration
olliethedev Aug 28, 2026
8709574
docs: align lifecycle naming guidance
olliethedev Aug 28, 2026
37ca499
docs(blog): teach resolved client stack runtime
olliethedev Aug 28, 2026
9f6217c
Merge pull request #231 from better-stack-ai/feat/218-ai-chat-lifecycle
olliethedev Aug 28, 2026
54d8130
docs(blog): align resolved runtime guidance
olliethedev Aug 28, 2026
42e6eae
docs(blog): separate server and browser runtime wiring
olliethedev Aug 28, 2026
6a64077
feat(stack): normalize workflow lifecycle hooks
olliethedev Aug 28, 2026
0498985
Merge pull request #230 from better-stack-ai/feat/212-blog-client-run…
olliethedev Aug 28, 2026
d9f63d6
docs: complete workflow hook migration maps
olliethedev Aug 28, 2026
047c768
feat(stack): normalize content lifecycle hooks
olliethedev Aug 28, 2026
89c90ba
docs(stack): align content lifecycle examples
olliethedev Aug 28, 2026
6bab147
Merge pull request #233 from better-stack-ai/feat/221-workflow-lifecycle
olliethedev Aug 28, 2026
da033d3
Merge pull request #234 from better-stack-ai/feat/220-content-lifecycle
olliethedev Aug 28, 2026
9065ca6
feat(stack): clarify server trust surfaces
olliethedev Aug 28, 2026
3794954
docs(stack): distinguish trusted and raw access
olliethedev Aug 28, 2026
d17b3be
Merge pull request #235 from better-stack-ai/feat/222-server-trust-su…
olliethedev Aug 28, 2026
b060436
feat(stack): centralize Comments client runtime
olliethedev Aug 28, 2026
e79b3d6
docs(comments): clarify resolved client runtime
olliethedev Aug 28, 2026
ce62021
docs(comments): align runtime guidance
olliethedev Aug 28, 2026
7ad53e2
fix(comments): normalize metadata site paths
olliethedev Aug 28, 2026
57592d0
Merge pull request #237 from better-stack-ai/feat/214-comments-client…
olliethedev Aug 28, 2026
8c76f40
feat(stack): resolve CMS client runtime centrally
olliethedev Aug 28, 2026
2445d77
docs(stack): clarify loader error hooks
olliethedev Aug 28, 2026
f2ded9f
fix(stack): honor plugin site paths in CMS navigation
olliethedev Aug 28, 2026
e14c898
fix(stack): bind UI Builder to CMS client runtime
olliethedev Aug 28, 2026
4005d1a
docs(stack): correct UI Builder loader example
olliethedev Aug 28, 2026
4bf5550
fix(stack): join root plugin site paths safely
olliethedev Aug 29, 2026
0354347
fix(codegen): resolve comments runtime in scaffolds
olliethedev Aug 29, 2026
1b15f25
fix(codegen): align canonical client scaffolds
olliethedev Aug 29, 2026
a0a0192
Merge pull request #236 from better-stack-ai/feat/213-cms-ui-builder-…
olliethedev Aug 29, 2026
59823a0
feat(stack): migrate AI chat client runtime
olliethedev Aug 28, 2026
9b8dd12
chore(stack): refresh AI Chat registry artifact
olliethedev Aug 28, 2026
eefb712
fix(stack): address AI Chat client review
olliethedev Aug 28, 2026
4809aec
fix(stack): align AI Chat browser runtime
olliethedev Aug 28, 2026
894d9bb
fix(stack): enforce AI Chat factory mode
olliethedev Aug 28, 2026
184b2e7
fix(codegen): use valid root site path
olliethedev Aug 28, 2026
5028340
fix(stack): normalize AI Chat site paths
olliethedev Aug 28, 2026
8fc9b9f
fix(stack): align AI Chat resolved endpoints
olliethedev Aug 28, 2026
e14bcf2
fix(stack): navigate before deleted chat refresh
olliethedev Aug 28, 2026
bc8e947
chore: update shadcn registry
github-actions[bot] Aug 29, 2026
c645739
fix(stack): isolate deleted chat refresh
olliethedev Aug 29, 2026
6b0947a
fix(stack): evict deleted chat details
olliethedev Aug 29, 2026
25c8e9a
test(e2e): scope public chat message assertions
olliethedev Aug 29, 2026
3965ab7
fix(stack): sanitize AI Chat loader cache failures
olliethedev Aug 29, 2026
cdcb16e
docs(ai-chat): fix ejected conversation example
olliethedev Aug 29, 2026
b2ab26d
Merge pull request #232 from better-stack-ai/feat/211-ai-chat-client-…
olliethedev Aug 29, 2026
e974b06
feat(stack): resolve form and kanban client runtimes
olliethedev Aug 29, 2026
efe8640
fix(codegen): emit resolved comments client factory
olliethedev Aug 29, 2026
de8ffbe
fix(stack): harden workflow site navigation
olliethedev Aug 29, 2026
e544378
chore: update shadcn registry [skip ci]
github-actions[bot] Aug 29, 2026
ae8f577
chore: verify registry sync
olliethedev Aug 29, 2026
fbb5e29
fix(stack): centralize plugin site navigation
olliethedev Aug 29, 2026
6072b23
docs(stack): document plugin site navigation
olliethedev Aug 29, 2026
a98ed24
fix(stack): require kanban provider callbacks
olliethedev Aug 29, 2026
f9e491a
fix(codegen): configure kanban preview providers
olliethedev Aug 29, 2026
5d4bba9
docs(form-builder): correct loader hook signatures
olliethedev Aug 29, 2026
4264e86
Merge pull request #238 from better-stack-ai/feat/215-form-kanban-cli…
olliethedev Aug 29, 2026
20c6742
feat(stack): resolve media and route docs clients
olliethedev Aug 29, 2026
7c5a44d
fix(media): authenticate Vercel Blob token exchange
olliethedev Aug 29, 2026
d1b6b09
fix(stack): isolate resolved media and route docs runtimes
olliethedev Aug 29, 2026
674b696
docs(media): distinguish request and browser stacks
olliethedev Aug 29, 2026
9a47fc6
docs(media): clarify upload transport contracts
olliethedev Aug 29, 2026
b95a741
fix(codegen): emit canonical media client factories
olliethedev Aug 29, 2026
c731919
fix(route-docs): bind introspection to client stack
olliethedev Aug 29, 2026
9232515
fix(media): resolve assets against plugin origin
olliethedev Aug 29, 2026
4ddedf0
fix(route-docs): scope schema cache per stack
olliethedev Aug 29, 2026
b3e7791
fix(media): normalize registration mutation results
olliethedev Aug 29, 2026
393acf8
fix(route-docs): isolate identical stack caches
olliethedev Aug 29, 2026
125e91d
fix(codegen): resolve browser stack origins
olliethedev Aug 29, 2026
e562a58
fix(route-docs): keep schema loading client-side
olliethedev Aug 29, 2026
0318597
fix(media): preserve mutation data identity
olliethedev Aug 29, 2026
cd003b9
fix(route-docs): stabilize reconstructed cache keys
olliethedev Aug 29, 2026
3edbd8e
fix(route-docs): key schemas by sitemap output
olliethedev Aug 29, 2026
2a4fe1e
fix(stack): isolate client-only route docs caches
olliethedev Aug 29, 2026
49fbc7f
fix(media): lazy load library page
olliethedev Aug 29, 2026
1b78fc3
fix(stack): bind media and route docs caches
olliethedev Aug 29, 2026
f16ad13
docs(media): describe loader context
olliethedev Aug 29, 2026
558ce59
docs(media): clarify identity hydration
olliethedev Aug 29, 2026
5034949
fix(media): partition resolved query caches
olliethedev Aug 29, 2026
010c3c9
docs(media): explain endpoint-scoped query keys
olliethedev Aug 29, 2026
e9ff944
Merge pull request #239 from better-stack-ai/feat/216-media-route-doc…
olliethedev Aug 29, 2026
29475bd
feat(registry): add canonical route docs consumer
olliethedev Aug 29, 2026
f423d00
feat(cli): generate canonical v3 stack wiring
olliethedev Aug 29, 2026
03259a0
fix(cli): seed through trusted plugin operations
olliethedev Aug 29, 2026
08525b5
feat(codegen): migrate framework overlays to canonical stacks
olliethedev Aug 29, 2026
4c11f24
fix(route-docs): preserve ejected page override
olliethedev Aug 29, 2026
1ba5f25
test(registry): require owned installs to succeed
olliethedev Aug 29, 2026
6e8dec3
fix(codegen): keep identity partitions server-only
olliethedev Aug 29, 2026
7ca7580
fix(codegen): align canonical stack helper overlays
olliethedev Aug 29, 2026
4581c0b
test(registry): validate composed installs deterministically
olliethedev Aug 29, 2026
99f2fea
test(codegen): enforce canonical extension boundaries
olliethedev Aug 29, 2026
5bf4697
ci(registry): validate pinned fixture updates
olliethedev Aug 29, 2026
05dc636
fix(cli): keep extension fixtures factory-shaped
olliethedev Aug 29, 2026
48aa24b
fix(codegen): hydrate Vite provider origins
olliethedev Aug 29, 2026
be2bb10
test(codegen): tolerate optional runtime API typing
olliethedev Aug 29, 2026
66dcf6f
docs(auth): document Vite SSR provider origins
olliethedev Aug 29, 2026
7a1c7dc
docs(installation): accept serialized provider origins
olliethedev Aug 29, 2026
d7aecdc
chore(registry): preserve exact fixture snapshots
olliethedev Aug 29, 2026
1ece824
fix(codegen): reconcile WealthReview queue columns
olliethedev Aug 29, 2026
16739ec
fix(codegen): trust configured request destinations
olliethedev Aug 29, 2026
5561b6e
fix(codegen): hydrate trusted client origins
olliethedev Aug 29, 2026
c270af9
test(stack): prove managed origin hydration
olliethedev Aug 29, 2026
a545aec
fix(codegen): require explicit public site origins
olliethedev Aug 29, 2026
add01c4
fix(codegen): hydrate standalone client origins
olliethedev Aug 29, 2026
990e5ee
fix(codegen): hydrate maintained standalone origins
olliethedev Aug 29, 2026
eaae014
fix(codegen): secure managed API browser flows
olliethedev Aug 29, 2026
b7e4674
fix(codegen): derive reverse sync manifests
olliethedev Aug 29, 2026
6bc3510
docs: include managed API browser credentials
olliethedev Aug 29, 2026
dd98360
fix(codegen): align Next request origins
olliethedev Aug 29, 2026
82df2b6
fix(codegen): migrate legacy Next page routes
olliethedev Aug 29, 2026
3e93744
fix(codegen): verify legacy scaffold snapshots
olliethedev Aug 29, 2026
bc0d361
fix(codegen): migrate historical Next scaffolds safely
olliethedev Aug 29, 2026
da58fff
docs(integration): align public API origin names
olliethedev Aug 29, 2026
3b53b6c
fix(codegen): preserve supported legacy scaffold variants
olliethedev Aug 29, 2026
5559dd3
fix(codegen): verify historical migration inputs
olliethedev Aug 29, 2026
44c3e06
fix(codegen): fail closed around legacy child routes
olliethedev Aug 29, 2026
3dd2141
Merge pull request #240 from better-stack-ai/feat/223-canonical-gener…
olliethedev Aug 29, 2026
1e1e0e7
docs(v3): publish canonical DX migration guide (#224)
olliethedev Aug 29, 2026
dbea244
test(v3): validate migration guard markers (#224)
olliethedev Aug 29, 2026
76a2f4a
docs(v3): address canonical DX review findings (#224)
olliethedev Aug 29, 2026
03fcd7e
test(v3): cover contextual legacy hook forms (#224)
olliethedev Aug 29, 2026
70f2fc8
refactor(v3): centralize canonical guard inventory (#224)
olliethedev Aug 29, 2026
248528d
fix(v3): validate referenced plugin configs (#224)
olliethedev Aug 29, 2026
5b4470b
fix(v3): fail closed on opaque factory config (#224)
olliethedev Aug 29, 2026
78a6d56
fix(v3): close canonical guard bypasses (#224)
olliethedev Aug 29, 2026
3911b60
fix(v3): scope canonical config bindings (#224)
olliethedev Aug 29, 2026
127e50b
fix(v3): reject opaque lifecycle hook spreads (#224)
olliethedev Aug 29, 2026
1f8b74a
fix(v3): validate lifecycle hook bindings (#224)
olliethedev Aug 29, 2026
447751a
fix(v3): fail closed on opaque plugin config (#224)
olliethedev Aug 29, 2026
08d3838
fix(v3): close canonical guide gaps (#224)
olliethedev Aug 29, 2026
acfe7cd
fix(v3): harden canonical guard alias parsing (#224)
olliethedev Aug 29, 2026
b72afc7
fix(v3): resolve canonical aliases lexically (#224)
olliethedev Aug 29, 2026
bbe5d4d
fix(v3): bound canonical alias fences (#224)
olliethedev Aug 29, 2026
bc8b2b7
docs(v3): document canonical example exports (#224)
olliethedev Aug 29, 2026
be7a3b8
fix(v3): inspect canonical factory call syntax (#224)
olliethedev Aug 29, 2026
629e1ca
fix(v3): cover namespace factory calls (#224)
olliethedev Aug 29, 2026
faa1619
refactor(v3): index canonical calls by scope (#224)
olliethedev Aug 29, 2026
a456d26
refactor(v3): bound canonical migration guard (#224)
olliethedev Aug 29, 2026
7b53969
fix(v3): scan direct factory calls through comments (#224)
olliethedev Aug 29, 2026
9a4f4c5
fix(v3): tighten bounded canonical matchers (#224)
olliethedev Aug 29, 2026
c9edae7
fix(v3): catch flat aggregate error hooks (#224)
olliethedev Aug 29, 2026
61b4775
Merge pull request #241 from better-stack-ai/docs/224-canonical-dx
olliethedev Aug 29, 2026
ec2a7ff
refactor(stack)!: remove legacy v3 seams
olliethedev Aug 29, 2026
d77acdf
chore: update shadcn registry [skip ci]
github-actions[bot] Aug 29, 2026
55ee445
Merge pull request #242 from better-stack-ai/chore/225-remove-legacy-…
olliethedev Aug 29, 2026
9d107bb
chore: prepare v3.0.0-rc.3
olliethedev Aug 29, 2026
4c4aa13
fix: align RC3 better-call peer
olliethedev Aug 29, 2026
6426b3f
Revert "fix: align RC3 better-call peer"
olliethedev Aug 29, 2026
d00777c
Merge pull request #243 from better-stack-ai/chore/v3.0.0-rc.3
olliethedev Aug 29, 2026
d078cd7
test: add packed npm and pnpm consumer gates (#251)
olliethedev Aug 31, 2026
f90f7b5
fix(codegen): delegate through consumer-aware CLI (#250)
olliethedev Aug 31, 2026
48324db
feat(cli): restore Better Auth UI scaffold (#252)
olliethedev Aug 31, 2026
af1bde8
docs: add stable v3 migration playbook (#175) (#253)
olliethedev Aug 31, 2026
696fe1f
chore: prepare v3.0.0-rc.4 release (#254)
olliethedev Sep 1, 2026
f967b03
chore: prepare v3.0.0 stable release (#255)
olliethedev Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
152 changes: 43 additions & 109 deletions .agents/skills/btst-backend-plugin-dev/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,85 +3,45 @@
## defineBackendPlugin shape (api/plugin.ts)

```typescript
import type { DBAdapter as Adapter } from "@btst/db"
import { defineBackendPlugin, createEndpoint } from "@btst/stack/plugins/api"
import { z } from "zod"
import { dbSchema } from "./db-schema"
import { listItems, getItemById } from "./getters"
import { createItem, updateItem, deleteItem } from "./mutations"

const ItemQuerySchema = z.object({ limit: z.coerce.number().optional() })
const CreateItemSchema = z.object({ name: z.string() })

export const myBackendPlugin = defineBackendPlugin({
name: "my-plugin",
dbPlugin: dbSchema,

// api factory — bound to shared adapter, no HTTP context
api: (adapter: Adapter) => ({
listItems: () => listItems(adapter),
getItemById: (id: string) => getItemById(adapter, id),
createItem: (data: CreateItemInput) => createItem(adapter, data),
}),

// routes factory — HTTP endpoints built with createEndpoint
routes: (adapter: Adapter) => {
const listItemsEndpoint = createEndpoint(
"/items",
{ method: "GET", query: ItemQuerySchema },
async (ctx) => {
return await listItems(adapter)
},
)

const createItemEndpoint = createEndpoint(
"/items",
{ method: "POST", body: CreateItemSchema },
async (ctx) => {
return await createItem(adapter, ctx.body)
},
)

const getItemEndpoint = createEndpoint(
"/items/:id",
{ method: "GET" },
async (ctx) => {
const item = await getItemById(adapter, ctx.params.id)
if (!item) throw ctx.error(404, { message: "Item not found" })
return item
},
)

return { listItems: listItemsEndpoint, createItem: createItemEndpoint, getItem: getItemEndpoint } as const
},
})

// Router type for client consumption
export type MyApiRouter = ReturnType<ReturnType<typeof myBackendPlugin>["routes"]>
```

### ctx object inside createEndpoint handlers
/** Configuration accepted by `myBackendPlugin`. */
export interface MyBackendPluginOptions {
/** Lifecycle callbacks composed around plugin operations. */
hooks?: MyBackendHooks
}

| Property | Description |
|---|---|
| `ctx.query` | Validated query params (when `query:` schema provided) |
| `ctx.body` | Validated request body (when `body:` schema provided) |
| `ctx.params` | URL path params (e.g. `:id` → `ctx.params.id`) |
| `ctx.headers` | Request `Headers` object |
| `ctx.request` | Raw `Request` object |
| `ctx.error(status, { message })` | Create an HTTP error — always `throw` the result |
export const myBackendPlugin = (options: MyBackendPluginOptions = {}) =>
defineBackendPlugin({
id: "myPlugin",
dbPlugin: dbSchema,
operations: (adapter) => createMyOperations(adapter, options.hooks),
raw: (adapter) => ({
prefetchForRoute: createItemPrefetchForRoute(adapter),
}),
routes: (_adapter, _context, operations) => {
const createItem = createEndpoint(
"/items",
{ method: "POST", body: CreateItemSchema, requireRequest: true },
operations.createItem.route((ctx) => ctx.body),
)
return { createItem } as const
},
})

---
/** Inferred router contract imported by the client plugin. */
export type MyApiRouter = ReturnType<
ReturnType<typeof myBackendPlugin>["routes"]
>
```

## getters.ts

Pure DB functions — no HTTP context, no lifecycle hooks, always accept `adapter` as first arg:
Lower-level DB functions — no HTTP context or lifecycle composition, always accept `adapter` as first arg:

```typescript
import type { DBAdapter as Adapter } from "@btst/db"
import type { Item } from "./types"

// Authorization hooks are NOT called — callers are responsible for access control
// Lower-level primitive: callers own validation and lifecycle composition.
export async function listItems(adapter: Adapter): Promise<Item[]> {
return adapter.findMany({ model: "item" })
}
Expand All @@ -95,15 +55,15 @@ export async function getItemById(adapter: Adapter, id: string): Promise<Item |

## mutations.ts

Write operations — no auth hooks, no HTTP context. JSDoc disclaimer required:
Write operations — no HTTP context or lifecycle composition. JSDoc disclaimer required:

```typescript
import type { DBAdapter as Adapter } from "@btst/db"
import type { CreateItemInput, Item } from "./types"

/**
* Create a new item directly in the database.
* Authorization hooks are NOT called — caller is responsible for access control.
* Lower-level primitive: caller owns validation and lifecycle composition.
*/
export async function createItem(adapter: Adapter, data: CreateItemInput): Promise<Item> {
return adapter.create({
Expand All @@ -114,7 +74,7 @@ export async function createItem(adapter: Adapter, data: CreateItemInput): Promi

/**
* Update an existing item.
* Authorization hooks are NOT called — caller is responsible for access control.
* Lower-level primitive: caller owns validation and lifecycle composition.
*/
export async function updateItem(
adapter: Adapter,
Expand All @@ -126,7 +86,7 @@ export async function updateItem(

/**
* Delete an item.
* Authorization hooks are NOT called — caller is responsible for access control.
* Lower-level primitive: caller owns validation and lifecycle composition.
*/
export async function deleteItem(adapter: Adapter, id: string): Promise<void> {
await adapter.delete({ model: "item", where: { id } })
Expand All @@ -140,10 +100,10 @@ export async function deleteItem(adapter: Adapter, id: string): Promise<void> {
Re-export getters and mutations for direct server-side import (SSG, scripts, AI tools):

```typescript
// Getters — read-only, no auth hooks
// Getters — read-only lower-level primitives
export { listItems, getItemById } from "./getters"

// Mutations — write ops, no auth hooks
// Mutations — write lower-level primitives
export { createItem, updateItem, deleteItem } from "./mutations"

// Types for consumers
Expand All @@ -154,52 +114,26 @@ export { serializeItem } from "./serializers"

---

## Lifecycle hook implementation in routes
## Lifecycle hooks in operations

```typescript
routes: (adapter: Adapter) => {
const createItemEndpoint = createEndpoint(
"/items",
{ method: "POST", body: CreateItemSchema },
async (ctx) => {
const context = { body: ctx.body, headers: ctx.headers }

// before hook — throw to deny
await ctx.hooks?.onBeforeItemCreated?.(ctx.body, context)

const item = await createItem(adapter, ctx.body)

// after hook — fire and forget or await
await ctx.hooks?.onAfterItemCreated?.(item, context)

return item
},
)

return { createItem: createItemEndpoint } as const
},
```

Hook naming always follows: `onBefore{Entity}{Action}`, `onAfter{Entity}{Action}`, `on{Entity}{Action}Error`.

---
Invoke domain hooks from the operation lifecycle after authorization. Hooks can enforce domain invariants, publish side effects, and observe errors; they are not the authorization policy.

## Plugin stack() wiring (in stack.ts)
## Plugin `createBackendStack()` wiring (in stack.ts)

```typescript
import { stack } from "@btst/stack"
import { createBackendStack } from "@btst/stack/api"
import { myBackendPlugin } from "./src/plugins/my-plugin/api/plugin"

export const myStack = stack({
export const myStack = createBackendStack({
basePath: "/api/data",
plugins: {
myPlugin: myBackendPlugin,
myPlugin: myBackendPlugin(),
},
adapter: (db) => createDrizzleAdapter(schema, db, {}),
})

export const { handler, dbSchema } = myStack

// Direct server-side access (bypasses auth hooks):
const items = await myStack.api.myPlugin.listItems()
// Explicitly trusted business access keeps validation and lifecycle hooks:
const item = await myStack.trusted.myPlugin.createItem({ name: "Scheduled item" })
```
51 changes: 30 additions & 21 deletions .agents/skills/btst-backend-plugin-dev/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: btst-backend-plugin-dev
description: Patterns for writing BTST backend plugins inside the monorepo, including defineBackendPlugin structure, getters.ts/mutations.ts separation, the api factory, lifecycle hook naming conventions, and accessing the adapter in AI tool execute functions. Use when creating or modifying a backend plugin, adding DB getters or mutations, wiring the api factory, or implementing lifecycle hooks in src/plugins/{name}/api/.
description: Patterns for writing BTST backend plugins inside the monorepo, including operation-first authorization, getters.ts/mutations.ts separation, lifecycle hooks, and narrow SSG raw factories. Use when creating or modifying a backend plugin, adding DB getters or mutations, operations, permission descriptors, or lifecycle hooks in src/plugins/{name}/api/.
---

# BTST Backend Plugin Development
Expand All @@ -12,19 +12,21 @@ src/plugins/{name}/
api/
plugin.ts ← defineBackendPlugin entry
getters.ts ← read-only DB functions (no HTTP context)
mutations.ts ← write DB functions (no auth hooks)
mutations.ts ← lower-level write DB functions
operations.ts ← validated operations, permission facts, lifecycle
index.ts ← re-exports getters + mutations + types
query-keys.ts ← React Query key factory
```

## Rules

- **`getters.ts`** — pure async DB functions only. No HTTP context, no lifecycle hooks. Always takes `adapter` as first arg.
- **`mutations.ts`** — write operations (create/update/delete). No auth hooks, no HTTP context. Add JSDoc: "Authorization hooks are NOT called."
- **`mutations.ts`** — lower-level write primitives. No HTTP context or lifecycle composition. Document that callers own validation and lifecycle.
- **`api/index.ts`** — re-export everything from getters + mutations for direct server-side import.
- The `api` factory and `routes` factory share the same adapter instance — bind getters inside the factory, don't pass adapter at call site.
- If the plugin has a one-time init step (e.g. `syncContentTypes`), call it inside each getter/mutation wrapper — not only inside `routes`.
- **Never** use `myStack.api.*` as a substitute for authenticated HTTP endpoints — auth hooks are not called.
- **`operations.ts`** — define the one maintained business inventory with input validation, exact permission descriptors, authoritative facts, domain execution, and lifecycle hooks.
- Bind HTTP routes to same-key operations. Use `operationRouteMap` only for real route-name mismatches.
- The optional `raw` factory is narrow: first-party plugins expose only `prefetchForRoute` for SSG. Do not duplicate business getters or mutations on `createBackendStack().raw`.
- Use `myStack.forRequest(request).operations.*` for request work and `myStack.trusted.*` for explicitly trusted jobs.

## Key patterns

Expand All @@ -38,30 +40,37 @@ src/plugins/{name}/

## Lifecycle hook naming

Pattern: `onBefore{Entity}{Action}`, `onAfter{Entity}{Action}`, `on{Entity}{Action}Error`
Pattern: `onBefore{Action}{Entity}`, `onAfter{Action}{Entity}`, `onError{Action}{Entity}`

```typescript
// Examples from existing plugins:
onBeforeListPosts, onPostsRead, onListPostsError
onBeforeCreatePost, onPostCreated, onCreatePostError
onBeforeUpdatePost, onPostUpdated, onUpdatePostError
onBeforeDeletePost, onPostDeleted, onDeletePostError
onBeforePost, onAfterPost // comments plugin (create comment)
onBeforeEdit, onAfterEdit // comments plugin (edit comment)
onBeforeDelete, onAfterDelete // comments plugin (delete comment)
onBeforeStatusChange, onAfterApprove
onBeforeListPosts, onAfterListPosts, onErrorListPosts
onBeforeCreatePost, onAfterCreatePost, onErrorCreatePost
onBeforeUpdatePost, onAfterUpdatePost, onErrorUpdatePost
onBeforeDeletePost, onAfterDeletePost, onErrorDeletePost

// Preserve meaningful domain events instead of inventing CRUD phases.
onBeforeChat, onAfterChat, onErrorChat
onBeforeActivateTools
```

## Adapter in AI tool execute functions
Normalize names without adding lifecycle phases that the plugin does not
already support.

## Trusted operations in AI tool execute functions

`myStack` is a module-level const. The `execute` closure runs lazily (only on HTTP request), so `myStack` is always initialised by then:

```typescript
export const myStack = stack({ ... })
import { createBackendStack } from "@btst/stack/api"

export const myStack = createBackendStack({ ... })

const myTool = tool({
execute: async (params) => {
await createKanbanTask(myStack.adapter, { title: params.title, columnId: "col-id" })
await myStack.trusted.kanban.createTask({
title: params.title,
columnId: "col-id",
})
return { success: true }
}
})
Expand All @@ -73,8 +82,8 @@ const myTool = tool({
- **Wrong adapter type** — use `import type { DBAdapter as Adapter } from "@btst/db"` in getters/mutations/plugin files.
- **`"GET /path"` string keys** — routes use `createEndpoint()`, not string-keyed method/path objects.
- **`ctx.json()`** — does not exist; return data directly from route handlers.
- **`stack().api` bypasses auth hooks** — never use for authenticated data access; enforce auth at the call site.
- **Plugin init not called via `api`** — if `routes` factory runs a setup (e.g. `syncContentTypes`), also await it inside each `api` getter wrapper.
- **Business methods on `createBackendStack().raw`** — do not add them. Keep the composed lifecycle explicit through `forRequest(request).operations` or `trusted`, and reserve `raw` for narrow lower-level/SSG helpers.
- **Authorization in lifecycle hooks** — routine access control belongs in operation descriptors and the one shared rule. Hooks receive already-authorized context.
- **Write ops in `getters.ts`** — write functions belong in `mutations.ts`, not `getters.ts`.

## Full code patterns
Expand Down
Loading
Loading