proteum 2.4.3 → 2.5.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 +60 -55
- package/agents/project/AGENTS.md +112 -31
- package/agents/project/CODING_STYLE.md +2 -2
- package/agents/project/app-root/AGENTS.md +1 -3
- package/agents/project/client/AGENTS.md +1 -1
- package/agents/project/client/pages/AGENTS.md +21 -9
- package/agents/project/diagnostics.md +2 -2
- package/agents/project/optimizations.md +1 -1
- package/agents/project/root/AGENTS.md +105 -22
- package/agents/project/server/routes/AGENTS.md +30 -1
- package/agents/project/tests/AGENTS.md +1 -1
- package/cli/commands/doctor.ts +54 -3
- package/cli/commands/runtime.ts +6 -0
- package/cli/commands/worktree.ts +116 -0
- package/cli/compiler/artifacts/controllers.ts +16 -15
- package/cli/compiler/artifacts/discovery.ts +129 -17
- package/cli/compiler/artifacts/routing.ts +0 -5
- package/cli/compiler/artifacts/services.ts +253 -76
- package/cli/compiler/common/controllers.ts +159 -57
- package/cli/compiler/common/generatedRouteModules.ts +457 -363
- package/cli/mcp/router.ts +47 -3
- package/cli/presentation/commands.ts +25 -15
- package/cli/runtime/commands.ts +39 -12
- package/cli/runtime/worktreeBootstrap.ts +608 -0
- package/cli/scaffold/index.ts +28 -18
- package/cli/scaffold/templates.ts +44 -33
- package/cli/utils/agents.ts +14 -1
- package/client/services/router/index.tsx +23 -3
- package/client/services/router/request/api.ts +14 -4
- package/common/dev/contractsDoctor.ts +1 -1
- package/common/dev/mcpPayloads.ts +8 -1
- package/common/env/proteumEnv.ts +14 -2
- package/common/router/contracts.ts +1 -1
- package/common/router/definitions.ts +177 -0
- package/common/router/index.ts +23 -12
- package/common/router/pageData.ts +5 -5
- package/common/router/register.ts +2 -2
- package/common/router/request/api.ts +12 -2
- package/docs/agent-routing.md +5 -2
- package/docs/diagnostics.md +2 -0
- package/docs/mcp.md +6 -3
- package/eslint.js +36 -1
- package/package.json +1 -1
- package/server/app/commands.ts +5 -1
- package/server/app/container/console/http-client-error-context.test.cjs +10 -1
- package/server/app/container/console/index.ts +2 -1
- package/server/app/controller/index.ts +98 -40
- package/server/app/index.ts +92 -1
- package/server/app/service/index.ts +5 -1
- package/server/index.ts +6 -2
- package/server/services/router/index.ts +47 -38
- package/server/services/router/response/index.ts +2 -2
- package/tests/agents-utils.test.cjs +14 -1
- package/tests/cli-mcp-command.test.cjs +84 -0
- package/tests/definition-contracts.test.cjs +453 -0
- package/tests/dev-transpile-watch.test.cjs +37 -28
- package/tests/eslint-rules.test.cjs +39 -1
- package/tests/mcp.test.cjs +90 -0
- package/tests/worktree-bootstrap.test.cjs +206 -0
- package/types/aliases.d.ts +0 -5
- package/types/controller-input.test.ts +23 -17
- package/types/controller-request-context.test.ts +10 -11
- package/cli/commands/migrate.ts +0 -51
- package/cli/migrate/pageContract.ts +0 -516
- package/docs/migrate-from-2.1.3.md +0 -396
- package/scripts/cleanup-generated-controllers.ts +0 -62
- package/scripts/fix-reference-app-typing.ts +0 -490
- package/scripts/format-router-registrations.ts +0 -119
- package/scripts/migrate-explicit-controllers-and-request.ts +0 -423
- package/scripts/refactor-client-app-imports.ts +0 -244
- package/scripts/refactor-client-pages.ts +0 -587
- package/scripts/refactor-server-controllers.ts +0 -471
- package/scripts/refactor-server-runtime-aliases.ts +0 -360
- package/scripts/restore-client-app-import-files.ts +0 -41
|
@@ -55,7 +55,7 @@ This file is the canonical source of truth for diagnostics, temporary instrument
|
|
|
55
55
|
## Verification And Testing
|
|
56
56
|
|
|
57
57
|
- Use the cheapest trustworthy verification that matches the failing layer.
|
|
58
|
-
- After implementing a change, verify at the smallest trustworthy layer required by the changed surface first,
|
|
58
|
+
- After implementing a change, verify at the smallest trustworthy layer required by the changed surface first, including targeted tests when behavior changed. Do not run coverage by default, and do not default to a running app, browser MCP, or Playwright while iterating when a narrower static or request-level verification is enough.
|
|
59
59
|
- For compile-time or type-safety issues, start with the relevant targeted typecheck or build command. Do not run them by default for unrelated runtime, copy, docs, or local refactor changes.
|
|
60
60
|
- For request/runtime issues, verify through the real page, route, generated controller call, or command on a running app.
|
|
61
61
|
- Start the smallest trustworthy runtime surface first: MCP `workflow_start`, then MCP `route_candidates { projectId, query }`, MCP `orient { projectId, query }`, or MCP `explain_summary { projectId, query }` only when more owner detail is needed. If runtime health is unreachable, repair/start dev before any diagnose, trace, or perf read. Once runtime is reachable, use the relevant real URL, generated controller call, command, or MCP `diagnose { projectId, path }`. Use CLI equivalents only when MCP is unavailable or terminal evidence is required. Use browser MCP validation only when request-level verification is insufficient or the change is browser-visible.
|
|
@@ -64,7 +64,7 @@ This file is the canonical source of truth for diagnostics, temporary instrument
|
|
|
64
64
|
- For browser regressions, prefer a browser MCP repro first and add targeted Playwright E2E coverage only when the user asks for automated coverage, when a stable regression path needs automation, or when browser MCP verification is insufficient.
|
|
65
65
|
- Only the final verifier agent should usually run browser flows. Earlier agents should stay on `orient`, `verify owner`, `verify request`, `diagnose`, and command-level checks unless browser execution is the only trustworthy reproducer.
|
|
66
66
|
- Treat server startup failures, runtime errors, browser console errors or warnings, and Playwright failures as blocking unless they are clearly unrelated to the change.
|
|
67
|
-
- When the touched surface can affect coding-style enforcement, run the
|
|
67
|
+
- When the touched surface can affect coding-style enforcement, run the targeted lint or typecheck command for that surface before finishing. Run the repository's non-coverage commit gate before committing, and run the full `npm run check` gate before pushing or when explicitly requested.
|
|
68
68
|
- If the task started any long-lived `proteum dev` server, stop it explicitly with `npx proteum dev stop --session-file <path>` or `npx proteum dev stop --all --stale`, then confirm the remaining tracked sessions with `npx proteum dev list --json`.
|
|
69
69
|
- Add `data-testid` when stable selectors are missing instead of relying on brittle text or DOM-shape selectors.
|
|
70
70
|
- If an isolated test misses prerequisite state, run the smallest broader scope that reproduces the real setup.
|
|
@@ -23,7 +23,7 @@ When tradeoffs exist inside optimization work, optimize in this order:
|
|
|
23
23
|
|
|
24
24
|
## SSR And Page Size
|
|
25
25
|
|
|
26
|
-
- SSR page data belongs in the explicit `
|
|
26
|
+
- SSR page data belongs in the explicit `definePageRoute({ path, options, data, render })` `data` function, not in `api.fetch(...)`.
|
|
27
27
|
- `options` carries route behavior. `data` returns one flat object or is `null` when the page has no SSR data loader.
|
|
28
28
|
- Route-option keys and `_`-prefixed route-option aliases are forbidden in page data and must live in `options`.
|
|
29
29
|
- If a page needs route data, return it from `data` and read it in `render`.
|
|
@@ -21,6 +21,7 @@ Managed compact root routers must use trigger -> canonical instruction file refe
|
|
|
21
21
|
- When a Proteum MCP client is available, first call MCP `workflow_start` with `cwd` or a known `projectId`. If it is ambiguous or returns offline app candidates, call `project_resolve { cwd }`, select the intended app root, start exactly one dev server from that app root when needed, then retry `workflow_start`. Pass the returned live `projectId` to every follow-up app-bound MCP tool. `npx proteum dev` ensures one managed machine MCP daemon is running; do not start a second managed daemon. Prefer MCP `runtime_status`, `orient`, `instructions_resolve`, `explain_summary`, `route_candidates`, `doctor`, `diagnose`, `trace_show`, `perf_request`, `logs_tail`, and `db_query` for read-only runtime/status/orientation/owner/route/trace/perf/log/database reads. Do not run CLI equivalents after a successful MCP result for the same read. Do not run broad source searches for route/page/controller ownership after MCP returns the owner. Use CLI commands when you need reproducible terminal validation, dev/build/check workflows, fallback repair, or output to share with a human.
|
|
22
22
|
- MCP payloads are compact single-line `proteum-mcp-v1` JSON with capped and paginated detail. Do not expand MCP output for human readability.
|
|
23
23
|
- For every non-trivial coding task, load and follow root-level `DOCUMENTATION.md` before coding.
|
|
24
|
+
- For bug fixes, regressions, incidents, broken public routes, auth/OAuth failures, integration failures, or production behavior fixes, load and follow root-level `DOCUMENTATION.md` before coding so the relevant fix note, regression-test docs, ADR, or explicit skip reason is handled in the same change.
|
|
24
25
|
- If the user reports an issue, or the agent encounters one during exploration, implementation, verification, or runtime reproduction, load and follow root-level `diagnostics.md`.
|
|
25
26
|
- If the task touches client-side files, especially `client/**` and page files, load and apply root-level `optimizations.md` only after implementation for post-implementation checking and optimization. Skip it at task start and skip it for server-only, test-only, doc-only, and non-client refactor tasks unless the user explicitly asks for optimization work.
|
|
26
27
|
- If the task needs new app or artifact boilerplate, prefer `npx proteum init ...` and `npx proteum create ...` before creating files by hand. Use `--dry-run --json` when an agent needs a machine-readable plan before writing files.
|
|
@@ -63,21 +64,22 @@ Managed compact root routers must use trigger -> canonical instruction file refe
|
|
|
63
64
|
### Before Finishing
|
|
64
65
|
|
|
65
66
|
- Before finishing, re-check touched files against root-level `CODING_STYLE.md` and any narrower area `AGENTS.md` that applied to the edit. Re-check against root-level `optimizations.md` only for touched client-side files. Re-check against root-level `diagnostics.md` only if the task involved an issue, diagnosis, runtime reproduction, or verification failure.
|
|
66
|
-
-
|
|
67
|
-
-
|
|
67
|
+
- Before finishing a production code change, re-check root-level `DOCUMENTATION.md` update rules. If behavior changed, a bug was fixed, a decision changed, or an important route, auth/OAuth, or integration issue was addressed, update the relevant docs before committing or explicitly explain why no docs update was needed.
|
|
68
|
+
- For production changes, always add or update focused unit tests and run the targeted unit or integration tests that match the changed behavior. Do not run coverage after every ordinary change by default. Reserve whole-project coverage for the repository's full `npm run check` gate during push workflows or when the user explicitly requests it; do not run coverage for commit-only workflows by default. Document any generated files, migrations, framework shims, unreachable defensive branches, or changes that cannot reasonably be unit-tested as explicit exceptions.
|
|
69
|
+
- Run targeted tests and checks that match the changed surface before finishing each feature or change. Continue running tests after changes, but do not run coverage by default. Reserve the non-coverage commit gate for commit workflows, and reserve the full `npm run check` gate for push workflows, explicit user requests, or when project-local instructions require the full gate. After implementing a new feature or changing existing feature behavior, update the relevant end-to-end coverage and run the cheapest trustworthy Playwright or browser verification for that behavior before finishing. For docs-only, wording-only, type-only, generated-output cleanup, or clearly local non-runtime refactors, skip Playwright unless the user explicitly asks for it or verification reveals a real issue.
|
|
68
70
|
- Before finishing a task, stop every `proteum dev` session started during the task and confirm cleanup with `npx proteum dev list --json` or an explicit `npx proteum dev stop --session-file <path>`.
|
|
69
71
|
- When you have finished your work, ask the user whether they want a commit message. After providing a commit message or after creating a commit, immediately follow it with this exact prompt and obey it:
|
|
70
72
|
`Explain in short minimalistic and few bullet points what we changed in this thread, like you would do to your grandma. Start with a verb in the past.`
|
|
71
73
|
|
|
72
74
|
## Core Contracts
|
|
73
75
|
|
|
74
|
-
- Client pages live in `client/pages/**` and
|
|
75
|
-
- Page URLs come from the explicit `
|
|
76
|
-
- Callable app APIs live only in `server/controllers/**/*.ts` files that
|
|
76
|
+
- Client pages live in `client/pages/**` and default-export `definePageRoute(...)` or `defineErrorRoute(...)`.
|
|
77
|
+
- Page URLs come from the explicit route definition `path`, not from the file path.
|
|
78
|
+
- Callable app APIs live only in `server/controllers/**/*.ts` files that default-export `defineController(...)`.
|
|
77
79
|
- Dev-only internal execution lives only in `commands/**/*.ts` files that extend `Commands`.
|
|
78
80
|
- Manual HTTP endpoints live only in `server/routes/**`.
|
|
79
|
-
- Controllers
|
|
80
|
-
- Request-scoped state lives only on
|
|
81
|
+
- Controllers declare input on `defineAction({ input, handler })`; handlers receive parsed `input` in context.
|
|
82
|
+
- Request-scoped state lives only on action handler context and manual-route handler context objects.
|
|
81
83
|
- Keep one class or one React/Preact component per file.
|
|
82
84
|
- Prefer a deep tree grouped by business concern instead of long file names.
|
|
83
85
|
- Use the default `*.ts` or `*.tsx` file unless an `*.ssr.ts` or `*.ssr.tsx` variant is truly required.
|
|
@@ -90,14 +92,15 @@ Managed compact root routers must use trigger -> canonical instruction file refe
|
|
|
90
92
|
- Do not import runtime values from `@models`.
|
|
91
93
|
- Do not use `@request` runtime globals.
|
|
92
94
|
- Do not use `@app` on the client.
|
|
95
|
+
- Do not import `@app` in route, page, or controller files. Runtime app/services/router access belongs in typed callback parameters.
|
|
93
96
|
- Prefer type inference rooted in the explicit application graph in `server/index.ts`.
|
|
94
97
|
|
|
95
98
|
## Surface Contracts
|
|
96
99
|
|
|
97
100
|
### App Bootstrap And Services
|
|
98
101
|
|
|
99
|
-
- `server/index.ts` default-exports
|
|
100
|
-
- Root services are
|
|
102
|
+
- `server/index.ts` default-exports `defineApplication({ services, router, models, commands })` and is the canonical type root.
|
|
103
|
+
- Root services are declared in the explicit `services` graph and instantiated with `new ServiceClass(app, config, app)`.
|
|
101
104
|
- Typed root-service config lives in `server/config/*.ts` via `Services.config(ServiceClass, { ... })`.
|
|
102
105
|
- Router plugins are instantiated explicitly inside the `Router` config `plugins` object.
|
|
103
106
|
- Router plugins can subscribe to `request` and `request.finished`; `request.profiling` exists before `request` runs and carries the finalized request/API/SQL snapshot by `request.finished`.
|
|
@@ -109,6 +112,51 @@ Managed compact root routers must use trigger -> canonical instruction file refe
|
|
|
109
112
|
- Companion client-callable entrypoints live in `server/controllers/**`.
|
|
110
113
|
- `proteum create service ...` scaffolds the service file, a typed config export under `server/config/*.ts`, and the root registration in `server/index.ts`; review and adapt the generated names before committing.
|
|
111
114
|
|
|
115
|
+
Example app root shape; replace names with the project app type and service names:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { defineApplication, type Application } from '@server/app';
|
|
119
|
+
import Router from '@server/services/router';
|
|
120
|
+
import SchemaRouter from '@server/services/schema/router';
|
|
121
|
+
import BillingService from '@/server/services/Billing';
|
|
122
|
+
|
|
123
|
+
import * as appConfig from '@/server/config/app';
|
|
124
|
+
|
|
125
|
+
type ProjectServices = {
|
|
126
|
+
Billing: BillingService;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
type ProjectRouterPlugins = {
|
|
130
|
+
schema: SchemaRouter;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export type ProjectRouter = Router<ProjectApp, ProjectRouterPlugins>;
|
|
134
|
+
export interface ProjectApp extends Application, ProjectServices {
|
|
135
|
+
Router: ProjectRouter;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const createProjectRouter = (app: ProjectApp): ProjectRouter =>
|
|
139
|
+
new Router<ProjectApp, ProjectRouterPlugins>(
|
|
140
|
+
app,
|
|
141
|
+
{
|
|
142
|
+
...appConfig.routerBaseConfig,
|
|
143
|
+
plugins: {
|
|
144
|
+
schema: new SchemaRouter({}, app),
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
app,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const ProjectApplication = defineApplication<ProjectServices, ProjectRouter>({
|
|
151
|
+
services: (app) => ({
|
|
152
|
+
Billing: new BillingService(app, {}, app),
|
|
153
|
+
}),
|
|
154
|
+
router: createProjectRouter,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
export default ProjectApplication;
|
|
158
|
+
```
|
|
159
|
+
|
|
112
160
|
### Connected Projects
|
|
113
161
|
|
|
114
162
|
- Declare connected namespaces in `proteum.config.ts` with explicit values such as `connect: { Product: { source: PRODUCT_CONNECTED_SOURCE, urlInternal: PRODUCT_URL_INTERNAL } }`.
|
|
@@ -120,13 +168,27 @@ Managed compact root routers must use trigger -> canonical instruction file refe
|
|
|
120
168
|
|
|
121
169
|
### Controllers
|
|
122
170
|
|
|
123
|
-
- Files live under `server/controllers/**/*.ts` and default-export
|
|
124
|
-
-
|
|
125
|
-
- Route path comes from the controller
|
|
126
|
-
- `
|
|
171
|
+
- Files live under `server/controllers/**/*.ts` and default-export `defineController({ path, actions })`.
|
|
172
|
+
- Actions declared with `defineAction(...)` become generated client-callable endpoints.
|
|
173
|
+
- Route path comes from the controller `path` plus the action name.
|
|
174
|
+
- Set `path: 'Custom/path'` on `defineController(...)` to override the base path.
|
|
127
175
|
- Generated client calls use `POST`.
|
|
128
176
|
- Prefer `proteum create controller ...` for new controller boilerplate, then adapt the generated method to real service calls.
|
|
129
177
|
|
|
178
|
+
```ts
|
|
179
|
+
import { defineAction, defineController, schema } from '@server/app/controller';
|
|
180
|
+
|
|
181
|
+
export default defineController({
|
|
182
|
+
path: 'Billing',
|
|
183
|
+
actions: {
|
|
184
|
+
read: defineAction({
|
|
185
|
+
input: schema.object({ accountId: schema.string() }),
|
|
186
|
+
handler: ({ input }) => ({ accountId: input.accountId }),
|
|
187
|
+
}),
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
130
192
|
### Commands
|
|
131
193
|
|
|
132
194
|
- Files live under `commands/**/*.ts` and default-export a class extending `Commands` from `@server/app/commands`.
|
|
@@ -139,25 +201,47 @@ Managed compact root routers must use trigger -> canonical instruction file refe
|
|
|
139
201
|
|
|
140
202
|
### Client Pages
|
|
141
203
|
|
|
142
|
-
- Proteum scans page files for
|
|
143
|
-
- File path controls chunk identity and layout discovery; route path comes from the explicit `
|
|
144
|
-
- The
|
|
204
|
+
- Proteum scans page files for default-exported `definePageRoute(...)` and `defineErrorRoute(...)` definitions.
|
|
205
|
+
- File path controls chunk identity and layout discovery; route path comes from the explicit definition `path` value.
|
|
206
|
+
- The supported page shape is `definePageRoute({ path, options, data, render })`.
|
|
145
207
|
- `options` is always required. `data` is the only nullable argument and must be `null` when the page has no SSR data loader.
|
|
146
208
|
- `data` returns one flat object. Route-option keys such as `auth`, `layout`, `static`, and `_static` are forbidden in page data and must live in `options`.
|
|
147
209
|
- Controller fetchers and promises returned from `data` resolve before render.
|
|
148
210
|
- `render` consumes resolved page data and uses generated controller methods from render args or `@/client/context`.
|
|
149
211
|
- Use `api.reload(...)` or `api.set(...)` only when intentionally mutating active page data state.
|
|
150
|
-
- Error pages use `
|
|
212
|
+
- Error pages use `defineErrorRoute({ code, options, render })` in `client/pages/_messages/**`.
|
|
151
213
|
- Prefer `proteum create page ...` for new page boilerplate, then review the explicit route path, options object, and data payload.
|
|
152
214
|
|
|
215
|
+
```tsx
|
|
216
|
+
import { definePageRoute } from '@common/router/definitions';
|
|
217
|
+
|
|
218
|
+
export default definePageRoute({
|
|
219
|
+
path: '/billing',
|
|
220
|
+
options: { auth: true },
|
|
221
|
+
data: ({ BillingController }) => ({ billing: BillingController.read({ accountId: 'current' }) }),
|
|
222
|
+
render: ({ billing }) => <BillingPage billing={billing} />,
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
153
226
|
### Manual Routes
|
|
154
227
|
|
|
155
228
|
- Use `server/routes/**` only for explicit HTTP behavior that should not be a generated controller action.
|
|
156
229
|
- Good fits include redirects, sitemap or RSS output, OAuth callbacks, webhooks, and public resources with custom semantics.
|
|
157
|
-
-
|
|
230
|
+
- Receive app services through `defineServerRoutes((app) => [...])` and use handler context for `request`, `response`, router plugins, and custom router context.
|
|
158
231
|
- If the route is a normal app API, prefer a controller.
|
|
159
232
|
- Prefer `proteum create route ...` for new manual-route boilerplate.
|
|
160
233
|
|
|
234
|
+
```ts
|
|
235
|
+
import { defineServerRoute } from '@common/router/definitions';
|
|
236
|
+
|
|
237
|
+
export default defineServerRoute({
|
|
238
|
+
method: 'GET',
|
|
239
|
+
path: '/health',
|
|
240
|
+
options: {},
|
|
241
|
+
handler: ({ response }) => response.json({ ok: true }),
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
161
245
|
### Models And Aliases
|
|
162
246
|
|
|
163
247
|
- Use Prisma typings from `@models/types`.
|
|
@@ -167,19 +251,18 @@ Managed compact root routers must use trigger -> canonical instruction file refe
|
|
|
167
251
|
- Aliases:
|
|
168
252
|
- `@/client/...`, `@/server/...`, `@/common/...`: app code
|
|
169
253
|
- `@client/...`, `@server/...`, `@common/...`: Proteum core modules
|
|
170
|
-
- `@app`: server-side application services for manual routes only
|
|
171
254
|
- `@generated/*`: generated app surfaces
|
|
172
255
|
|
|
173
256
|
## Verification Matrix
|
|
174
257
|
|
|
175
258
|
Verify at the correct layer:
|
|
176
259
|
|
|
177
|
-
- Default: use the cheapest trustworthy verification for the changed surface
|
|
260
|
+
- Default: use the cheapest trustworthy verification for the changed surface, including targeted tests for changed behavior. Do not run coverage by default during ordinary change closeout.
|
|
178
261
|
- Route additions: boot the app and hit the real URL.
|
|
179
262
|
- Controller changes: exercise the generated client call or generated `/api/...` endpoint.
|
|
180
263
|
- SSR changes: use the browser MCP to load the real page and inspect rendered HTML plus browser console.
|
|
181
264
|
- Router or plugin changes: verify request context, auth, redirects, metrics, and validation on a running app.
|
|
182
|
-
- New features or feature-behavior changes: use the cheapest trustworthy verification while iterating, use the browser MCP for browser-visible validation, then update the relevant end-to-end coverage
|
|
265
|
+
- New features or feature-behavior changes: use the cheapest trustworthy verification while iterating, use the browser MCP for browser-visible validation, then update and run the relevant end-to-end coverage. Save the non-coverage commit gate for commit workflows and the full `npm run check` gate for push workflows unless the user or project-local instructions explicitly ask for the full gate earlier.
|
|
183
266
|
- Generated, connected, or ownership-ambiguous changes: start with MCP `workflow_start`, then `orient { projectId, query }` and `explain_summary { projectId, query }` only when more detail is needed; use `npx proteum orient <query>` and `npx proteum verify owner <query>` when MCP is unavailable or terminal evidence is required.
|
|
184
267
|
- Browser-visible issues: use the browser MCP after request-level verification is insufficient. Use `npx proteum e2e --port <port> ...` only when automated end-to-end coverage or a Playwright suite is required.
|
|
185
268
|
- Raw browser execution outside end-to-end suites: use the browser MCP only. Keep Playwright in `npx proteum e2e --port <port>` for targeted/full end-to-end suites.
|
|
@@ -217,7 +300,7 @@ Verify at the correct layer:
|
|
|
217
300
|
### Discouraged Patterns
|
|
218
301
|
|
|
219
302
|
- request-scoped state inside normal service methods
|
|
220
|
-
- hiding route
|
|
303
|
+
- hiding route definitions behind abstractions that remove the default-exported `definePageRoute(...)` or `defineServerRoute(...)` contract
|
|
221
304
|
- editing `.proteum` directly
|
|
222
305
|
|
|
223
306
|
## Hard Stops
|
|
@@ -11,8 +11,37 @@ Diagnostics source of truth: root-level `diagnostics.md`.
|
|
|
11
11
|
- Use `server/routes/**` only for explicit HTTP behavior that should not be generated from controllers.
|
|
12
12
|
- If the endpoint is a normal app API, prefer `server/controllers/**/*.ts`.
|
|
13
13
|
- Good fits include redirects, resources, OAuth callbacks, webhooks, sitemap-like output, and custom public endpoints.
|
|
14
|
+
- Route files default-export `defineServerRoute({ method, path, options, handler })` or `defineServerRoutes([...])`.
|
|
15
|
+
- Keep `method`, `path`, and `options` static. Runtime services are received through the route factory or handler context.
|
|
16
|
+
- Do not import `@app` in route files. Use `defineServerRoutes((app) => [...])` when routes need app services.
|
|
17
|
+
- Use `expressHandler(...)` only when a route needs raw Express `req`, `res`, or `next`.
|
|
14
18
|
- If a route needs a curated registry, keep server-only data in `/server/catalogs/**` and shared data in `/common/catalogs/**`.
|
|
15
19
|
|
|
20
|
+
Example route file; replace `ProjectApp` with the concrete app type exported from `server/index.ts`.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { defineServerRoute, defineServerRoutes, expressHandler } from '@common/router/definitions';
|
|
24
|
+
import type { ProjectApp } from '@/server/index';
|
|
25
|
+
|
|
26
|
+
export default defineServerRoutes((app: ProjectApp) => [
|
|
27
|
+
defineServerRoute({
|
|
28
|
+
method: 'GET',
|
|
29
|
+
path: '/health',
|
|
30
|
+
options: {},
|
|
31
|
+
handler: ({ response }) => response.json({ ok: true }),
|
|
32
|
+
}),
|
|
33
|
+
defineServerRoute({
|
|
34
|
+
method: 'POST',
|
|
35
|
+
path: '/webhook',
|
|
36
|
+
options: {},
|
|
37
|
+
handler: expressHandler((request, response) => {
|
|
38
|
+
app.Webhooks.handle(request.body);
|
|
39
|
+
response.status(204).send('');
|
|
40
|
+
}),
|
|
41
|
+
}),
|
|
42
|
+
]);
|
|
43
|
+
```
|
|
44
|
+
|
|
16
45
|
## Absolute URLs
|
|
17
46
|
|
|
18
|
-
Use `Router.url('/relative/path')` to generate absolute URLs.
|
|
47
|
+
Use `context.Router.url('/relative/path')` inside handlers, or `app.Router.url('/relative/path')` inside `defineServerRoutes((app) => ...)`, to generate absolute URLs.
|
|
@@ -9,7 +9,7 @@ Diagnostics source of truth: root-level `diagnostics.md`.
|
|
|
9
9
|
|
|
10
10
|
- Understand the real user flow and the main feature branches before writing tests.
|
|
11
11
|
- Test the current controller/page runtime model, not legacy `@Route` or `api.fetch(...)` behavior.
|
|
12
|
-
- For every production change, add or update focused unit tests and
|
|
12
|
+
- For every production change, add or update focused unit tests and run the targeted test command that matches the changed behavior. Do not run whole-project coverage after every ordinary change by default. Use the repository's non-coverage commit gate before commit, and use `npm run check` as the full gate before push or when the user explicitly asks for it, and document any generated files, migrations, framework shims, unreachable defensive branches, or changes that cannot reasonably be unit-tested as explicit exceptions.
|
|
13
13
|
- Verify routing, controllers, SSR, and router plugins against a running app when behavior depends on real request handling.
|
|
14
14
|
- After implementing a new feature or changing existing feature behavior, update the end-to-end coverage for that behavior and run the full Playwright suite before finishing. Prefer `npx proteum e2e --port <port>` for Playwright runs so base URLs and auth tokens are passed through Proteum-managed child env instead of shell-leading environment assignments. Use a browser MCP repro against a running app during iteration when it is the fastest trustworthy loop.
|
|
15
15
|
- Exercise real URLs, generated controller calls, or real browser flows instead of re-deriving framework internals in tests.
|
package/cli/commands/doctor.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import cli from '..';
|
|
2
2
|
import Compiler from '../compiler';
|
|
3
3
|
import { readProteumManifest } from '../compiler/common/proteumManifest';
|
|
4
|
+
import {
|
|
5
|
+
createWorktreeBootstrapDiagnostics,
|
|
6
|
+
getWorktreeBootstrapStatus,
|
|
7
|
+
type TWorktreeBootstrapDiagnostic,
|
|
8
|
+
} from '../runtime/worktreeBootstrap';
|
|
4
9
|
import { buildContractsDoctorResponse } from '@common/dev/contractsDoctor';
|
|
5
|
-
import { buildDoctorResponse,
|
|
10
|
+
import { buildDoctorResponse, renderDoctorResponseHuman } from '@common/dev/diagnostics';
|
|
6
11
|
import { compactList, printAgentResponse, printJson, truncateForAgent } from '../utils/agentOutput';
|
|
12
|
+
import type { TDoctorResponse } from '@common/dev/diagnostics';
|
|
7
13
|
|
|
8
14
|
const allowedDoctorArgs = new Set(['contracts', 'full', 'human', 'json', 'strict']);
|
|
9
15
|
|
|
@@ -30,6 +36,31 @@ const compactDiagnostic = (diagnostic: ReturnType<typeof buildDoctorResponse>['d
|
|
|
30
36
|
fixHint: diagnostic.fixHint ? truncateForAgent(diagnostic.fixHint) : undefined,
|
|
31
37
|
});
|
|
32
38
|
|
|
39
|
+
const mergeBootstrapDiagnostics = ({
|
|
40
|
+
bootstrapDiagnostics,
|
|
41
|
+
response,
|
|
42
|
+
strict,
|
|
43
|
+
}: {
|
|
44
|
+
bootstrapDiagnostics: TWorktreeBootstrapDiagnostic[];
|
|
45
|
+
response: TDoctorResponse;
|
|
46
|
+
strict: boolean;
|
|
47
|
+
}): TDoctorResponse => {
|
|
48
|
+
if (bootstrapDiagnostics.length === 0) return response;
|
|
49
|
+
|
|
50
|
+
const diagnostics = [...response.diagnostics, ...bootstrapDiagnostics];
|
|
51
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.level === 'error').length;
|
|
52
|
+
const warnings = diagnostics.filter((diagnostic) => diagnostic.level === 'warning').length;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
diagnostics,
|
|
56
|
+
summary: {
|
|
57
|
+
errors,
|
|
58
|
+
strictFailed: response.summary.strictFailed || (strict && diagnostics.length > 0),
|
|
59
|
+
warnings,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
33
64
|
export const run = async (): Promise<void> => {
|
|
34
65
|
validateDoctorArgs();
|
|
35
66
|
|
|
@@ -37,10 +68,25 @@ export const run = async (): Promise<void> => {
|
|
|
37
68
|
await compiler.refreshGeneratedTypings();
|
|
38
69
|
|
|
39
70
|
const manifest = readProteumManifest(cli.paths.appRoot);
|
|
40
|
-
const
|
|
71
|
+
const rawResponse =
|
|
41
72
|
cli.args.contracts === true
|
|
42
73
|
? buildContractsDoctorResponse(manifest, cli.args.strict === true)
|
|
43
74
|
: buildDoctorResponse(manifest, cli.args.strict === true);
|
|
75
|
+
const bootstrapStatus = getWorktreeBootstrapStatus({
|
|
76
|
+
appRoot: cli.paths.appRoot,
|
|
77
|
+
proteumVersion: String(cli.packageJson.version || ''),
|
|
78
|
+
});
|
|
79
|
+
const response =
|
|
80
|
+
cli.args.contracts === true
|
|
81
|
+
? rawResponse
|
|
82
|
+
: mergeBootstrapDiagnostics({
|
|
83
|
+
bootstrapDiagnostics: createWorktreeBootstrapDiagnostics({
|
|
84
|
+
appRoot: cli.paths.appRoot,
|
|
85
|
+
status: bootstrapStatus,
|
|
86
|
+
}),
|
|
87
|
+
response: rawResponse,
|
|
88
|
+
strict: cli.args.strict === true,
|
|
89
|
+
});
|
|
44
90
|
|
|
45
91
|
if (cli.args.full === true) {
|
|
46
92
|
printJson(response);
|
|
@@ -53,7 +99,12 @@ export const run = async (): Promise<void> => {
|
|
|
53
99
|
response,
|
|
54
100
|
title: 'Proteum doctor contracts',
|
|
55
101
|
})
|
|
56
|
-
:
|
|
102
|
+
: renderDoctorResponseHuman({
|
|
103
|
+
emptyMessage: 'No diagnostics were found.',
|
|
104
|
+
manifest,
|
|
105
|
+
response,
|
|
106
|
+
title: 'Proteum doctor',
|
|
107
|
+
}),
|
|
57
108
|
);
|
|
58
109
|
} else {
|
|
59
110
|
printAgentResponse({
|
package/cli/commands/runtime.ts
CHANGED
|
@@ -7,6 +7,7 @@ import cli from '..';
|
|
|
7
7
|
import { readProteumManifest } from '../compiler/common/proteumManifest';
|
|
8
8
|
import { listDevSessionInspections, writeMachineDevSessionRecord, type TDevSessionInspection } from '../runtime/devSessions';
|
|
9
9
|
import { inspectDevPort, type TDevPortInspection } from '../runtime/ports';
|
|
10
|
+
import { compactWorktreeBootstrapStatus, getWorktreeBootstrapStatus } from '../runtime/worktreeBootstrap';
|
|
10
11
|
import { printAgentResponse, printJson, quoteCommandArgument } from '../utils/agentOutput';
|
|
11
12
|
import type { TDoctorResponse } from '@common/dev/diagnostics';
|
|
12
13
|
import type { TProteumManifest } from '@common/dev/proteumManifest';
|
|
@@ -193,6 +194,10 @@ export const run = async () => {
|
|
|
193
194
|
port: manifest.env.resolved.routerPort,
|
|
194
195
|
})
|
|
195
196
|
: undefined;
|
|
197
|
+
const worktreeBootstrap = getWorktreeBootstrapStatus({
|
|
198
|
+
appRoot: cli.paths.appRoot,
|
|
199
|
+
proteumVersion: String(cli.packageJson.version || ''),
|
|
200
|
+
});
|
|
196
201
|
|
|
197
202
|
const payload = {
|
|
198
203
|
appRoot: cli.paths.appRoot,
|
|
@@ -216,6 +221,7 @@ export const run = async () => {
|
|
|
216
221
|
sessions: sessions.map(compactSession),
|
|
217
222
|
health,
|
|
218
223
|
configuredDevPort,
|
|
224
|
+
worktreeBootstrap: compactWorktreeBootstrapStatus(worktreeBootstrap),
|
|
219
225
|
};
|
|
220
226
|
|
|
221
227
|
if (cli.args.full === true) {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { UsageError } from 'clipanion';
|
|
2
|
+
|
|
3
|
+
import cli from '..';
|
|
4
|
+
import {
|
|
5
|
+
compactWorktreeBootstrapStatus,
|
|
6
|
+
getWorktreeBootstrapStatus,
|
|
7
|
+
runWorktreeBootstrapCreate,
|
|
8
|
+
runWorktreeBootstrapInit,
|
|
9
|
+
} from '../runtime/worktreeBootstrap';
|
|
10
|
+
import { printAgentResponse, printJson } from '../utils/agentOutput';
|
|
11
|
+
|
|
12
|
+
/*----------------------------------
|
|
13
|
+
- HELPERS
|
|
14
|
+
----------------------------------*/
|
|
15
|
+
|
|
16
|
+
const getAction = () => {
|
|
17
|
+
const action = typeof cli.args.action === 'string' ? cli.args.action : '';
|
|
18
|
+
if (action === 'init' || action === 'create') return action;
|
|
19
|
+
|
|
20
|
+
throw new UsageError('Usage: `proteum worktree init` or `proteum worktree create <target-repo-root>`.');
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getStringArg = (name: string) => {
|
|
24
|
+
const value = cli.args[name];
|
|
25
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getBooleanArg = (name: string) => cli.args[name] === true;
|
|
29
|
+
|
|
30
|
+
const printResult = ({
|
|
31
|
+
data,
|
|
32
|
+
json,
|
|
33
|
+
summary,
|
|
34
|
+
}: {
|
|
35
|
+
data: object;
|
|
36
|
+
json: boolean;
|
|
37
|
+
summary: string;
|
|
38
|
+
}) => {
|
|
39
|
+
if (json) {
|
|
40
|
+
printJson({ ok: true, format: 'proteum-agent-v1', summary, data });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
printAgentResponse({ summary, data });
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/*----------------------------------
|
|
48
|
+
- COMMAND
|
|
49
|
+
----------------------------------*/
|
|
50
|
+
|
|
51
|
+
export const run = async (): Promise<void> => {
|
|
52
|
+
const action = getAction();
|
|
53
|
+
const json = getBooleanArg('json');
|
|
54
|
+
const source = getStringArg('source') || undefined;
|
|
55
|
+
const skipDeps = getBooleanArg('skipDeps');
|
|
56
|
+
const reason = getStringArg('reason') || undefined;
|
|
57
|
+
|
|
58
|
+
if (action === 'init') {
|
|
59
|
+
const result = await runWorktreeBootstrapInit({
|
|
60
|
+
appRoot: cli.paths.appRoot,
|
|
61
|
+
coreRoot: cli.paths.core.root,
|
|
62
|
+
json,
|
|
63
|
+
proteumVersion: String(cli.packageJson.version || ''),
|
|
64
|
+
reason,
|
|
65
|
+
refresh: getBooleanArg('refresh'),
|
|
66
|
+
skipDeps,
|
|
67
|
+
source,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
printResult({
|
|
71
|
+
data: {
|
|
72
|
+
appRoot: result.appRoot,
|
|
73
|
+
markerFilepath: result.markerFilepath,
|
|
74
|
+
worktreeBootstrap: compactWorktreeBootstrapStatus(result.status),
|
|
75
|
+
},
|
|
76
|
+
json,
|
|
77
|
+
summary: 'Proteum worktree bootstrap completed.',
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const targetRepoRoot = getStringArg('target');
|
|
83
|
+
const branch = getStringArg('branch');
|
|
84
|
+
if (!source) throw new UsageError('worktree create requires --source <source-app-root>.');
|
|
85
|
+
|
|
86
|
+
const result = await runWorktreeBootstrapCreate({
|
|
87
|
+
appRoot: source,
|
|
88
|
+
base: getStringArg('base') || undefined,
|
|
89
|
+
branch,
|
|
90
|
+
coreRoot: cli.paths.core.root,
|
|
91
|
+
json,
|
|
92
|
+
proteumVersion: String(cli.packageJson.version || ''),
|
|
93
|
+
reason,
|
|
94
|
+
refresh: true,
|
|
95
|
+
skipDeps,
|
|
96
|
+
source,
|
|
97
|
+
targetRepoRoot,
|
|
98
|
+
});
|
|
99
|
+
const status = getWorktreeBootstrapStatus({
|
|
100
|
+
appRoot: result.targetAppRoot,
|
|
101
|
+
proteumVersion: String(cli.packageJson.version || ''),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
printResult({
|
|
105
|
+
data: {
|
|
106
|
+
branch: result.branch,
|
|
107
|
+
sourceAppRoot: result.sourceAppRoot,
|
|
108
|
+
sourceRepoRoot: result.sourceRepoRoot,
|
|
109
|
+
targetAppRoot: result.targetAppRoot,
|
|
110
|
+
targetRepoRoot: result.targetRepoRoot,
|
|
111
|
+
worktreeBootstrap: compactWorktreeBootstrapStatus(status),
|
|
112
|
+
},
|
|
113
|
+
json,
|
|
114
|
+
summary: `Created Proteum worktree at ${result.targetRepoRoot}.`,
|
|
115
|
+
});
|
|
116
|
+
};
|