gorsee 0.2.11 → 0.2.13

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.
Files changed (49) hide show
  1. package/README.md +52 -4
  2. package/dist-pkg/ai/bundle.d.ts +1 -0
  3. package/dist-pkg/ai/framework-context.d.ts +2 -0
  4. package/dist-pkg/ai/framework-context.js +6 -1
  5. package/dist-pkg/ai/ide.d.ts +1 -0
  6. package/dist-pkg/ai/ide.js +3 -0
  7. package/dist-pkg/ai/index.d.ts +10 -1
  8. package/dist-pkg/ai/index.js +13 -2
  9. package/dist-pkg/ai/mcp.js +4 -0
  10. package/dist-pkg/ai/session-pack.d.ts +8 -0
  11. package/dist-pkg/ai/session-pack.js +51 -1
  12. package/dist-pkg/ai/store.d.ts +25 -1
  13. package/dist-pkg/ai/store.js +89 -3
  14. package/dist-pkg/ai/summary.d.ts +88 -0
  15. package/dist-pkg/ai/summary.js +310 -1
  16. package/dist-pkg/build/manifest.d.ts +4 -2
  17. package/dist-pkg/build/manifest.js +32 -2
  18. package/dist-pkg/cli/cmd-ai.js +66 -0
  19. package/dist-pkg/cli/cmd-build.js +72 -26
  20. package/dist-pkg/cli/cmd-check.js +104 -11
  21. package/dist-pkg/cli/cmd-create.js +333 -7
  22. package/dist-pkg/cli/cmd-deploy.js +17 -3
  23. package/dist-pkg/cli/cmd-docs.d.ts +3 -1
  24. package/dist-pkg/cli/cmd-docs.js +5 -3
  25. package/dist-pkg/cli/cmd-start.js +8 -1
  26. package/dist-pkg/cli/cmd-upgrade.d.ts +3 -0
  27. package/dist-pkg/cli/cmd-upgrade.js +14 -2
  28. package/dist-pkg/cli/cmd-worker.d.ts +9 -0
  29. package/dist-pkg/cli/cmd-worker.js +78 -0
  30. package/dist-pkg/cli/framework-md.js +16 -4
  31. package/dist-pkg/cli/index.js +5 -0
  32. package/dist-pkg/runtime/app-config.d.ts +5 -0
  33. package/dist-pkg/runtime/app-config.js +26 -5
  34. package/dist-pkg/runtime/typed-routes.js +3 -4
  35. package/dist-pkg/server/index.d.ts +2 -1
  36. package/dist-pkg/server/index.js +1 -0
  37. package/dist-pkg/server/jobs.d.ts +35 -1
  38. package/dist-pkg/server/jobs.js +226 -3
  39. package/dist-pkg/server/manifest.d.ts +30 -0
  40. package/dist-pkg/server/manifest.js +30 -1
  41. package/dist-pkg/server/redis-client.d.ts +9 -0
  42. package/dist-pkg/server/redis-client.js +4 -1
  43. package/dist-pkg/server/redis-job-queue.d.ts +2 -0
  44. package/dist-pkg/server/redis-job-queue.js +434 -16
  45. package/dist-pkg/server/worker-service.d.ts +33 -0
  46. package/dist-pkg/server/worker-service.js +135 -0
  47. package/dist-pkg/server-entry.d.ts +2 -1
  48. package/dist-pkg/server-entry.js +4 -0
  49. package/package.json +3 -3
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # Gorsee.js
2
2
 
3
- AI-first reactive full-stack TypeScript framework for deterministic collaboration between humans and coding agents.
3
+ AI-first reactive full-stack TypeScript framework and application platform for deterministic collaboration between humans and coding agents across frontend, fullstack, and server systems.
4
4
 
5
- Gorsee is not a pet project, not a research toy, and not a bundle of optional recipes. It is a product-grade framework with strict runtime contracts, built-in security boundaries, fine-grained reactivity, and AI-native developer tooling.
5
+ Gorsee is not a pet project, not a research toy, and not a bundle of optional recipes. It is a product-grade platform with strict runtime contracts, built-in security boundaries, fine-grained reactivity, and AI-native developer tooling.
6
6
 
7
7
  ## Product Direction
8
8
 
9
9
  Gorsee is built around four non-negotiable product goals:
10
10
 
11
- - AI-first development: the framework must be predictable for coding agents, not only ergonomic for humans.
11
+ - AI-first development: the platform must be predictable for coding agents, not only ergonomic for humans.
12
12
  - Deterministic architecture: one clear way beats many inconsistent ways.
13
13
  - Reactive execution: fine-grained reactivity, islands, and minimal client JavaScript over VDOM-heavy legacy models.
14
14
  - Built-in guarantees: auth, request policy, cache boundaries, diagnostics, and deploy contracts are framework properties, not scattered ecosystem recipes.
@@ -17,6 +17,7 @@ Read the strategic docs:
17
17
 
18
18
  - [Product Vision](./docs/PRODUCT_VISION.md)
19
19
  - [Framework Doctrine](./docs/FRAMEWORK_DOCTRINE.md)
20
+ - [Application Modes](./docs/APPLICATION_MODES.md)
20
21
  - [Security Model](./docs/SECURITY_MODEL.md)
21
22
  - [Top-Tier Roadmap](./docs/TOP_TIER_ROADMAP.md)
22
23
  - [Canonical Language Plan](./docs/CANONICAL_LANGUAGE_PLAN.md)
@@ -77,6 +78,33 @@ Read the strategic docs:
77
78
  - [Evidence Policy](./docs/EVIDENCE_POLICY.md)
78
79
  - [Roadmap Completion Policy](./docs/ROADMAP_COMPLETION_POLICY.md)
79
80
 
81
+ ## Canonical Modes
82
+
83
+ Gorsee ships one product with three canonical application modes:
84
+
85
+ - `frontend` for browser-first prerendered apps deployed to static-capable targets
86
+ - `fullstack` for the canonical Gorsee route/runtime model across UI + server execution
87
+ - `server` for API-first and service-oriented systems without mandatory browser surfaces
88
+
89
+ Set the mode explicitly in `app.config.ts`:
90
+
91
+ ```ts
92
+ export default {
93
+ app: {
94
+ mode: "fullstack",
95
+ },
96
+ }
97
+ ```
98
+
99
+ `fullstack` remains the default when `app.mode` is omitted.
100
+
101
+ High-level mode guidance:
102
+
103
+ - `frontend` builds browser-first prerendered output and should prefer static-capable deploy targets.
104
+ - `fullstack` keeps the canonical Gorsee route/runtime model and can target Bun, Node, or worker-style deploy adapters.
105
+ - `server` focuses on API-first and service-oriented systems with process/runtime ownership kept explicit.
106
+ - `server` mode should prefer `gorsee worker` for canonical long-running worker and service entry execution.
107
+
80
108
  ## Quick Start
81
109
 
82
110
  ```bash
@@ -86,6 +114,22 @@ bun install
86
114
  bun run dev
87
115
  ```
88
116
 
117
+ Worker-first server path:
118
+
119
+ ```bash
120
+ bunx gorsee create my-service --template worker-service
121
+ cd my-service
122
+ bun install
123
+ bun run worker
124
+ ```
125
+
126
+ Canonical worker CLI path for server-mode apps:
127
+
128
+ ```bash
129
+ gorsee worker
130
+ gorsee worker --entry workers/custom.ts
131
+ ```
132
+
89
133
  Alternative bootstrap paths:
90
134
 
91
135
  ```bash
@@ -238,21 +282,25 @@ const userRoute = createTypedRoute("/users/[id]")
238
282
 
239
283
  ## Starter Templates
240
284
 
241
- `gorsee create` ships with one generic baseline and four first-party product starters:
285
+ `gorsee create` ships with canonical starters for each official mode:
242
286
 
287
+ - `frontend` for browser-first prerendered apps
243
288
  - `basic` for the minimal canonical scaffold
244
289
  - `secure-saas` for authenticated SaaS apps with protected route groups
245
290
  - `content-site` for public content and marketing sites
246
291
  - `agent-aware-ops` for internal tools with AI-first workflows enabled
247
292
  - `workspace-monorepo` for workspace layouts with one app package and one shared package
293
+ - `server-api` for API-first and service-oriented server apps
248
294
 
249
295
  Examples:
250
296
 
251
297
  ```bash
298
+ bunx gorsee create my-frontend --template frontend
252
299
  bunx gorsee create my-saas --template secure-saas
253
300
  bunx gorsee create my-site --template content-site
254
301
  bunx gorsee create my-ops --template agent-aware-ops
255
302
  bunx gorsee create my-workspace --template workspace-monorepo
303
+ bunx gorsee create my-service --template server-api
256
304
  npx create-gorsee my-app
257
305
  npm create gorsee@latest my-app
258
306
  ```
@@ -17,6 +17,7 @@ export interface AIContextBundle {
17
17
  diagnostics: {
18
18
  updatedAt?: string;
19
19
  sessionId?: string;
20
+ app?: AIContextPacket["app"];
20
21
  latest?: Partial<AIDiagnostic>;
21
22
  } | null;
22
23
  recentEvents: AIEvent[];
@@ -1,3 +1,4 @@
1
+ import { type AppMode } from "../runtime/app-config.js";
1
2
  export interface AIFrameworkDocRef {
2
3
  path: string;
3
4
  purpose: string;
@@ -8,6 +9,7 @@ export interface AIFrameworkPacket {
8
9
  generatedAt: string;
9
10
  cwd: string;
10
11
  projectName: string;
12
+ appMode: AppMode;
11
13
  product: {
12
14
  name: "Gorsee";
13
15
  version?: string;
@@ -1,12 +1,14 @@
1
1
  import { access, readFile } from "node:fs/promises";
2
2
  import { basename, join, relative } from "node:path";
3
3
  import { generateFrameworkMD } from "../cli/framework-md.js";
4
+ import { loadAppConfig, resolveAppMode } from "../runtime/app-config.js";
4
5
  const LOCAL_DOCS = [
5
6
  { path: "AGENTS.md", purpose: "\u041F\u0440\u043E\u0435\u043A\u0442\u043D\u044B\u0439 operating contract \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u043E\u0432" },
6
7
  { path: "FRAMEWORK.md", purpose: "AI-friendly reference \u0434\u043B\u044F \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F" },
7
8
  { path: "README.md", purpose: "\u0411\u044B\u0441\u0442\u0440\u044B\u0439 \u0432\u0445\u043E\u0434 \u0438 \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u0430\u044F \u043F\u043E\u0432\u0435\u0440\u0445\u043D\u043E\u0441\u0442\u044C" }
8
9
  ], CANONICAL_DOCS = [
9
10
  { path: "docs/FRAMEWORK_DOCTRINE.md", purpose: "\u0410\u0440\u0445\u0438\u0442\u0435\u043A\u0442\u0443\u0440\u043D\u0430\u044F \u0434\u043E\u043A\u0442\u0440\u0438\u043D\u0430 \u0438 \u0438\u043D\u0432\u0430\u0440\u0438\u0430\u043D\u0442\u044B" },
11
+ { path: "docs/APPLICATION_MODES.md", purpose: "\u041A\u0430\u043D\u043E\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0440\u0435\u0436\u0438\u043C\u044B frontend/fullstack/server \u0438 \u0433\u0440\u0430\u043D\u0438\u0446\u044B \u043C\u0435\u0436\u0434\u0443 \u043D\u0438\u043C\u0438" },
10
12
  { path: "docs/API_STABILITY.md", purpose: "\u0421\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u044B\u0435 \u043F\u0443\u0431\u043B\u0438\u0447\u043D\u044B\u0435 entrypoints \u0438 migration semantics" },
11
13
  { path: "docs/SECURITY_MODEL.md", purpose: "Runtime/security guarantees \u0438 fail-closed \u043C\u043E\u0434\u0435\u043B\u044C" },
12
14
  { path: "docs/AI_WORKFLOWS.md", purpose: "\u041A\u0430\u043D\u043E\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0439 human+agent workflow" },
@@ -68,13 +70,14 @@ function renderDocLines(docs) {
68
70
  return docs.map((doc) => `- \`${doc.path}\` -- ${doc.purpose}`);
69
71
  }
70
72
  export async function buildAIFrameworkPacket(cwd) {
71
- const projectName = await resolveProjectName(cwd), localDocs = await collectDocRefs(cwd, LOCAL_DOCS), canonicalDocs = await collectDocRefs(cwd, CANONICAL_DOCS), frameworkReferencePath = await pathExists(join(cwd, "FRAMEWORK.md")) ? relative(cwd, join(cwd, "FRAMEWORK.md")) || "FRAMEWORK.md" : void 0, frameworkReferenceMarkdown = frameworkReferencePath ? await readFile(join(cwd, frameworkReferencePath), "utf-8") : generateFrameworkMD(projectName);
73
+ const projectName = await resolveProjectName(cwd), appMode = resolveAppMode(await loadAppConfig(cwd)), localDocs = await collectDocRefs(cwd, LOCAL_DOCS), canonicalDocs = await collectDocRefs(cwd, CANONICAL_DOCS), frameworkReferencePath = await pathExists(join(cwd, "FRAMEWORK.md")) ? relative(cwd, join(cwd, "FRAMEWORK.md")) || "FRAMEWORK.md" : void 0, frameworkReferenceMarkdown = frameworkReferencePath ? await readFile(join(cwd, frameworkReferencePath), "utf-8") : generateFrameworkMD(projectName);
72
74
  return {
73
75
  kind: "gorsee.framework",
74
76
  schemaVersion: 1,
75
77
  generatedAt: new Date().toISOString(),
76
78
  cwd,
77
79
  projectName,
80
+ appMode,
78
81
  product: {
79
82
  name: "Gorsee",
80
83
  version: await resolveFrameworkVersion(),
@@ -108,6 +111,7 @@ export async function buildAIFrameworkPacket(cwd) {
108
111
  recommendedStart: [
109
112
  "Read AGENTS.md first when it exists.",
110
113
  "Read FRAMEWORK.md for the current app shape and syntax.",
114
+ `Respect the current app.mode contract: ${appMode}.`,
111
115
  "Use gorsee/client for browser-safe code and gorsee/server for runtime/server boundaries.",
112
116
  "Prefer scoped stable subpaths when auth, db, security, forms, routes, ai, or log is the primary concern.",
113
117
  "Use gorsee ai export --bundle for incident/debug context, not for framework cold-start context."
@@ -122,6 +126,7 @@ export function renderAIFrameworkMarkdown(packet) {
122
126
  "",
123
127
  `Generated: ${packet.generatedAt}`,
124
128
  `Project: ${packet.projectName}`,
129
+ `App Mode: ${packet.appMode}`,
125
130
  packet.product.version ? `Framework Version: ${packet.product.version}` : void 0,
126
131
  "",
127
132
  "## Product",
@@ -8,6 +8,7 @@ export interface IDEProjectionPaths {
8
8
  export interface IDEProjection {
9
9
  schemaVersion: string;
10
10
  updatedAt: string;
11
+ app?: AIContextPacket["app"];
11
12
  diagnostics: Array<{
12
13
  code?: string;
13
14
  message: string;
@@ -16,6 +16,7 @@ export async function buildIDEProjection(storePaths, options = {}) {
16
16
  return {
17
17
  schemaVersion: GORSEE_AI_CONTEXT_SCHEMA_VERSION,
18
18
  updatedAt: new Date().toISOString(),
19
+ app: context.app,
19
20
  diagnostics: diagnosticsSnapshot?.latest ? [{
20
21
  code: diagnosticsSnapshot.latest.code,
21
22
  message: diagnosticsSnapshot.latest.message ?? "",
@@ -44,11 +45,13 @@ export async function writeIDEProjection(projectionPaths, projection) {
44
45
  await writeFile(projectionPaths.diagnosticsPath, JSON.stringify({
45
46
  schemaVersion: projection.schemaVersion,
46
47
  updatedAt: projection.updatedAt,
48
+ app: projection.app,
47
49
  diagnostics: projection.diagnostics
48
50
  }, null, 2), "utf-8");
49
51
  await writeFile(projectionPaths.eventsPath, JSON.stringify({
50
52
  schemaVersion: projection.schemaVersion,
51
53
  updatedAt: projection.updatedAt,
54
+ app: projection.app,
52
55
  events: projection.recentEvents,
53
56
  artifactRegressions: projection.artifactRegressions
54
57
  }, null, 2), "utf-8");
@@ -4,7 +4,7 @@ export { writeArtifactFailurePack, writeArtifactLifecycleEvent, writeArtifactSuc
4
4
  export { createAIBridgeHandler, createAIBridgeServer, type AIBridgeHandler, type AIBridgeServer, type AIBridgeServerOptions, type AIBridgeSnapshot, } from "./bridge.js";
5
5
  export { buildAIHealthReport, readAIDiagnosticsSnapshot, readReactiveTraceArtifact, readAIEvents, resolveAIStorePaths, type AIHealthReport, type AIStorePaths, } from "./store.js";
6
6
  export { createAIMCPServer, createLineReader, type AIMCPServerOptions, } from "./mcp.js";
7
- export { createAIContextPacket, renderAIContextMarkdown, type AIContextPacket, } from "./summary.js";
7
+ export { createAIContextPacket, createAIDeploySummary, createAIIncidentBrief, createAIIncidentSnapshot, createAIReleaseBrief, renderAIDeploySummaryMarkdown, renderAIIncidentBriefMarkdown, renderAIIncidentSnapshotMarkdown, renderAIContextMarkdown, renderAIReleaseBriefMarkdown, type AIContextPacket, type AIDeploySummary, type AIIncidentBrief, type AIIncidentSnapshot, type AIReleaseBrief, } from "./summary.js";
8
8
  export { buildAIContextBundle, renderAIContextBundleMarkdown, type AIContextBundle, type AIContextSnippet, } from "./bundle.js";
9
9
  export { buildAIFrameworkPacket, renderAIFrameworkMarkdown, type AIFrameworkDocRef, type AIFrameworkPacket, } from "./framework-context.js";
10
10
  export { buildIDEProjection, resolveIDEProjectionPaths, writeIDEProjection, type IDEProjection, type IDEProjectionPaths, } from "./ide.js";
@@ -12,6 +12,12 @@ export { createIDEProjectionWatcher, type IDEProjectionWatcher, type IDEProjecti
12
12
  export { resolveAISessionPackConfig, resolveAISessionPackPaths, shouldGenerateAISessionPack, writeAISessionPack, type AISessionPackConfig, type AISessionPackPaths, } from "./session-pack.js";
13
13
  export type AIEventSeverity = "debug" | "info" | "warn" | "error";
14
14
  export type AIEventSource = "runtime" | "build" | "cli" | "check" | "deploy" | "dev" | "log";
15
+ export type AIAppMode = "frontend" | "fullstack" | "server";
16
+ export type AIRuntimeTopology = "single-instance" | "multi-instance";
17
+ export interface AIAppContext {
18
+ mode: AIAppMode;
19
+ runtimeTopology: AIRuntimeTopology;
20
+ }
15
21
  export interface AITraceContext {
16
22
  requestId?: string;
17
23
  traceId: string;
@@ -28,6 +34,7 @@ export interface AIDiagnostic {
28
34
  route?: string;
29
35
  fix?: string;
30
36
  ts: string;
37
+ app?: AIAppContext;
31
38
  }
32
39
  export interface AIEvent {
33
40
  id: string;
@@ -48,6 +55,7 @@ export interface AIEvent {
48
55
  line?: number;
49
56
  tags?: string[];
50
57
  data?: Record<string, unknown>;
58
+ app?: AIAppContext;
51
59
  }
52
60
  export interface AIBridgeConfig {
53
61
  url: string;
@@ -63,6 +71,7 @@ export interface AIObservabilityConfig {
63
71
  sessionId?: string;
64
72
  bridge?: AIBridgeConfig;
65
73
  sessionPack?: AISessionPackConfig;
74
+ app?: AIAppContext;
66
75
  }
67
76
  export declare function configureAIObservability(config?: AIObservabilityConfig): void;
68
77
  export declare function getAIObservabilityConfig(): AIObservabilityConfig;
@@ -31,7 +31,15 @@ export {
31
31
  } from "./mcp.js";
32
32
  export {
33
33
  createAIContextPacket,
34
- renderAIContextMarkdown
34
+ createAIDeploySummary,
35
+ createAIIncidentBrief,
36
+ createAIIncidentSnapshot,
37
+ createAIReleaseBrief,
38
+ renderAIDeploySummaryMarkdown,
39
+ renderAIIncidentBriefMarkdown,
40
+ renderAIIncidentSnapshotMarkdown,
41
+ renderAIContextMarkdown,
42
+ renderAIReleaseBriefMarkdown
35
43
  } from "./summary.js";
36
44
  export {
37
45
  buildAIContextBundle,
@@ -88,6 +96,7 @@ export async function emitAIEvent(input) {
88
96
  id: input.id ?? crypto.randomUUID(),
89
97
  ts: input.ts ?? new Date().toISOString(),
90
98
  ...input,
99
+ app: input.app ?? currentConfig.app,
91
100
  data: redactValue(input.data, currentConfig.redactKeys ?? DEFAULT_REDACT_KEYS)
92
101
  }, writes = [];
93
102
  if (currentConfig.jsonlPath)
@@ -194,6 +203,7 @@ async function writeDiagnosticsSnapshot(path, event) {
194
203
  const snapshot = {
195
204
  updatedAt: event.ts,
196
205
  sessionId: currentConfig.sessionId,
206
+ app: event.app,
197
207
  latest: {
198
208
  code: event.code,
199
209
  message: event.message,
@@ -204,7 +214,8 @@ async function writeDiagnosticsSnapshot(path, event) {
204
214
  route: event.route,
205
215
  requestId: event.requestId,
206
216
  traceId: event.traceId,
207
- spanId: event.spanId
217
+ spanId: event.spanId,
218
+ app: event.app
208
219
  }
209
220
  };
210
221
  await mkdir(dirname(path), { recursive: !0 });
@@ -131,6 +131,10 @@ export function createLineReader(input) {
131
131
  }
132
132
  function renderDoctorText(report) {
133
133
  const lines = [
134
+ ...report.app ? [
135
+ `App mode: ${report.app.mode}`,
136
+ `Runtime topology: ${report.app.runtimeTopology}`
137
+ ] : [],
134
138
  `Events: ${report.events.total}`,
135
139
  `Diagnostics: ${report.diagnostics.total}`,
136
140
  `Errors: ${report.diagnostics.errors}`,
@@ -13,6 +13,14 @@ export interface AISessionPackPaths {
13
13
  outDir: string;
14
14
  latestJsonPath: string;
15
15
  latestMarkdownPath: string;
16
+ latestReleaseBriefJsonPath: string;
17
+ latestReleaseBriefMarkdownPath: string;
18
+ latestIncidentBriefJsonPath: string;
19
+ latestIncidentBriefMarkdownPath: string;
20
+ latestDeploySummaryJsonPath: string;
21
+ latestDeploySummaryMarkdownPath: string;
22
+ latestIncidentSnapshotJsonPath: string;
23
+ latestIncidentSnapshotMarkdownPath: string;
16
24
  historyDir: string;
17
25
  }
18
26
  export declare function resolveAISessionPackConfig(cwd: string, config?: AISessionPackConfig): AISessionPackConfig | undefined;
@@ -2,6 +2,16 @@ import { mkdir, writeFile } from "node:fs/promises";
2
2
  import { isAbsolute, join } from "node:path";
3
3
  import { buildAIContextBundle, renderAIContextBundleMarkdown } from "./bundle.js";
4
4
  import { GORSEE_AI_CONTEXT_SCHEMA_VERSION } from "./contracts.js";
5
+ import {
6
+ createAIDeploySummary,
7
+ createAIIncidentBrief,
8
+ createAIIncidentSnapshot,
9
+ createAIReleaseBrief,
10
+ renderAIDeploySummaryMarkdown,
11
+ renderAIIncidentBriefMarkdown,
12
+ renderAIIncidentSnapshotMarkdown,
13
+ renderAIReleaseBriefMarkdown
14
+ } from "./summary.js";
5
15
  export function resolveAISessionPackConfig(cwd, config) {
6
16
  if (!config?.enabled)
7
17
  return config;
@@ -21,6 +31,14 @@ export function resolveAISessionPackPaths(cwd, config) {
21
31
  outDir,
22
32
  latestJsonPath: join(outDir, "latest.json"),
23
33
  latestMarkdownPath: join(outDir, "latest.md"),
34
+ latestReleaseBriefJsonPath: join(outDir, "release-brief.json"),
35
+ latestReleaseBriefMarkdownPath: join(outDir, "release-brief.md"),
36
+ latestIncidentBriefJsonPath: join(outDir, "incident-brief.json"),
37
+ latestIncidentBriefMarkdownPath: join(outDir, "incident-brief.md"),
38
+ latestDeploySummaryJsonPath: join(outDir, "deploy-summary.json"),
39
+ latestDeploySummaryMarkdownPath: join(outDir, "deploy-summary.md"),
40
+ latestIncidentSnapshotJsonPath: join(outDir, "incident-snapshot.json"),
41
+ latestIncidentSnapshotMarkdownPath: join(outDir, "incident-snapshot.md"),
24
42
  historyDir: join(outDir, "history")
25
43
  };
26
44
  }
@@ -30,18 +48,50 @@ export async function writeAISessionPack(cwd, storePaths, config) {
30
48
  maxSnippets: resolved.maxSnippets
31
49
  });
32
50
  await mkdir(paths.historyDir, { recursive: !0 });
33
- const stamp = bundle.generatedAt.replace(/[:.]/g, "-"), json = JSON.stringify(bundle, null, 2), markdown = [
51
+ const stamp = bundle.generatedAt.replace(/[:.]/g, "-"), deploySummary = createAIDeploySummary(bundle.packet), releaseBrief = createAIReleaseBrief(bundle.packet), incidentBrief = createAIIncidentBrief(bundle.packet), incidentSnapshot = createAIIncidentSnapshot(bundle.packet), json = JSON.stringify(bundle, null, 2), markdown = [
34
52
  `<!-- gorsee-ai-schema: ${GORSEE_AI_CONTEXT_SCHEMA_VERSION} -->`,
35
53
  renderAIContextBundleMarkdown(bundle)
36
54
  ].join(`
55
+ `), releaseBriefJson = JSON.stringify(releaseBrief, null, 2), deploySummaryJson = JSON.stringify(deploySummary, null, 2), deploySummaryMarkdown = [
56
+ `<!-- gorsee-ai-schema: ${GORSEE_AI_CONTEXT_SCHEMA_VERSION} -->`,
57
+ renderAIDeploySummaryMarkdown(deploySummary)
58
+ ].join(`
59
+ `), releaseBriefMarkdown = [
60
+ `<!-- gorsee-ai-schema: ${GORSEE_AI_CONTEXT_SCHEMA_VERSION} -->`,
61
+ renderAIReleaseBriefMarkdown(releaseBrief)
62
+ ].join(`
63
+ `), incidentBriefJson = JSON.stringify(incidentBrief, null, 2), incidentBriefMarkdown = [
64
+ `<!-- gorsee-ai-schema: ${GORSEE_AI_CONTEXT_SCHEMA_VERSION} -->`,
65
+ renderAIIncidentBriefMarkdown(incidentBrief)
66
+ ].join(`
67
+ `), incidentSnapshotJson = JSON.stringify(incidentSnapshot, null, 2), incidentSnapshotMarkdown = [
68
+ `<!-- gorsee-ai-schema: ${GORSEE_AI_CONTEXT_SCHEMA_VERSION} -->`,
69
+ renderAIIncidentSnapshotMarkdown(incidentSnapshot)
70
+ ].join(`
37
71
  `);
38
72
  if ((resolved.formats ?? ["json", "markdown"]).includes("json")) {
39
73
  await writeFile(paths.latestJsonPath, json, "utf-8");
40
74
  await writeFile(join(paths.historyDir, `${stamp}.json`), json, "utf-8");
75
+ await writeFile(paths.latestDeploySummaryJsonPath, deploySummaryJson, "utf-8");
76
+ await writeFile(join(paths.historyDir, `${stamp}.deploy-summary.json`), deploySummaryJson, "utf-8");
77
+ await writeFile(paths.latestReleaseBriefJsonPath, releaseBriefJson, "utf-8");
78
+ await writeFile(join(paths.historyDir, `${stamp}.release-brief.json`), releaseBriefJson, "utf-8");
79
+ await writeFile(paths.latestIncidentBriefJsonPath, incidentBriefJson, "utf-8");
80
+ await writeFile(join(paths.historyDir, `${stamp}.incident-brief.json`), incidentBriefJson, "utf-8");
81
+ await writeFile(paths.latestIncidentSnapshotJsonPath, incidentSnapshotJson, "utf-8");
82
+ await writeFile(join(paths.historyDir, `${stamp}.incident-snapshot.json`), incidentSnapshotJson, "utf-8");
41
83
  }
42
84
  if ((resolved.formats ?? ["json", "markdown"]).includes("markdown")) {
43
85
  await writeFile(paths.latestMarkdownPath, markdown, "utf-8");
44
86
  await writeFile(join(paths.historyDir, `${stamp}.md`), markdown, "utf-8");
87
+ await writeFile(paths.latestDeploySummaryMarkdownPath, deploySummaryMarkdown, "utf-8");
88
+ await writeFile(join(paths.historyDir, `${stamp}.deploy-summary.md`), deploySummaryMarkdown, "utf-8");
89
+ await writeFile(paths.latestReleaseBriefMarkdownPath, releaseBriefMarkdown, "utf-8");
90
+ await writeFile(join(paths.historyDir, `${stamp}.release-brief.md`), releaseBriefMarkdown, "utf-8");
91
+ await writeFile(paths.latestIncidentBriefMarkdownPath, incidentBriefMarkdown, "utf-8");
92
+ await writeFile(join(paths.historyDir, `${stamp}.incident-brief.md`), incidentBriefMarkdown, "utf-8");
93
+ await writeFile(paths.latestIncidentSnapshotMarkdownPath, incidentSnapshotMarkdown, "utf-8");
94
+ await writeFile(join(paths.historyDir, `${stamp}.incident-snapshot.md`), incidentSnapshotMarkdown, "utf-8");
45
95
  }
46
96
  return { bundle, paths, stamp };
47
97
  }
@@ -1,11 +1,33 @@
1
- import type { AIDiagnostic, AIEvent } from "./index.js";
1
+ import type { AIAppContext, AIDiagnostic, AIEvent } from "./index.js";
2
2
  import type { ReactiveTraceArtifact } from "../reactive/diagnostics.js";
3
+ import type { ReleaseArtifact } from "../server/manifest.js";
3
4
  export interface AIStorePaths {
4
5
  eventsPath: string;
5
6
  diagnosticsPath: string;
6
7
  reactiveTracePath: string;
8
+ releaseArtifactPath?: string;
7
9
  }
8
10
  export interface AIHealthReport {
11
+ app?: AIAppContext;
12
+ release?: {
13
+ appMode: ReleaseArtifact["appMode"];
14
+ runtimeKind: ReleaseArtifact["runtime"]["kind"];
15
+ processEntrypoints: string[];
16
+ handlerEntrypoints: string[];
17
+ workerEntrypoint?: string;
18
+ summary: ReleaseArtifact["summary"];
19
+ generatedAt: string;
20
+ };
21
+ readiness: {
22
+ deploy: {
23
+ status: "ready" | "caution" | "blocked";
24
+ reasons: string[];
25
+ };
26
+ scaling: {
27
+ status: "ready" | "caution" | "blocked" | "not-applicable";
28
+ reasons: string[];
29
+ };
30
+ };
9
31
  events: {
10
32
  total: number;
11
33
  bySeverity: Record<string, number>;
@@ -59,9 +81,11 @@ export declare function readAIEvents(path: string, options?: {
59
81
  export declare function readAIDiagnosticsSnapshot(path: string): Promise<{
60
82
  updatedAt?: string;
61
83
  sessionId?: string;
84
+ app?: AIAppContext;
62
85
  latest?: Partial<AIDiagnostic>;
63
86
  } | null>;
64
87
  export declare function readReactiveTraceArtifact(path: string): Promise<ReactiveTraceArtifact | null>;
88
+ export declare function readReleaseArtifact(path: string): Promise<ReleaseArtifact | null>;
65
89
  export declare function buildAIHealthReport(paths: AIStorePaths, options?: {
66
90
  limit?: number;
67
91
  }): Promise<AIHealthReport>;
@@ -5,7 +5,8 @@ export function resolveAIStorePaths(cwd) {
5
5
  return {
6
6
  eventsPath: join(cwd, ".gorsee", "ai-events.jsonl"),
7
7
  diagnosticsPath: join(cwd, ".gorsee", "ai-diagnostics.json"),
8
- reactiveTracePath: join(cwd, ".gorsee", "reactive-trace.json")
8
+ reactiveTracePath: join(cwd, ".gorsee", "reactive-trace.json"),
9
+ releaseArtifactPath: join(cwd, "dist", "release.json")
9
10
  };
10
11
  }
11
12
  export async function readAIEvents(path, options = {}) {
@@ -32,8 +33,14 @@ export async function readReactiveTraceArtifact(path) {
32
33
  return null;
33
34
  return safeJSONParse(content);
34
35
  }
36
+ export async function readReleaseArtifact(path) {
37
+ const content = await safeReadFile(path);
38
+ if (!content)
39
+ return null;
40
+ return safeJSONParse(content);
41
+ }
35
42
  export async function buildAIHealthReport(paths, options = {}) {
36
- const events = await readAIEvents(paths.eventsPath, options), snapshot = await readAIDiagnosticsSnapshot(paths.diagnosticsPath), bySeverity = {}, byKind = {};
43
+ const events = await readAIEvents(paths.eventsPath, options), snapshot = await readAIDiagnosticsSnapshot(paths.diagnosticsPath), releaseArtifact = paths.releaseArtifactPath ? await readReleaseArtifact(paths.releaseArtifactPath) : null, bySeverity = {}, byKind = {};
37
44
  let errors = 0, warnings = 0;
38
45
  for (const event of events) {
39
46
  bySeverity[event.severity] = (bySeverity[event.severity] ?? 0) + 1;
@@ -51,8 +58,33 @@ export async function buildAIHealthReport(paths, options = {}) {
51
58
  file: event.file,
52
59
  line: event.line,
53
60
  code: event.code
54
- })), incidentClusters = buildIncidentClusters(events), artifactRegressions = buildArtifactRegressions(events);
61
+ })), incidentClusters = buildIncidentClusters(events), artifactRegressions = buildArtifactRegressions(events), appContext = snapshot?.app ?? [...events].reverse().find((event) => event.app)?.app, releaseContext = releaseArtifact ? {
62
+ appMode: releaseArtifact.appMode,
63
+ runtimeKind: releaseArtifact.runtime.kind,
64
+ processEntrypoints: releaseArtifact.runtime.processEntrypoints,
65
+ handlerEntrypoints: releaseArtifact.runtime.handlerEntrypoints,
66
+ workerEntrypoint: releaseArtifact.runtime.workerEntrypoint,
67
+ summary: releaseArtifact.summary,
68
+ generatedAt: releaseArtifact.generatedAt
69
+ } : void 0;
55
70
  return {
71
+ app: appContext,
72
+ release: releaseContext,
73
+ readiness: {
74
+ deploy: buildDeployReadiness({
75
+ app: appContext,
76
+ release: releaseContext,
77
+ diagnosticsErrors: errors,
78
+ diagnosticsWarnings: warnings,
79
+ incidents,
80
+ artifactRegressions
81
+ }),
82
+ scaling: buildScalingReadiness({
83
+ app: appContext,
84
+ release: releaseContext,
85
+ events
86
+ })
87
+ },
56
88
  events: {
57
89
  total: events.length,
58
90
  bySeverity,
@@ -70,6 +102,60 @@ export async function buildAIHealthReport(paths, options = {}) {
70
102
  artifactRegressions
71
103
  };
72
104
  }
105
+ function buildDeployReadiness(input) {
106
+ const reasons = [];
107
+ if (!input.release)
108
+ reasons.push("dist/release.json is missing; release-level runtime context is not available.");
109
+ if (input.app && input.release && input.app.mode !== input.release.appMode)
110
+ reasons.push(`app.mode (${input.app.mode}) does not match release artifact mode (${input.release.appMode}).`);
111
+ if (input.diagnosticsErrors > 0)
112
+ reasons.push(`AI diagnostics currently report ${input.diagnosticsErrors} error event(s).`);
113
+ if (input.artifactRegressions.some((entry) => entry.errors > 0))
114
+ reasons.push("artifact regressions contain error-level failures.");
115
+ if (reasons.length > 0)
116
+ return { status: "blocked", reasons };
117
+ if (input.diagnosticsWarnings > 0)
118
+ reasons.push(`AI diagnostics currently report ${input.diagnosticsWarnings} warning event(s).`);
119
+ if (input.incidents.length > 0)
120
+ reasons.push(`recent incident history is non-empty (${input.incidents.length}).`);
121
+ if (input.artifactRegressions.some((entry) => entry.warnings > 0))
122
+ reasons.push("artifact regressions contain warning-level drift.");
123
+ if (reasons.length > 0)
124
+ return { status: "caution", reasons };
125
+ reasons.push(input.release ? `release artifact is aligned for ${input.release.appMode} (${input.release.runtimeKind}).` : "no blocking deploy signals were found.");
126
+ return { status: "ready", reasons };
127
+ }
128
+ function buildScalingReadiness(input) {
129
+ const reasons = [], eventCodes = new Set(input.events.map((event) => event.code).filter((code) => Boolean(code)));
130
+ if (input.release?.appMode === "frontend")
131
+ return {
132
+ status: "not-applicable",
133
+ reasons: ["frontend-static release artifacts do not own a multi-instance server runtime."]
134
+ };
135
+ if (input.app?.runtimeTopology !== "multi-instance")
136
+ return {
137
+ status: "not-applicable",
138
+ reasons: ["runtime.topology is not declared as multi-instance."]
139
+ };
140
+ if (!input.release)
141
+ reasons.push("dist/release.json is missing; scaling cannot be assessed against the built runtime.");
142
+ if (input.release && input.release.runtimeKind === "frontend-static")
143
+ reasons.push("release runtime kind is frontend-static, which cannot satisfy multi-instance server scaling.");
144
+ for (const code of ["W917", "W918", "W919", "W920"])
145
+ if (eventCodes.has(code))
146
+ reasons.push(`check/runtime events still report ${code}, which blocks distributed scaling readiness.`);
147
+ if (reasons.length > 0)
148
+ return { status: "blocked", reasons };
149
+ if (eventCodes.has("W921"))
150
+ return {
151
+ status: "caution",
152
+ reasons: ["multi-instance runtime is configured, but AI observability does not yet forward to a bridge/aggregator (W921)."]
153
+ };
154
+ return {
155
+ status: "ready",
156
+ reasons: ["multi-instance topology is declared and no distributed-state blocker signals are present."]
157
+ };
158
+ }
73
159
  function buildIncidentClusters(events) {
74
160
  const clusters = new Map;
75
161
  for (const event of events) {