@terreno/mcp 57.3.0 → 57.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/bootstrap.d.ts +0 -18
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +10 -2428
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/guidelines/admin-frontend/guidelines/core.md +5 -1
- package/dist/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +23 -10
- package/dist/docs/resources/syncdb.md +4 -1
- package/dist/docs/upgrades/57.4.0.md +140 -0
- package/dist/docs/versioned/next/README.md +3 -0
- package/dist/docs/versioned/next/explanation/README.md +4 -1
- package/dist/docs/versioned/next/explanation/admin-interface.md +20 -13
- package/dist/docs/versioned/next/explanation/charts.md +29 -0
- package/dist/docs/versioned/next/explanation/deployment-architecture-gcp.md +32 -0
- package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/dist/docs/versioned/next/explanation/example-coverage.md +43 -0
- package/dist/docs/versioned/next/explanation/local-first-data.md +5 -2
- package/dist/docs/versioned/next/explanation/loop-engineering.md +9 -6
- package/dist/docs/versioned/next/explanation/model-router-actions.md +19 -2
- package/dist/docs/versioned/next/explanation/organizations.md +88 -0
- package/dist/docs/versioned/next/explanation/planning-plugins.md +114 -0
- package/dist/docs/versioned/next/explanation/positioning.md +3 -3
- package/dist/docs/versioned/next/explanation/repository-settings.md +6 -5
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +44 -34
- package/dist/docs/versioned/next/explanation/source-rules.md +1 -1
- package/dist/docs/versioned/next/explanation/static-analysis.md +76 -16
- package/dist/docs/versioned/next/how-to/README.md +10 -0
- package/dist/docs/versioned/next/how-to/add-gpt-chat-mascot.md +92 -0
- package/dist/docs/versioned/next/how-to/add-organizations.md +204 -0
- package/dist/docs/versioned/next/how-to/admin-add-model.md +9 -1
- package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +7 -1
- package/dist/docs/versioned/next/how-to/audit-log.md +85 -0
- package/dist/docs/versioned/next/how-to/background-jobs.md +296 -0
- package/dist/docs/versioned/next/how-to/build-admin-screens.md +42 -13
- package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +37 -0
- package/dist/docs/versioned/next/how-to/circleci.md +136 -27
- package/dist/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +36 -0
- package/dist/docs/versioned/next/how-to/configure-better-auth.md +4 -0
- package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +154 -0
- package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +2 -0
- package/dist/docs/versioned/next/how-to/github-actions-ci.md +4 -3
- package/dist/docs/versioned/next/how-to/github-issue-lifecycle.md +12 -8
- package/dist/docs/versioned/next/how-to/in-app-notifications.md +147 -0
- package/dist/docs/versioned/next/how-to/install-agent-skills.md +24 -12
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +19 -3
- package/dist/docs/versioned/next/how-to/product-announcements.md +155 -0
- package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +127 -0
- package/dist/docs/versioned/next/how-to/run-tests-locally.md +102 -0
- package/dist/docs/versioned/next/how-to/seed-a-database.md +7 -2
- package/dist/docs/versioned/next/implementationPlans/README.md +8 -5
- package/dist/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +44 -0
- package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +386 -0
- package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +11 -8
- package/dist/docs/versioned/next/implementationPlans/announcements-surfaces.md +231 -0
- package/dist/docs/versioned/next/implementationPlans/announcements.md +289 -0
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -1
- package/dist/docs/versioned/next/implementationPlans/charts-and-dashboards.md +246 -0
- package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +4 -3
- package/dist/docs/versioned/next/implementationPlans/create-terreno-app.md +177 -0
- package/dist/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +219 -0
- package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +17 -17
- package/dist/docs/versioned/next/implementationPlans/framework-audit-log.md +247 -0
- package/dist/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +40 -0
- package/dist/docs/versioned/next/implementationPlans/job-queues.md +330 -0
- package/dist/docs/versioned/next/implementationPlans/knip-cleanup.md +136 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +210 -0
- package/dist/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +254 -0
- package/dist/docs/versioned/next/implementationPlans/notification-center.md +185 -0
- package/dist/docs/versioned/next/implementationPlans/org-management-ui.md +8 -3
- package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/dist/docs/versioned/next/reference/README.md +4 -0
- package/dist/docs/versioned/next/reference/admin-backend.md +83 -5
- package/dist/docs/versioned/next/reference/admin-config.md +7 -0
- package/dist/docs/versioned/next/reference/admin-frontend.md +178 -5
- package/dist/docs/versioned/next/reference/admin-spa.md +18 -1
- package/dist/docs/versioned/next/reference/ai.md +2 -0
- package/dist/docs/versioned/next/reference/announcements.md +179 -0
- package/dist/docs/versioned/next/reference/api.md +343 -4
- package/dist/docs/versioned/next/reference/comms.md +33 -0
- package/dist/docs/versioned/next/reference/create-terreno-app.md +44 -0
- package/dist/docs/versioned/next/reference/environment-variables.md +29 -0
- package/dist/docs/versioned/next/reference/jobs.md +319 -0
- package/dist/docs/versioned/next/reference/legacy/rtk.md +5 -1
- package/dist/docs/versioned/next/reference/lifecycle-plugin.md +29 -11
- package/dist/docs/versioned/next/reference/mcp-server.md +2 -43
- package/dist/docs/versioned/next/reference/scan-plugin.md +169 -0
- package/dist/docs/versioned/next/reference/syncdb.md +38 -12
- package/dist/docs/versioned/next/reference/test.md +57 -6
- package/dist/docs/versioned/next/reference/ui-performance.md +2 -1
- package/dist/docs/versioned/next/reference/ui.md +257 -3
- package/dist/docs/versioned/next/tasks/admin-scripts-durable-jobs.md +10 -0
- package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +114 -0
- package/dist/docs/versioned/next/tasks/announcements-surfaces.md +101 -0
- package/dist/docs/versioned/next/tasks/announcements.md +173 -0
- package/dist/docs/versioned/next/tasks/charts-and-dashboards.md +120 -0
- package/dist/docs/versioned/next/tasks/create-terreno-app.md +82 -0
- package/dist/docs/versioned/next/tasks/datatable-server-side-filtering.md +87 -0
- package/dist/docs/versioned/next/tasks/examples-demo-coverage.md +123 -17
- package/dist/docs/versioned/next/tasks/framework-audit-log.md +86 -0
- package/dist/docs/versioned/next/tasks/gpt-chat-mascot.md +13 -0
- package/dist/docs/versioned/next/tasks/job-queues.md +144 -0
- package/dist/docs/versioned/next/tasks/knip-cleanup.md +146 -0
- package/dist/docs/versioned/next/tasks/migrate-admin-to-syncdb.md +209 -0
- package/dist/docs/versioned/next/tasks/mongodb-migrations-tooling.md +107 -0
- package/dist/docs/versioned/next/tasks/notification-center.md +102 -0
- package/dist/docs/versioned/next/tasks/org-management-ui.md +14 -14
- package/dist/docs/versioned/next/tutorials/README.md +1 -1
- package/dist/docs/versioned/next/tutorials/getting-started.md +62 -22
- package/dist/help/excerpt.d.ts +2 -0
- package/dist/help/excerpt.d.ts.map +1 -0
- package/dist/help/excerpt.js +8 -0
- package/dist/help/excerpt.js.map +1 -0
- package/dist/help/updateNotes.d.ts +42 -0
- package/dist/help/updateNotes.d.ts.map +1 -0
- package/dist/help/updateNotes.js +235 -0
- package/dist/help/updateNotes.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/local/tools/databaseQuery.d.ts +1 -0
- package/dist/local/tools/databaseQuery.d.ts.map +1 -1
- package/dist/local/tools/databaseQuery.js +1 -0
- package/dist/local/tools/databaseQuery.js.map +1 -1
- package/dist/packageGuidelines.d.ts +2 -2
- package/dist/packageGuidelines.d.ts.map +1 -1
- package/dist/packageGuidelines.js +2 -8
- package/dist/packageGuidelines.js.map +1 -1
- package/dist/resources.d.ts +3 -0
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +3 -0
- package/dist/resources.js.map +1 -1
- package/dist/search/chunker.d.ts +1 -0
- package/dist/search/chunker.d.ts.map +1 -1
- package/dist/search/chunker.js +1 -0
- package/dist/search/chunker.js.map +1 -1
- package/dist/search/docIndex.d.ts +1 -5
- package/dist/search/docIndex.d.ts.map +1 -1
- package/dist/search/docIndex.js +1 -0
- package/dist/search/docIndex.js.map +1 -1
- package/dist/search/docVersion.d.ts +2 -1
- package/dist/search/docVersion.d.ts.map +1 -1
- package/dist/search/docVersion.js +3 -1
- package/dist/search/docVersion.js.map +1 -1
- package/dist/tools.d.ts +2 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +113 -2
- package/dist/tools.js.map +1 -1
- package/package.json +28 -27
- package/src/docs/guidelines/admin-frontend/guidelines/core.md +5 -1
- package/src/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +23 -10
- package/src/docs/resources/syncdb.md +4 -1
- package/src/docs/upgrades/57.4.0.md +140 -0
- package/src/docs/versioned/next/README.md +3 -0
- package/src/docs/versioned/next/explanation/README.md +4 -1
- package/src/docs/versioned/next/explanation/admin-interface.md +20 -13
- package/src/docs/versioned/next/explanation/charts.md +29 -0
- package/src/docs/versioned/next/explanation/deployment-architecture-gcp.md +32 -0
- package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/src/docs/versioned/next/explanation/example-coverage.md +43 -0
- package/src/docs/versioned/next/explanation/local-first-data.md +5 -2
- package/src/docs/versioned/next/explanation/loop-engineering.md +9 -6
- package/src/docs/versioned/next/explanation/model-router-actions.md +19 -2
- package/src/docs/versioned/next/explanation/organizations.md +88 -0
- package/src/docs/versioned/next/explanation/planning-plugins.md +114 -0
- package/src/docs/versioned/next/explanation/positioning.md +3 -3
- package/src/docs/versioned/next/explanation/repository-settings.md +6 -5
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +44 -34
- package/src/docs/versioned/next/explanation/source-rules.md +1 -1
- package/src/docs/versioned/next/explanation/static-analysis.md +76 -16
- package/src/docs/versioned/next/how-to/README.md +10 -0
- package/src/docs/versioned/next/how-to/add-gpt-chat-mascot.md +92 -0
- package/src/docs/versioned/next/how-to/add-organizations.md +204 -0
- package/src/docs/versioned/next/how-to/admin-add-model.md +9 -1
- package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +7 -1
- package/src/docs/versioned/next/how-to/audit-log.md +85 -0
- package/src/docs/versioned/next/how-to/background-jobs.md +296 -0
- package/src/docs/versioned/next/how-to/build-admin-screens.md +42 -13
- package/src/docs/versioned/next/how-to/charts-and-dashboards.md +37 -0
- package/src/docs/versioned/next/how-to/circleci.md +136 -27
- package/src/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +36 -0
- package/src/docs/versioned/next/how-to/configure-better-auth.md +4 -0
- package/src/docs/versioned/next/how-to/create-a-terreno-app.md +154 -0
- package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +2 -0
- package/src/docs/versioned/next/how-to/github-actions-ci.md +4 -3
- package/src/docs/versioned/next/how-to/github-issue-lifecycle.md +12 -8
- package/src/docs/versioned/next/how-to/in-app-notifications.md +147 -0
- package/src/docs/versioned/next/how-to/install-agent-skills.md +24 -12
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +19 -3
- package/src/docs/versioned/next/how-to/product-announcements.md +155 -0
- package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +127 -0
- package/src/docs/versioned/next/how-to/run-tests-locally.md +102 -0
- package/src/docs/versioned/next/how-to/seed-a-database.md +7 -2
- package/src/docs/versioned/next/implementationPlans/README.md +8 -5
- package/src/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +44 -0
- package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +386 -0
- package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +11 -8
- package/src/docs/versioned/next/implementationPlans/announcements-surfaces.md +231 -0
- package/src/docs/versioned/next/implementationPlans/announcements.md +289 -0
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -1
- package/src/docs/versioned/next/implementationPlans/charts-and-dashboards.md +246 -0
- package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +4 -3
- package/src/docs/versioned/next/implementationPlans/create-terreno-app.md +177 -0
- package/src/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +219 -0
- package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +17 -17
- package/src/docs/versioned/next/implementationPlans/framework-audit-log.md +247 -0
- package/src/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +40 -0
- package/src/docs/versioned/next/implementationPlans/job-queues.md +330 -0
- package/src/docs/versioned/next/implementationPlans/knip-cleanup.md +136 -0
- package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +210 -0
- package/src/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +254 -0
- package/src/docs/versioned/next/implementationPlans/notification-center.md +185 -0
- package/src/docs/versioned/next/implementationPlans/org-management-ui.md +8 -3
- package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/src/docs/versioned/next/reference/README.md +4 -0
- package/src/docs/versioned/next/reference/admin-backend.md +83 -5
- package/src/docs/versioned/next/reference/admin-config.md +7 -0
- package/src/docs/versioned/next/reference/admin-frontend.md +178 -5
- package/src/docs/versioned/next/reference/admin-spa.md +18 -1
- package/src/docs/versioned/next/reference/ai.md +2 -0
- package/src/docs/versioned/next/reference/announcements.md +179 -0
- package/src/docs/versioned/next/reference/api.md +343 -4
- package/src/docs/versioned/next/reference/comms.md +33 -0
- package/src/docs/versioned/next/reference/create-terreno-app.md +44 -0
- package/src/docs/versioned/next/reference/environment-variables.md +29 -0
- package/src/docs/versioned/next/reference/jobs.md +319 -0
- package/src/docs/versioned/next/reference/legacy/rtk.md +5 -1
- package/src/docs/versioned/next/reference/lifecycle-plugin.md +29 -11
- package/src/docs/versioned/next/reference/mcp-server.md +2 -43
- package/src/docs/versioned/next/reference/scan-plugin.md +169 -0
- package/src/docs/versioned/next/reference/syncdb.md +38 -12
- package/src/docs/versioned/next/reference/test.md +57 -6
- package/src/docs/versioned/next/reference/ui-performance.md +2 -1
- package/src/docs/versioned/next/reference/ui.md +257 -3
- package/src/docs/versioned/next/tasks/admin-scripts-durable-jobs.md +10 -0
- package/src/docs/versioned/next/tasks/agent-ui-blocks.md +114 -0
- package/src/docs/versioned/next/tasks/announcements-surfaces.md +101 -0
- package/src/docs/versioned/next/tasks/announcements.md +173 -0
- package/src/docs/versioned/next/tasks/charts-and-dashboards.md +120 -0
- package/src/docs/versioned/next/tasks/create-terreno-app.md +82 -0
- package/src/docs/versioned/next/tasks/datatable-server-side-filtering.md +87 -0
- package/src/docs/versioned/next/tasks/examples-demo-coverage.md +123 -17
- package/src/docs/versioned/next/tasks/framework-audit-log.md +86 -0
- package/src/docs/versioned/next/tasks/gpt-chat-mascot.md +13 -0
- package/src/docs/versioned/next/tasks/job-queues.md +144 -0
- package/src/docs/versioned/next/tasks/knip-cleanup.md +146 -0
- package/src/docs/versioned/next/tasks/migrate-admin-to-syncdb.md +209 -0
- package/src/docs/versioned/next/tasks/mongodb-migrations-tooling.md +107 -0
- package/src/docs/versioned/next/tasks/notification-center.md +102 -0
- package/src/docs/versioned/next/tasks/org-management-ui.md +14 -14
- package/src/docs/versioned/next/tutorials/README.md +1 -1
- package/src/docs/versioned/next/tutorials/getting-started.md +62 -22
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# @terreno/jobs
|
|
2
|
+
|
|
3
|
+
Durable background jobs plugin for `@terreno/api`. Persists work in MongoDB, runs handlers
|
|
4
|
+
with retries and schedules, and supports pluggable dispatch (`MongoJobRunner`, GCP Cloud
|
|
5
|
+
Tasks, Vercel Queues, custom).
|
|
6
|
+
|
|
7
|
+
Operator guide: [Durable background jobs](../how-to/background-jobs.md).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun add @terreno/jobs
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peers: `mongoose ^8.0.0 || ^9.0.0`; optional `@google-cloud/tasks`, `@vercel/queue`.
|
|
16
|
+
|
|
17
|
+
## Package exports
|
|
18
|
+
|
|
19
|
+
| Import | Contents |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `@terreno/jobs` | `JobsApp`, `getJobsService`, `tryGetJobsService`, `unregisterJobsService`, `Job`, `JobSchedule`, types, `MongoJobRunner` |
|
|
22
|
+
| `@terreno/jobs/runners/gcpCloudTasks` | `GcpCloudTasksRunner`, client seams |
|
|
23
|
+
| `@terreno/jobs/runners/vercelQueues` | `VercelQueuesRunner`, `createVercelQueuesConsumer`, limits/helpers |
|
|
24
|
+
|
|
25
|
+
## JobsApp
|
|
26
|
+
|
|
27
|
+
`JobsApp` implements `TerrenoPlugin` and `JobsRunnerHost`.
|
|
28
|
+
|
|
29
|
+
### Options (`JobsAppOptions`)
|
|
30
|
+
|
|
31
|
+
| Option | Default | Description |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `basePath` | `"/jobs"` | Admin routes and execute route prefix |
|
|
34
|
+
| `runner` | `MongoJobRunner` | Dispatch + optional poll loop |
|
|
35
|
+
| `timezone` | `"UTC"` | Default IANA zone for schedules without `schedule.timezone` |
|
|
36
|
+
| `lockTtlMs` | `900_000` (15 min) | Stale `lockedAt` reclaim threshold |
|
|
37
|
+
| `pollIntervalMs` | `1_000` | Mongo runner idle sleep between polls |
|
|
38
|
+
| `accessControl` | — | Terreno RBAC; jobs admin needs `admin:access` **and** `admin:jobs` |
|
|
39
|
+
| `redactPayload` | — | Admin payload projection hook (see below) |
|
|
40
|
+
| `mountExecuteRoute` | `false` | Force `POST {basePath}/execute` |
|
|
41
|
+
| `executeAuth` | — | Required when execute route is mounted |
|
|
42
|
+
|
|
43
|
+
Execute route mounts when `mountExecuteRoute: true` **or** `runner.requiresExecuteRoute === true`.
|
|
44
|
+
|
|
45
|
+
### Methods
|
|
46
|
+
|
|
47
|
+
| Method | Description |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `define(name, definition)` | Register handler, retry, and optional schedule |
|
|
50
|
+
| `register(app, openApi?)` | Mount admin + execute routes; register `JobsService` |
|
|
51
|
+
| `startWorker()` | Start runner `start()` (fire-and-forget); reconciles schedules first |
|
|
52
|
+
| `stopWorker()` | Abort signal + `runner.stop()` |
|
|
53
|
+
| `executeQueuedJob(jobId, signal?)` | Run by id (queue consumer / tests) |
|
|
54
|
+
| `adminContribution()` | Admin screen + home widget metadata |
|
|
55
|
+
|
|
56
|
+
## JobDefinition
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface JobDefinition {
|
|
60
|
+
handler: (payload: unknown, ctx: JobHandlerContext) => Promise<void>;
|
|
61
|
+
retry?: {
|
|
62
|
+
backoffMs?: number; // default 1_000
|
|
63
|
+
maxAttempts?: number; // default 5
|
|
64
|
+
maxBackoffMs?: number; // default 900_000
|
|
65
|
+
};
|
|
66
|
+
schedule?: {
|
|
67
|
+
cron: string;
|
|
68
|
+
timezone?: string; // IANA; falls back to JobsApp timezone
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`JobHandlerContext`: `{jobId, log: ScopedLogger, signal: AbortSignal}`.
|
|
74
|
+
|
|
75
|
+
## Enqueue (`EnqueueJobParams`)
|
|
76
|
+
|
|
77
|
+
| Field | Required | Description |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `name` | yes | Registered handler name |
|
|
80
|
+
| `payload` | yes | JSON-serializable mixed |
|
|
81
|
+
| `runAt` | no | Earliest claim time (default now, UTC) |
|
|
82
|
+
| `idempotencyKey` | no | Dedupes per `name` |
|
|
83
|
+
| `scheduleId` | no | Set by scheduler for cron children |
|
|
84
|
+
|
|
85
|
+
`getJobsService().enqueue(...)` after `JobsApp.register()`. `tryGetJobsService()` returns
|
|
86
|
+
`undefined` when `JobsApp` is not mounted. `unregisterJobsService()` clears the process
|
|
87
|
+
singleton (tests).
|
|
88
|
+
|
|
89
|
+
### Admin scripts (`admin/script`)
|
|
90
|
+
|
|
91
|
+
`@terreno/admin-backend` `defineAdminScriptJob` registers handler name `admin/script` with
|
|
92
|
+
`retry.maxAttempts: 1`. Payload: `{scriptName, taskId, wetRun, args, createdByName?}`.
|
|
93
|
+
The Scripts UI still polls `BackgroundTask` by `taskId`. Without `JobsApp`, script HTTP
|
|
94
|
+
runs stay in-process.
|
|
95
|
+
|
|
96
|
+
## JobRunner
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
interface JobRunner {
|
|
100
|
+
readonly id: string;
|
|
101
|
+
readonly requiresExecuteRoute?: boolean;
|
|
102
|
+
enqueue(job: JobDocument): Promise<void>;
|
|
103
|
+
start?(options: JobRunnerStartOptions): Promise<void>;
|
|
104
|
+
stop?(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Built-in ids: `mongo`, `gcp-cloud-tasks`, `vercel-queues`.
|
|
109
|
+
|
|
110
|
+
## Job model (`Job`)
|
|
111
|
+
|
|
112
|
+
| Field | Default | Description |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `name` | — | Handler name (indexed) |
|
|
115
|
+
| `status` | — | See statuses below |
|
|
116
|
+
| `payload` | — | Handler input |
|
|
117
|
+
| `payloadRedacted` | `false` | Admin omission flag |
|
|
118
|
+
| `runAt` | — | Earliest claim (indexed) |
|
|
119
|
+
| `attemptCount` | `0` | Executions so far |
|
|
120
|
+
| `attempts` | `[]` | `{at, error?, errorClass?}` |
|
|
121
|
+
| `maxAttempts` | `5` | From definition at enqueue |
|
|
122
|
+
| `backoffMs` | `1_000` | Retry base |
|
|
123
|
+
| `maxBackoffMs` | `900_000` | Retry cap |
|
|
124
|
+
| `lastError` | — | Latest failure message; `$unset` when the job completes |
|
|
125
|
+
| `lockedAt` / `lockedBy` | — | Claim lease |
|
|
126
|
+
| `idempotencyKey` | — | Partial unique with `name` |
|
|
127
|
+
| `scheduleId` | — | Parent schedule ref |
|
|
128
|
+
| `retriedFromId` / `retriedById` | — | Admin retry linkage |
|
|
129
|
+
| `created` / `updated` | plugins | Timestamps |
|
|
130
|
+
|
|
131
|
+
Indexes: `(name, idempotencyKey)` partial unique; `(status, runAt)`; `(scheduleId, status)`.
|
|
132
|
+
|
|
133
|
+
### Statuses
|
|
134
|
+
|
|
135
|
+
| Status | Meaning |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| `pending` | Waiting for `runAt`; claimable |
|
|
138
|
+
| `running` | Claimed; handler executing |
|
|
139
|
+
| `scheduled` | Reserved in schema / in-flight filter |
|
|
140
|
+
| `completed` | Success |
|
|
141
|
+
| `dead` | Exhausted `maxAttempts` |
|
|
142
|
+
| `failed` | No handler registered at run time |
|
|
143
|
+
| `cancelled` | Admin cancel |
|
|
144
|
+
|
|
145
|
+
### Retry backoff
|
|
146
|
+
|
|
147
|
+
`computeRetryDelayMs`: `backoffMs * 2^(attemptCount-1)`, capped at `maxBackoffMs`, × equal jitter in `[0.5, 1.0)`.
|
|
148
|
+
|
|
149
|
+
### Claim / lock
|
|
150
|
+
|
|
151
|
+
`createClaimLock` sets `lockedAt`, `lockedBy` (worker id). Claims match `status ∈ {pending,running,scheduled}`, `runAt <= now`, and expired or missing lock. `executeJobById` returns `noop` for terminal rows, `409` conflict for live lock or not-due.
|
|
152
|
+
|
|
153
|
+
## JobSchedule model
|
|
154
|
+
|
|
155
|
+
| Field | Default | Description |
|
|
156
|
+
| --- | --- | --- |
|
|
157
|
+
| `name` | — | Unique; matches `define` name |
|
|
158
|
+
| `handlerName` | — | Same as `name` |
|
|
159
|
+
| `cron` | — | Standard cron expression |
|
|
160
|
+
| `timezone` | `UTC` | IANA zone |
|
|
161
|
+
| `enabled` | `true` | Pause via admin API |
|
|
162
|
+
| `nextRunAt` | — | Next tick (UTC instant) |
|
|
163
|
+
|
|
164
|
+
## HTTP routes
|
|
165
|
+
|
|
166
|
+
Base: `{basePath}` (default `/jobs`). All admin routes require auth + jobs admin permission.
|
|
167
|
+
|
|
168
|
+
| Method | Path | Description |
|
|
169
|
+
| --- | --- | --- |
|
|
170
|
+
| GET | `/jobs` | Paginated list (`page`, `limit`, `name`, `status`, `scheduleId`, `start`, `end`, `q`) |
|
|
171
|
+
| GET | `/jobs/stats` | Counts by status |
|
|
172
|
+
| GET | `/jobs/:id` | Detail |
|
|
173
|
+
| POST | `/jobs/:id/retry` | New linked row from `dead` / `failed` |
|
|
174
|
+
| POST | `/jobs/:id/requeue` | Reset same row from `dead` / `failed` / `cancelled` |
|
|
175
|
+
| POST | `/jobs/:id/cancel` | Cancel `pending` / `running` / `scheduled` |
|
|
176
|
+
| GET | `/jobs/schedules` | All schedules |
|
|
177
|
+
| POST | `/jobs/schedules/:name/pause` | `enabled: false` |
|
|
178
|
+
| POST | `/jobs/schedules/:name/resume` | `enabled: true` |
|
|
179
|
+
| POST | `/jobs/execute` | Internal dispatch (`{jobId}`); `executeAuth` required |
|
|
180
|
+
|
|
181
|
+
List defaults: `limit` 50, max 200. Execute uses 30-minute handler timeout.
|
|
182
|
+
|
|
183
|
+
### RBAC
|
|
184
|
+
|
|
185
|
+
When `accessControl` is configured, every jobs admin route checks **both**
|
|
186
|
+
`admin:access` and `admin:jobs` (`accessControl.can({permissions: {admin: ["access", "jobs"]}})`).
|
|
187
|
+
Default `terrenoStatements.admin` includes both actions. When `accessControl` is omitted,
|
|
188
|
+
routes fall back to legacy `user.admin === true`.
|
|
189
|
+
|
|
190
|
+
### Admin payload redaction
|
|
191
|
+
|
|
192
|
+
`JobsAdminRedactPayload`:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
type JobsAdminRedactPayload = (input: {
|
|
196
|
+
name: string;
|
|
197
|
+
payload: unknown;
|
|
198
|
+
payloadRedacted: boolean;
|
|
199
|
+
}) => unknown | undefined;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Default (omit hook):** `payload` is not included in admin list/detail JSON.
|
|
203
|
+
|
|
204
|
+
**Opt-in projection** — return only fields operators need; raw identity passthrough is
|
|
205
|
+
discouraged:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
new JobsApp({
|
|
209
|
+
redactPayload: ({name, payload}) => {
|
|
210
|
+
if (name === "billing/capture") {
|
|
211
|
+
return {invoiceId: (payload as {invoiceId?: string}).invoiceId};
|
|
212
|
+
}
|
|
213
|
+
return undefined; // hide unknown job types
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Return `undefined` to hide payload for a row. `payloadRedacted: true` on the document
|
|
219
|
+
always hides payload regardless of the hook.
|
|
220
|
+
|
|
221
|
+
## Admin frontend widgets
|
|
222
|
+
|
|
223
|
+
`@terreno/admin-frontend` public exports for jobs:
|
|
224
|
+
|
|
225
|
+
| Export | Role |
|
|
226
|
+
| --- | --- |
|
|
227
|
+
| `JOBS_ADMIN_WIDGETS`, `JobsDashboardScreenWidget` | Screen widget map (`jobs` → dashboard) |
|
|
228
|
+
| `JOBS_HOME_WIDGETS`, `JobsHomeWidget` | Home widget (`dead` / `running` counts) |
|
|
229
|
+
| `JobsDashboardScreen`, `JobsJobDetail` | Full screens (Expo admin routes) |
|
|
230
|
+
| `JobsStatusBadge` | Status chip |
|
|
231
|
+
| `canCancelJob`, `canRequeueJob`, `canRetryJob`, `formatJobTimestamp`, `jobRowId`, `unwrapJobRow` | Row helpers |
|
|
232
|
+
| `JobRow`, `JobAttemptRow`, `JobScheduleRow`, `JobsStats` | Row/types for tables |
|
|
233
|
+
| `JobsDashboardFilters`, `parseJobsDashboardSearchParams`, `serializeJobsDashboardSearchParams` | URL filter helpers |
|
|
234
|
+
|
|
235
|
+
Widgets are registered in the built-in registry — enable via `JobsApp.adminContribution()` only.
|
|
236
|
+
The admin shell lists Jobs under **Platform** (not Screens). Hosts still contribute
|
|
237
|
+
`name: "jobs"`; do not add a second sidebar link.
|
|
238
|
+
Custom screens should pass the app's generated RTK `api` into `AdminProvider`; jobs screens wire
|
|
239
|
+
admin HTTP internally (not via a public `useJobsDashboardApi` export).
|
|
240
|
+
|
|
241
|
+
## GCP Cloud Tasks (`@terreno/jobs/runners/gcpCloudTasks`)
|
|
242
|
+
|
|
243
|
+
### `GcpCloudTasksRunnerConfig`
|
|
244
|
+
|
|
245
|
+
| Field | Required | Description |
|
|
246
|
+
| --- | --- | --- |
|
|
247
|
+
| `project` | yes | GCP project id |
|
|
248
|
+
| `location` | yes | Queue region |
|
|
249
|
+
| `queue` | yes | Queue name |
|
|
250
|
+
| `publicUrl` | yes | Absolute `http(s)` API origin (no trailing slash) |
|
|
251
|
+
| `serviceAccountEmail` | yes | OIDC invoker SA |
|
|
252
|
+
| `oidcAudience` | no | Defaults to execute URL |
|
|
253
|
+
| `basePath` | no | Default `/jobs` |
|
|
254
|
+
| `dispatchDeadlineSeconds` | no | Cloud Tasks HTTP wait before retry. Range 15–1800. Default **1800**. The runner sends protobuf `{seconds}` (what `@google-cloud/tasks` expects); REST encodes `"1800s"` so a 30-minute Cloud Run handler is not retried at the 10-minute HTTP default |
|
|
255
|
+
| `client` | no | Injected Cloud Tasks client. Required in `bun build --compile` images (REST + `google-auth-library` in the example app). Optional peer `createRequire` fallback for Node. |
|
|
256
|
+
|
|
257
|
+
Enqueue: `createTask` POST to `{publicUrl}{basePath}/execute`, body `{jobId}` base64,
|
|
258
|
+
`dispatchDeadline` `{seconds: 1800}` by default (REST JSON `"1800s"`), `scheduleTime` when
|
|
259
|
+
`runAt` is future.
|
|
260
|
+
`requiresExecuteRoute: true`.
|
|
261
|
+
`start()` ticks due Mongo schedules into `enqueue`; it does not claim job rows.
|
|
262
|
+
|
|
263
|
+
**Operator responsibility:** implement `executeAuth` to verify the OIDC token (issuer,
|
|
264
|
+
audience, SA email). `@terreno/jobs` does not ship a GCP verifier.
|
|
265
|
+
|
|
266
|
+
## Vercel Queues (`@terreno/jobs/runners/vercelQueues`)
|
|
267
|
+
|
|
268
|
+
### `VercelQueuesRunnerConfig`
|
|
269
|
+
|
|
270
|
+
| Field | Default | Description |
|
|
271
|
+
| --- | --- | --- |
|
|
272
|
+
| `topic` | required | Queue topic name |
|
|
273
|
+
| `region` | — | Optional send region |
|
|
274
|
+
| `retentionSeconds` | `86400` | Min 60, max 604800 |
|
|
275
|
+
| `send` | — | Test seam |
|
|
276
|
+
|
|
277
|
+
Constants: `VERCEL_QUEUE_MAX_DELAY_SECONDS` = 604800 (7d); visibility retry clamp 30–3600s.
|
|
278
|
+
|
|
279
|
+
Enqueue: `send(topic, {jobId}, {delaySeconds?, retentionSeconds?, idempotencyKey})`.
|
|
280
|
+
`runAt` beyond 7 days throws `VercelQueueRunAtBeyondLimitError`.
|
|
281
|
+
|
|
282
|
+
`createVercelQueuesConsumer({host, handleCallback?, signal?, visibilityTimeoutSeconds?})` wraps
|
|
283
|
+
`@vercel/queue` `handleCallback` → `executeJobById`. `requiresExecuteRoute: false`.
|
|
284
|
+
|
|
285
|
+
`vercel.json`: `"experimentalTriggers": [{"type": "queue/v2beta", "topic": "<topic>"}]`.
|
|
286
|
+
|
|
287
|
+
## Environment variables
|
|
288
|
+
|
|
289
|
+
`@terreno/jobs` uses **explicit constructor options**, not env readers.
|
|
290
|
+
|
|
291
|
+
| Variable | Read by | Notes |
|
|
292
|
+
| --- | --- | --- |
|
|
293
|
+
| `JOBS_START_WORKER` | example-backend only | `true`/`false`; unset → `true`. Disable in API when running `jobs:worker`. |
|
|
294
|
+
| `JOB_TRACE_LOGS` | app convention | Gates verbose worker logs via `createFeatureFlaggedLogger` (not read by jobs package) |
|
|
295
|
+
|
|
296
|
+
Legacy `GCP_TASKS_NOTIFICATIONS_QUEUE` / `GCP_TASK_PROCESSOR_QUEUE` appear in example-backend **test** fixtures only — not used by `@terreno/jobs`. Configure Cloud Tasks via `GcpCloudTasksRunner` constructor args.
|
|
297
|
+
|
|
298
|
+
## Process-local alternatives
|
|
299
|
+
|
|
300
|
+
| API | Package | Durable | Use |
|
|
301
|
+
| --- | --- | --- | --- |
|
|
302
|
+
| `cronjob()` | `@terreno/api` | No | In-process recurring timer |
|
|
303
|
+
| `wrapScript()` | `@terreno/api` | No | One-shot CLI / script wrapper |
|
|
304
|
+
| `JobsApp` | `@terreno/jobs` | Yes | Retries, DLQ, schedules, admin, cloud dispatch |
|
|
305
|
+
|
|
306
|
+
## Commands (monorepo)
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
bun run jobs:compile
|
|
310
|
+
bun run jobs:lint
|
|
311
|
+
bun run jobs:test
|
|
312
|
+
bun run jobs:worker # example-backend standalone worker
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Related
|
|
316
|
+
|
|
317
|
+
- [Durable background jobs how-to](../how-to/background-jobs.md)
|
|
318
|
+
- [API reference — wrapScript / cronjob](api.md#wrapscript)
|
|
319
|
+
- [Environment variables](environment-variables.md#background-jobs)
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
> **Deprecated for data synchronization.** `@terreno/rtk` remains published through the **56.x** line (and the stable **0.x** line) with a deprecation notice. It will **not** be published in the next major Terreno release. For collection reads/writes, offline sync, and realtime convergence, use [`@terreno/syncdb`](../syncdb.md) and the [migration guide](../../how-to/migrate-rtk-to-syncdb.md).
|
|
4
4
|
>
|
|
5
|
-
> RTK Query
|
|
5
|
+
> RTK Query remains the correct tool for **non-synced** generated OpenAPI routes
|
|
6
|
+
> such as `/auth/me` and AI APIs, Better Auth session Redux wiring, feature
|
|
7
|
+
> flags, and sockets. Built-in admin RPC uses host-bound fetch; ObjectId/API-only
|
|
8
|
+
> admin CRUD keeps RTK compatibility in Terreno 57, then `useAdminApi` and the
|
|
9
|
+
> required admin `api` prop are removed in Terreno 58.
|
|
6
10
|
|
|
7
11
|
Redux Toolkit Query utilities for frontends using @terreno/api backends. JWT auth, token storage, SDK code generation from OpenAPI, and real-time WebSocket connections.
|
|
8
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Lifecycle plugin reference
|
|
2
2
|
|
|
3
|
-
Plugin: `terreno-planning` (`2.
|
|
3
|
+
Plugin: `terreno-planning` (`2.12.0`)
|
|
4
4
|
|
|
5
5
|
Planning skills are model-invocable: agents may select them from descriptions, not only
|
|
6
6
|
from slash commands. Grow, Brew, and Taste each implement one bounded transition. Pick continues an inner loop until the
|
|
@@ -10,11 +10,11 @@ loops that invoke those stages; they are not stages and must not appear as `stag
|
|
|
10
10
|
|
|
11
11
|
| Skill | Preconditions | Primary output | PASS next |
|
|
12
12
|
| --- | --- | --- | --- |
|
|
13
|
-
| `terreno-1-grow` | request/spec + repository | approved IP/tasks + criterion/verification map | Pick (enters inner loop) |
|
|
13
|
+
| `terreno-1-grow` | request/spec + repository | approved IP/tasks + criterion/verification map + standalone approval brief | Pick (enters inner loop) |
|
|
14
14
|
| `terreno-2-pick` | approved task + branch/state | one implemented slice, then Roast, then the next task | Roast, or Brew when the list is done |
|
|
15
15
|
| `terreno-3-roast` | Pick result + current diff | independent requirement/evidence verdict for the current task | emit Pick if tasks remain, else Brew; never invoke Pick; pass a task-scoped briefing; do not spawn two unconstrained reviewers |
|
|
16
16
|
| `terreno-4-brew` | Roast PASS for every in-scope task + branch/evidence | pushed head + PR + product-CI trigger check + review-bot wait + attached evidence | Taste |
|
|
17
|
-
| `terreno-5-taste` | PR + current state | one current-head reaction; before push: pull latest `master`,
|
|
17
|
+
| `terreno-5-taste` | PR + current state | one current-head reaction; before push: pull latest `master`, re-verify last-run failed tests locally, run root `prepush` when present (otherwise affected-package checks) in a no-context subagent, then watch CI | null or fresh Taste |
|
|
18
18
|
|
|
19
19
|
Outer loops (not stages):
|
|
20
20
|
|
|
@@ -39,6 +39,8 @@ Results use `PASS`, `FAIL`, `BLOCKED`, or `PENDING` and the compact `v: 2` schem
|
|
|
39
39
|
(`v`, `stage`, `status`, `next`, `action`; omit empty keys). Loop state follows
|
|
40
40
|
[`execution-state.schema.json`](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-planning/references/execution-state.schema.json).
|
|
41
41
|
Chat and PRs show `status` / `next` / `action`; the YAML lives in a Details toggle.
|
|
42
|
+
When the current PR has GitHub Deployments, those demo URLs are the last visible
|
|
43
|
+
section of every wait-for-human or done chat.
|
|
42
44
|
|
|
43
45
|
The outer loop owns state persistence, Taste `PENDING` reinvocation, Grow/Brew/Taste
|
|
44
46
|
invocation, retries, and escalation. Pick owns the
|
|
@@ -49,9 +51,12 @@ until async review bots (Bugbot, CodeQL, and similar) on the current head have r
|
|
|
49
51
|
preferring provider CLI watch hooks or harness event subscriptions over sleep polling.
|
|
50
52
|
Taste then waits in a loop for product CI using GitHub CLI (`gh pr checks --watch`,
|
|
51
53
|
`gh run watch`) or CircleCI CLI (`circleci run watch`) until jobs are terminal or the
|
|
52
|
-
wait times out. Before any push, Taste always pulls latest `master`,
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
wait times out. Before any push, Taste always pulls latest `master`, records last-run
|
|
55
|
+
failed tests and re-verifies them locally, then spawns a
|
|
56
|
+
fresh subagent with no parent conversation. If the repository root defines a `prepush`
|
|
57
|
+
package script, Taste runs it as the authoritative local gate; otherwise it falls back
|
|
58
|
+
to lint, typecheck, and locally affected tests in each affected package. Taste then
|
|
59
|
+
pushes and watches product CI.
|
|
55
60
|
Taste observes product CI on every discovered host (GitHub Actions, CircleCI,
|
|
56
61
|
Buildkite, and similar), not only GitHub checks. A documented not-applicable host
|
|
57
62
|
counts as skipped; an unexplained untriggered host prevents Brew `PASS`. Brew still
|
|
@@ -66,11 +71,24 @@ docs, and risks, then presents that report once. It asks the human only
|
|
|
66
71
|
for an actual product/architecture/security/data/destructive/policy decision or
|
|
67
72
|
unreplaceable credential. Before asking, it gives the overall goal/state, completed
|
|
68
73
|
work, decisive evidence, two to four options with impact, and a recommendation; the
|
|
69
|
-
message ends with one exact question.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
message ends with one exact question, then PR deployment URLs when a PR has them.
|
|
75
|
+
|
|
76
|
+
Grow's human-facing output is a standalone approval brief, not an index. It opens with a
|
|
77
|
+
paragraph on where the repository is and where the change takes it, adds background on
|
|
78
|
+
current state when a reviewer needs it, then the idea, then the plan (tasks, tracer,
|
|
79
|
+
verification, out of scope, risks). A Decisions table follows the plan with no row cap,
|
|
80
|
+
pairing every settled human decision with the question that prompted it; it is omitted
|
|
81
|
+
when grilling settled none. Paths to the IP and task files come last.
|
|
82
|
+
|
|
83
|
+
GitHub communication follows a fixed attention budget. The PR title is
|
|
84
|
+
`[ticket] Short feature title` (Linear `TEAM-n` or GitHub `#n`; omit brackets when
|
|
85
|
+
no ticket is attached) and names the feature only — never `feat:`, `IP Approved`,
|
|
86
|
+
or `Task list`. `Why` preserves the IP's initial justification, `What changed`
|
|
87
|
+
briefly describes the approved IP and intended outcomes, and `Verification` always
|
|
88
|
+
contains reproducible testing instructions. Brew updates verification as testing
|
|
89
|
+
changes but otherwise keeps the body stable instead of summarizing the latest turn.
|
|
90
|
+
These are the only visible PR sections; optional detail is expandable; comments are
|
|
91
|
+
reserved for blocked decisions or non-obvious review resolutions.
|
|
74
92
|
|
|
75
93
|
Every stage follows the
|
|
76
94
|
[documentation contract](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-planning/references/documentation-contract.md):
|
|
@@ -107,7 +107,7 @@ Set `TERRENO_MCP_DOCS_DIR` environment variable to override default path.
|
|
|
107
107
|
|
|
108
108
|
## Tools
|
|
109
109
|
|
|
110
|
-
Code generation tools that return TypeScript/JavaScript code as text.
|
|
110
|
+
Code generation tools that return TypeScript/JavaScript code as text. They do not write files — the AI assistant receives the code and writes it to appropriate locations. Scaffold a new app with [`create-terreno-app`](create-terreno-app.md) (`bunx create-terreno-app`), not an MCP tool.
|
|
111
111
|
|
|
112
112
|
### terreno_search_docs
|
|
113
113
|
|
|
@@ -341,47 +341,6 @@ Validate a Mongoose schema against Terreno conventions.
|
|
|
341
341
|
- Recommendations for fixes
|
|
342
342
|
- Severity levels (error, warning, info)
|
|
343
343
|
|
|
344
|
-
### terreno_bootstrap_app
|
|
345
|
-
|
|
346
|
-
Scaffold a new full-stack Terreno application (Expo frontend, Express/Mongoose backend, Cursor rules, MCP settings).
|
|
347
|
-
|
|
348
|
-
**Parameters:**
|
|
349
|
-
|
|
350
|
-
``````typescript
|
|
351
|
-
{
|
|
352
|
-
appName: string; // kebab-case (e.g., "my-app")
|
|
353
|
-
appDisplayName: string; // Human-readable name
|
|
354
|
-
description?: string;
|
|
355
|
-
mcpServerUrl?: string; // Default: https://mcp.terreno.app
|
|
356
|
-
}
|
|
357
|
-
``````
|
|
358
|
-
|
|
359
|
-
**Returns:** File list, setup instructions, and full file contents for backend, frontend, CI workflows, and MCP configuration.
|
|
360
|
-
|
|
361
|
-
The generated Profile tab (`frontend/app/(tabs)/profile.tsx`) uses `@terreno/ui` `TapToEdit` for name, email, and password. Each field saves independently with `PATCH /auth/me` (`usePatchMeMutation`). Name and email each have their own `useEffect`, so saving one field does not wipe an in-progress edit on the other.
|
|
362
|
-
|
|
363
|
-
The generated app is expected to install and boot with no manual follow-up, so the
|
|
364
|
-
scaffold deliberately ships no binary assets and no references to files it does not
|
|
365
|
-
create:
|
|
366
|
-
|
|
367
|
-
- **Fonts** come from `@terreno/ui`. `TerrenoProvider` wraps children in
|
|
368
|
-
`TerrenoFontProvider`, which loads Nunito and Titillium Web from
|
|
369
|
-
`@expo-google-fonts/*`. The generated `app/_layout.tsx` calls no `useFonts` of its own.
|
|
370
|
-
- **Icon, splash, and favicon** are left unset in `app.json` so Expo uses its built-in
|
|
371
|
-
defaults. Point them at real files once the app has branding.
|
|
372
|
-
- **`metro.config.js`** pins every `jspdf` request to `jspdf/dist/jspdf.es.min.js` on web
|
|
373
|
-
and drops it on native. `@terreno/admin-frontend` pulls jspdf in for consent-PDF export,
|
|
374
|
-
and jspdf's CommonJS and Node builds contain an AMD-style `require(["html2canvas"], cb)`
|
|
375
|
-
call that Metro's static transform cannot parse — without the override it fails the whole
|
|
376
|
-
bundle, including Expo Router's static web render.
|
|
377
|
-
- **Auth-gated routes** use `<Stack.Protected guard={...}>`. Wrapping `Stack.Screen`
|
|
378
|
-
children in a conditional or fragment instead crashes the navigator.
|
|
379
|
-
- **`tsconfig.json`** sets no `baseUrl`; it is deprecated in TypeScript 6 and makes `tsc`
|
|
380
|
-
abort with TS5101 before checking a single file. `paths` resolve relative to the tsconfig.
|
|
381
|
-
- **Declared dependencies** include every package generated frontend source imports
|
|
382
|
-
(`react-native-reanimated`, `redux-persist`, `@reduxjs/toolkit`, `@expo/vector-icons`,
|
|
383
|
-
`lodash`, `luxon`, and the rest). Do not rely on transitive installs for those.
|
|
384
|
-
|
|
385
344
|
### terreno_bootstrap_ai_rules
|
|
386
345
|
|
|
387
346
|
Scaffold AI coding assistant rules (AGENTS.md, Cursor/Windsurf rules, Copilot instructions, rulesync config).
|
|
@@ -416,7 +375,7 @@ Multi-step workflow prompts that guide AI assistants through complex tasks.
|
|
|
416
375
|
|
|
417
376
|
### terreno_bootstrap
|
|
418
377
|
|
|
419
|
-
Workflow prompt for scaffolding a new Terreno app.
|
|
378
|
+
Workflow prompt for scaffolding a new Terreno app. Instructs the assistant to run `bunx create-terreno-app`, then `terreno_bootstrap_ai_rules`.
|
|
420
379
|
|
|
421
380
|
**Arguments:**
|
|
422
381
|
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Scan plugin reference
|
|
2
|
+
|
|
3
|
+
Plugin: `terreno-scan` (`2.11.0`). Requires the lifecycle plugin
|
|
4
|
+
([lifecycle plugin reference](lifecycle-plugin.md)).
|
|
5
|
+
|
|
6
|
+
A scan turns a long-term outcome — "cold builds under 30s", "no unbounded reads", "WCAG
|
|
7
|
+
AA on every screen" — into a measured campaign: charter the goal, map-reduce the
|
|
8
|
+
repository into verified findings, slice those findings into IPs and tasks, run each
|
|
9
|
+
slice through Grow/Pick/Roast/Brew/Taste, then re-measure and repeat.
|
|
10
|
+
|
|
11
|
+
Scan stages never edit product code. The lifecycle does.
|
|
12
|
+
|
|
13
|
+
| Skill | Preconditions | Primary output | PASS next |
|
|
14
|
+
| --- | --- | --- | --- |
|
|
15
|
+
| `terreno-scan-1-aim` | goal/outcome + repository | approved charter: metric, measured baseline, dry-run rules, exclusions, validity rule, severity rubric, slice policy, budget | Sweep |
|
|
16
|
+
| `terreno-scan-2-sweep` | approved charter | per-rule hits, deterministic shard manifest, schema-valid candidate findings from parallel workers | Sift |
|
|
17
|
+
| `terreno-scan-3-sift` | candidates for the round | deduped, adversarially verified, ranked findings report plus dropped-candidate summary | Plot, or Track when dry |
|
|
18
|
+
| `terreno-scan-4-plot` | findings report + slice policy | ordered PR-sized slices, one standalone Grow brief each, findings marked `planned` | Track, with `handoff: grow` |
|
|
19
|
+
| `terreno-scan-5-track` | landed slice or dry round | re-measured metric with command output, findings closed/reopened with evidence, round record, next move | Plot, Sweep, or `null` |
|
|
20
|
+
|
|
21
|
+
Outer loops (not stages):
|
|
22
|
+
|
|
23
|
+
| Skill | What it walks | Default |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `terreno-scan-campaign` | One goal, round after round | Aim once, then Sweep → Sift → Plot → lifecycle → Track until the metric hits target, the budget is spent, or a genuine human gate is reached. Stops at each gate. |
|
|
26
|
+
| `terreno-scan-loop` | The same, but resident | Keeps the campaign moving **and** heartbeats over every open campaign PR — routing it to a reviewer, answering review comments through Taste, fixing red CI, tracking merges — refilling work as PRs land. Runs until the goal is met, a stop argument fires, or a human decision is required. |
|
|
27
|
+
|
|
28
|
+
## Resident loop
|
|
29
|
+
|
|
30
|
+
`terreno-scan-loop` is the "leave it running" entry point. One session holds the goal,
|
|
31
|
+
the state, and the schedule; every unit of work happens in a fresh subagent.
|
|
32
|
+
|
|
33
|
+
Arguments: `goal=<slug>`, `reviewer=<handle[,handle]>`, `assignee=<handle>`, `wip=<n>`,
|
|
34
|
+
`rounds=<n>`, `until=<ISO timestamp>`, `tick=<seconds>`. Overrides apply to the run only
|
|
35
|
+
and are recorded in state as overrides — they never rewrite the charter.
|
|
36
|
+
|
|
37
|
+
Each tick snapshots the campaign's PRs and sorts each into one bucket, then acts in
|
|
38
|
+
priority order:
|
|
39
|
+
|
|
40
|
+
| Bucket | Action |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `unrouted` | Apply the charter's routing, then count the slice in flight |
|
|
43
|
+
| `answered` | A human commented or requested changes → one `terreno-5-taste` in a fresh subagent |
|
|
44
|
+
| `broken` | CI failed or the PR conflicts → one `terreno-5-taste` |
|
|
45
|
+
| `merged` | → `terreno-scan-5-track`: re-measure, close findings with evidence |
|
|
46
|
+
| `closed` | Mark the slice abandoned, return its findings to `open`, free its slot |
|
|
47
|
+
| `bot-pending`, `waiting` | Nothing — this is why the next tick exists |
|
|
48
|
+
|
|
49
|
+
Then it refills capacity up to `wipLimit` and schedules the next tick: the provider's
|
|
50
|
+
watch hook while CI runs, 2–5 minutes while review bots report, 15–30 minutes while a
|
|
51
|
+
human holds the PR, and no wait at all while backlog work remains. Ticks that find
|
|
52
|
+
nothing print nothing, and every comment is answered exactly once — answered ids live in
|
|
53
|
+
state. See
|
|
54
|
+
[`heartbeat.md`](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-scan/references/heartbeat.md).
|
|
55
|
+
|
|
56
|
+
## PR routing
|
|
57
|
+
|
|
58
|
+
Aim settles who reviews the campaign's PRs before any PR exists, and records a `review`
|
|
59
|
+
block: `mode` (`fixed`, `codeowners`, `blame`, `round-robin`, `none`), reviewer and
|
|
60
|
+
assignee handles, teams, draft policy, labels, `wipLimit`, `mergePolicy`, and how to
|
|
61
|
+
notify. Handles are verified with `gh api repos/{owner}/{repo}/collaborators/<handle>`
|
|
62
|
+
before use; an unverifiable handle is a human gate, never a silently unassigned PR.
|
|
63
|
+
|
|
64
|
+
Routing is applied right after Brew reports the PR (`gh pr edit --add-reviewer
|
|
65
|
+
--add-assignee --add-label`) and re-requested once when requested changes are addressed.
|
|
66
|
+
The campaign never approves its own PRs, never dismisses a review, and never merges when
|
|
67
|
+
`mergePolicy` is `human` — the default. See
|
|
68
|
+
[`pr-routing.md`](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-scan/references/pr-routing.md).
|
|
69
|
+
|
|
70
|
+
## Stage anatomy
|
|
71
|
+
|
|
72
|
+
Every stage includes Preconditions, Inputs, Procedure, Supporting skills, Evidence
|
|
73
|
+
produced, Success/Failure/Blocked conditions, and a recommended next stage — the same
|
|
74
|
+
shape as the lifecycle stages.
|
|
75
|
+
|
|
76
|
+
Results use `PASS`, `FAIL`, `BLOCKED`, or `PENDING` and the compact `v: 2`
|
|
77
|
+
[`scan-result.schema.json`](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-scan/references/scan-result.schema.json)
|
|
78
|
+
(`v`, `stage`, `status`, `next`, `action`; omit empty keys). Campaign state follows
|
|
79
|
+
[`scan-state.schema.json`](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-scan/references/scan-state.schema.json)
|
|
80
|
+
and findings follow
|
|
81
|
+
[`finding.schema.json`](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-scan/references/finding.schema.json).
|
|
82
|
+
Chat shows `status` / `next` / `action` plus the current metric; the YAML lives in a
|
|
83
|
+
Details toggle.
|
|
84
|
+
|
|
85
|
+
## Map-reduce
|
|
86
|
+
|
|
87
|
+
Sweep shards rule hits into batches (default: at most 25 files or 80 hits per shard,
|
|
88
|
+
never splitting a file) and maps one fresh worker per shard. Workers get a shard
|
|
89
|
+
briefing — goal, rules, validity rule, severity rubric, file list, hit lines, allowed
|
|
90
|
+
commands, and the return schema — and nothing else. They never search the repository at
|
|
91
|
+
large, never spawn nested agents, and never edit files. Sift then dedupes by
|
|
92
|
+
`rule + path + symbol`, re-runs evidence, refutes every high-severity candidate before
|
|
93
|
+
keeping it, drops what does not reproduce, and ranks by impact × confidence ÷ effort.
|
|
94
|
+
Dropped shards and capped coverage are always reported; silent truncation is a defect.
|
|
95
|
+
See
|
|
96
|
+
[`mapreduce.md`](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-scan/references/mapreduce.md).
|
|
97
|
+
|
|
98
|
+
## Goal and metric
|
|
99
|
+
|
|
100
|
+
Aim is not finished until the metric has a command that prints a number and the baseline
|
|
101
|
+
came from running it. Track re-runs that command with the charter's protocol every round
|
|
102
|
+
and records the value with its output. A finding that cannot be tied to the metric is
|
|
103
|
+
dismissed in Sift, not deferred. Re-baselining without recording both values, the reason,
|
|
104
|
+
and the round is untrustworthy and Track says so. See
|
|
105
|
+
[`goal-tracking.md`](https://github.com/FlourishHealth/terreno/blob/master/plugins/terreno-scan/references/goal-tracking.md).
|
|
106
|
+
|
|
107
|
+
## Artifacts
|
|
108
|
+
|
|
109
|
+
| Artifact | Default path | Committed |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| Charter | `docs/scans/<goal-slug>/charter.md` | yes |
|
|
112
|
+
| Findings report | `docs/scans/<goal-slug>/round-<n>-findings.md` | yes |
|
|
113
|
+
| Shard manifest, candidates, slice briefs | round artifact directory | per repository policy |
|
|
114
|
+
| Campaign state | `.terreno/scan/<goal-slug>.json` | no (git-ignored) |
|
|
115
|
+
| Slice IP and tasks | the repository's existing IP/task convention, written by Grow | yes |
|
|
116
|
+
|
|
117
|
+
An existing repository convention always wins over these defaults.
|
|
118
|
+
|
|
119
|
+
## State machine
|
|
120
|
+
|
|
121
|
+
```text
|
|
122
|
+
Aim PASS → Sweep → Sift
|
|
123
|
+
Sift PASS (findings) → Plot → handoff: grow
|
|
124
|
+
Sift PASS (dry round) → Track
|
|
125
|
+
Plot PASS → Grow → Pick/Roast inner loop → Brew → Taste → Track
|
|
126
|
+
Track PASS (backlog) → Plot
|
|
127
|
+
Track PASS (empty) → Sweep
|
|
128
|
+
Track PASS (target/budget/regression) → null, report and ask
|
|
129
|
+
Track PENDING (slice still in flight) → campaign waits → fresh Track
|
|
130
|
+
Any BLOCKED → named human/external gate
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The campaign loop owns persistence, retry, waiting, stop, and escalation. It never
|
|
134
|
+
implements a finding itself, never commits, and never opens a PR — Plot hands each slice
|
|
135
|
+
to Grow, and the lifecycle takes it from there.
|
|
136
|
+
|
|
137
|
+
## Hosts
|
|
138
|
+
|
|
139
|
+
| Host | Plugin | Invoke Aim | Invoke the campaign |
|
|
140
|
+
| --- | --- | --- | --- |
|
|
141
|
+
| Cursor | `terreno-scan` | `/terreno-scan-1-aim` | `/terreno-scan-campaign` |
|
|
142
|
+
| Codex | `terreno-scan` | `$terreno-scan-1-aim` | `$terreno-scan-campaign` |
|
|
143
|
+
| Claude Code | `terreno-scan` | `/terreno-scan:1-aim` | `/terreno-scan:campaign` |
|
|
144
|
+
|
|
145
|
+
The resident loop is `/terreno-scan-loop` (Cursor), `$terreno-scan-loop` (Codex), and
|
|
146
|
+
`/terreno-scan:loop` (Claude Code).
|
|
147
|
+
|
|
148
|
+
`plugins/terreno-scan/` is canonical. `plugins/terreno-scan-claude/` is generated with
|
|
149
|
+
shortened skill names; regenerate it with `bun run skills:sync` and never hand-edit it.
|
|
150
|
+
|
|
151
|
+
## Example
|
|
152
|
+
|
|
153
|
+
```text
|
|
154
|
+
/terreno-scan:loop reduce cold compile time reviewer=octocat wip=2
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Aim grills the target (30s), measures the baseline with `bun run compile` on a clean
|
|
158
|
+
tree, approves four rules, and records `octocat` as reviewer with a two-PR limit. Sweep
|
|
159
|
+
shards 180 hits into nine batches and maps nine workers. Sift keeps 12 of 31 candidates,
|
|
160
|
+
refuting four high-severity claims that did not reproduce. Plot writes three slices; the
|
|
161
|
+
loop runs the first through Grow, Pick/Roast, and Brew, then routes the PR to `octocat`.
|
|
162
|
+
While that PR waits, the loop opens the second slice up to the limit and then goes quiet,
|
|
163
|
+
ticking every 20 minutes. `octocat` requests a change at 14:05; the next tick hands that
|
|
164
|
+
PR to Taste, which replies, fixes, runs the pre-push gate, pushes, and watches CI. When
|
|
165
|
+
the PR merges, Track re-measures at 48.2s (from 58.6s), marks five findings fixed, and
|
|
166
|
+
the loop refills capacity with the third slice.
|
|
167
|
+
|
|
168
|
+
Stop it with a plain "stop" — it leaves every PR routed, state consistent, and reports
|
|
169
|
+
what is in flight and who holds it.
|