@beignet/cli 0.0.49 → 0.0.51
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/CHANGELOG.md +32 -0
- package/README.md +54 -5
- package/dist/analysis/workspace.d.ts +1 -0
- package/dist/analysis/workspace.d.ts.map +1 -1
- package/dist/analysis/workspace.js +20 -10
- package/dist/analysis/workspace.js.map +1 -1
- package/dist/app-map-changes.d.ts +103 -0
- package/dist/app-map-changes.d.ts.map +1 -0
- package/dist/app-map-changes.js +949 -0
- package/dist/app-map-changes.js.map +1 -0
- package/dist/git-changes.d.ts +30 -0
- package/dist/git-changes.d.ts.map +1 -0
- package/dist/git-changes.js +367 -0
- package/dist/git-changes.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -4
- package/dist/index.js.map +1 -1
- package/dist/inspect.js +413 -43
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +3 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -0
- package/dist/lib.js.map +1 -1
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +31 -27
- package/dist/make/shared.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +0 -2
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +46 -6
- package/dist/mcp.js.map +1 -1
- package/dist/operational-process.d.ts +1 -0
- package/dist/operational-process.d.ts.map +1 -1
- package/dist/operational-process.js.map +1 -1
- package/dist/operational-runner.js +1 -0
- package/dist/operational-runner.js.map +1 -1
- package/dist/outbox.d.ts +1 -0
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +58 -12
- package/dist/outbox.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +28 -3
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +4 -1
- package/dist/templates/base.js.map +1 -1
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +30 -5
- package/src/analysis/workspace.ts +25 -9
- package/src/app-map-changes.ts +1462 -0
- package/src/git-changes.ts +511 -0
- package/src/index.ts +84 -4
- package/src/inspect.ts +586 -51
- package/src/lib.ts +21 -0
- package/src/make/shared.ts +31 -27
- package/src/make.ts +0 -2
- package/src/mcp.ts +65 -12
- package/src/operational-process.ts +1 -0
- package/src/operational-runner.ts +1 -0
- package/src/outbox.ts +63 -12
- package/src/templates/agents.ts +28 -3
- package/src/templates/base.ts +4 -1
package/src/lib.ts
CHANGED
|
@@ -25,6 +25,23 @@ export {
|
|
|
25
25
|
mapApp,
|
|
26
26
|
projectAppMap,
|
|
27
27
|
} from "./app-map.js";
|
|
28
|
+
export type {
|
|
29
|
+
AppChangedFile,
|
|
30
|
+
AppChangeFileScope,
|
|
31
|
+
AppChangeGap,
|
|
32
|
+
AppChangeImpact,
|
|
33
|
+
AppChangeImpactedNode,
|
|
34
|
+
AppChangeImpactResult,
|
|
35
|
+
AppChangeReason,
|
|
36
|
+
AppChangeRelationship,
|
|
37
|
+
AppMapChangesOptions,
|
|
38
|
+
BoundedAppChangeGapSection,
|
|
39
|
+
BoundedAppChangeSection,
|
|
40
|
+
} from "./app-map-changes.js";
|
|
41
|
+
export {
|
|
42
|
+
formatAppChangeImpact,
|
|
43
|
+
mapAppChanges,
|
|
44
|
+
} from "./app-map-changes.js";
|
|
28
45
|
export type { CreateOptions } from "./create.js";
|
|
29
46
|
export { createProject } from "./create.js";
|
|
30
47
|
export type {
|
|
@@ -71,6 +88,10 @@ export {
|
|
|
71
88
|
explainTargetKinds,
|
|
72
89
|
formatExplain,
|
|
73
90
|
} from "./explain.js";
|
|
91
|
+
export type {
|
|
92
|
+
GitChangeComparison,
|
|
93
|
+
GitChangedFileStatus,
|
|
94
|
+
} from "./git-changes.js";
|
|
74
95
|
export { main } from "./index.js";
|
|
75
96
|
export type {
|
|
76
97
|
ApplyDoctorFixPlanOptions,
|
package/src/make/shared.ts
CHANGED
|
@@ -453,36 +453,40 @@ export function wireListenersProviderSource(
|
|
|
453
453
|
>()({
|
|
454
454
|
\tname: "app-listeners",
|
|
455
455
|
\tsetup({ ports, createServiceContext }) {
|
|
456
|
-
\t\
|
|
457
|
-
|
|
458
|
-
\t\
|
|
459
|
-
\t\t\
|
|
460
|
-
\t\t\t\
|
|
461
|
-
\t\t\
|
|
462
|
-
\t\t\t\
|
|
463
|
-
\t\t\t\t\
|
|
464
|
-
\t\t\t\t\
|
|
465
|
-
\t\t\t\t
|
|
466
|
-
\t\t\t\
|
|
467
|
-
\t\t\t\t\
|
|
468
|
-
\t\t\t\t\
|
|
469
|
-
\t\t\t\t\
|
|
470
|
-
\t\t\t\t\t\
|
|
471
|
-
\t\t\t\t\t\
|
|
472
|
-
\t\t\t\t\t\
|
|
473
|
-
\t\t\t\t\t\
|
|
474
|
-
\t\t\t\t\t\t\t
|
|
475
|
-
\t\t\t\t\t\t\t"beignet.listener"
|
|
476
|
-
\t\t\t\t\t\t
|
|
477
|
-
\t\t\t\t\t\
|
|
456
|
+
\t\tlet registration: ReturnType<typeof registerListeners> | undefined;
|
|
457
|
+
|
|
458
|
+
\t\treturn {
|
|
459
|
+
\t\t\tasync start() {
|
|
460
|
+
\t\t\t\tregistration = registerListeners(ports.eventBus, listeners, {
|
|
461
|
+
\t\t\t\t\tctx: () =>
|
|
462
|
+
\t\t\t\t\t\tcreateServiceContext({
|
|
463
|
+
\t\t\t\t\t\t\tactor: createServiceActor("beignet-listener"),
|
|
464
|
+
\t\t\t\t\t\t}),
|
|
465
|
+
\t\t\t\t\tonError(error, listener) {
|
|
466
|
+
\t\t\t\t\t\tports.logger.error("Event listener failed", {
|
|
467
|
+
\t\t\t\t\t\t\terror,
|
|
468
|
+
\t\t\t\t\t\t\tlistenerName: listener.name,
|
|
469
|
+
\t\t\t\t\t\t});
|
|
470
|
+
\t\t\t\t\t\tvoid tryReportException({
|
|
471
|
+
\t\t\t\t\t\t\treporter: ports.errorReporter,
|
|
472
|
+
\t\t\t\t\t\t\terror,
|
|
473
|
+
\t\t\t\t\t\t\treportOptions: {
|
|
474
|
+
\t\t\t\t\t\t\t\tlevel: "error",
|
|
475
|
+
\t\t\t\t\t\t\t\tmechanism: "beignet.listener",
|
|
476
|
+
\t\t\t\t\t\t\t\thandled: false,
|
|
477
|
+
\t\t\t\t\t\t\t\ttags: {
|
|
478
|
+
\t\t\t\t\t\t\t\t\t"beignet.kind": "listener",
|
|
479
|
+
\t\t\t\t\t\t\t\t\t"beignet.listener": listener.name,
|
|
480
|
+
\t\t\t\t\t\t\t\t},
|
|
481
|
+
\t\t\t\t\t\t\t\tcontexts: { listener: { name: listener.name } },
|
|
482
|
+
\t\t\t\t\t\t\t},
|
|
483
|
+
\t\t\t\t\t\t});
|
|
478
484
|
\t\t\t\t\t},
|
|
479
485
|
\t\t\t\t});
|
|
486
|
+
\t\t\t\tawait registration.ready;
|
|
480
487
|
\t\t\t},
|
|
481
|
-
\t\t
|
|
482
|
-
|
|
483
|
-
\t\treturn {
|
|
484
|
-
\t\t\tstop() {
|
|
485
|
-
\t\t\t\tunregister();
|
|
488
|
+
\t\t\tasync stop() {
|
|
489
|
+
\t\t\t\tawait registration?.unsubscribe();
|
|
486
490
|
\t\t\t},
|
|
487
491
|
\t\t};
|
|
488
492
|
\t},
|
package/src/make.ts
CHANGED
|
@@ -2225,7 +2225,6 @@ async function updateOutboxDrainProvider(
|
|
|
2225
2225
|
\t\t\tdefer: after,
|
|
2226
2226
|
\t\t\tcreateContext: () => createServiceContext(undefined),
|
|
2227
2227
|
\t\t\tregistry: async () => (await import("${outboxModule}")).outboxRegistry,
|
|
2228
|
-
\t\t\tbatchSize: 100,
|
|
2229
2228
|
\t\t});
|
|
2230
2229
|
|
|
2231
2230
|
\t\treturn {
|
|
@@ -5921,7 +5920,6 @@ export const { GET, POST } = createOutboxDrainRoute({
|
|
|
5921
5920
|
\tserver: getServer,
|
|
5922
5921
|
\tregistry: outboxRegistry,
|
|
5923
5922
|
\tsecret: env.CRON_SECRET,
|
|
5924
|
-
\tbatchSize: 100,
|
|
5925
5923
|
});
|
|
5926
5924
|
`;
|
|
5927
5925
|
}
|
package/src/mcp.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/server";
|
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
5
5
|
import { z } from "zod/v4";
|
|
6
6
|
import { mapApp, projectAppMap } from "./app-map.js";
|
|
7
|
+
import { mapAppChanges } from "./app-map-changes.js";
|
|
7
8
|
import { appMapNodeKinds } from "./app-map-schema.js";
|
|
8
9
|
import { checkApp } from "./check.js";
|
|
9
10
|
import {
|
|
@@ -284,6 +285,7 @@ const outboxRunInputSchema = z.discriminatedUnion("operation", [
|
|
|
284
285
|
z.object({
|
|
285
286
|
operation: z.literal("drain"),
|
|
286
287
|
batchSize: z.number().int().positive().optional(),
|
|
288
|
+
concurrency: z.number().int().positive().optional(),
|
|
287
289
|
module: operationalModuleSchema,
|
|
288
290
|
timeoutMs: operationalTimeoutSchema,
|
|
289
291
|
}),
|
|
@@ -348,17 +350,36 @@ function jsonResult(value: unknown): McpToolResult {
|
|
|
348
350
|
};
|
|
349
351
|
}
|
|
350
352
|
|
|
351
|
-
function operationalJsonResult(
|
|
353
|
+
function operationalJsonResult(
|
|
354
|
+
value: unknown,
|
|
355
|
+
options: { isError?: boolean } = {},
|
|
356
|
+
): McpToolResult {
|
|
352
357
|
const formatted = JSON.stringify(value, null, 2);
|
|
353
358
|
const text =
|
|
354
359
|
Buffer.byteLength(formatted) <= defaultOperationalResultMaxBytes
|
|
355
360
|
? formatted
|
|
356
361
|
: JSON.stringify(value);
|
|
357
362
|
return {
|
|
363
|
+
...(options.isError ? { isError: true } : {}),
|
|
358
364
|
content: [{ type: "text", text }],
|
|
359
365
|
};
|
|
360
366
|
}
|
|
361
367
|
|
|
368
|
+
function isUncertainOutboxDrainReport(value: unknown): boolean {
|
|
369
|
+
if (typeof value !== "object" || value === null || !("result" in value)) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
const result = value.result;
|
|
373
|
+
if (typeof result !== "object" || result === null) return false;
|
|
374
|
+
const settlementFailed =
|
|
375
|
+
"settlementFailed" in result ? result.settlementFailed : undefined;
|
|
376
|
+
const leaseLost = "leaseLost" in result ? result.leaseLost : undefined;
|
|
377
|
+
return (
|
|
378
|
+
(typeof settlementFailed === "number" && settlementFailed > 0) ||
|
|
379
|
+
(typeof leaseLost === "number" && leaseLost > 0)
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
362
383
|
function errorResult(error: unknown): McpToolResult {
|
|
363
384
|
return {
|
|
364
385
|
isError: true,
|
|
@@ -579,8 +600,21 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
579
600
|
"app_map",
|
|
580
601
|
{
|
|
581
602
|
description:
|
|
582
|
-
"Return a deterministic graph of this Beignet app
|
|
603
|
+
"Return a deterministic graph of this Beignet app, or map the current Git change set to bounded, source-backed concepts with changed=true. Changed mode is report-only and exposes unresolved evidence without claiming verification.",
|
|
583
604
|
inputSchema: {
|
|
605
|
+
changed: z
|
|
606
|
+
.boolean()
|
|
607
|
+
.optional()
|
|
608
|
+
.describe(
|
|
609
|
+
"Map the current Git change set instead of returning the complete app graph.",
|
|
610
|
+
),
|
|
611
|
+
base: z
|
|
612
|
+
.string()
|
|
613
|
+
.min(1)
|
|
614
|
+
.optional()
|
|
615
|
+
.describe(
|
|
616
|
+
"Include committed changes since the merge base of this already-local Git ref. Requires changed=true; Beignet never fetches it.",
|
|
617
|
+
),
|
|
584
618
|
feature: z
|
|
585
619
|
.string()
|
|
586
620
|
.optional()
|
|
@@ -598,6 +632,22 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
598
632
|
},
|
|
599
633
|
async (input) =>
|
|
600
634
|
safeToolResult(async () => {
|
|
635
|
+
if (input.base !== undefined && !input.changed) {
|
|
636
|
+
throw new Error("app_map base requires changed=true.");
|
|
637
|
+
}
|
|
638
|
+
if (
|
|
639
|
+
input.changed &&
|
|
640
|
+
(input.feature !== undefined ||
|
|
641
|
+
input.kinds !== undefined ||
|
|
642
|
+
input.includeDiagnostics !== undefined)
|
|
643
|
+
) {
|
|
644
|
+
throw new Error(
|
|
645
|
+
"app_map changed=true cannot be combined with feature, kinds, or includeDiagnostics.",
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
if (input.changed) {
|
|
649
|
+
return jsonResult(await mapAppChanges({ cwd, base: input.base }));
|
|
650
|
+
}
|
|
601
651
|
const result = await mapApp({ cwd, strict: true });
|
|
602
652
|
return jsonResult(
|
|
603
653
|
projectAppMap(result, {
|
|
@@ -942,7 +992,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
942
992
|
"outbox_run",
|
|
943
993
|
{
|
|
944
994
|
description:
|
|
945
|
-
"Run a state-changing outbox drain, requeue, purge, or prune operation and return exactly the matching versioned beignet outbox <operation> --json report. Purge and prune support dryRun. Runs in an isolated process with bounded structured output, cancellation, and a timeout.",
|
|
995
|
+
"Run a state-changing outbox drain, requeue, purge, or prune operation and return exactly the matching versioned beignet outbox <operation> --json report. Drain supports bounded unordered concurrency and returns an error-bearing report when settlement or lease state is uncertain. Purge and prune support dryRun. Runs in an isolated process with bounded structured output, cancellation, and a timeout.",
|
|
946
996
|
inputSchema: outboxRunInputSchema,
|
|
947
997
|
annotations: {
|
|
948
998
|
readOnlyHint: false,
|
|
@@ -958,6 +1008,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
958
1008
|
? {
|
|
959
1009
|
command: "outbox:drain" as const,
|
|
960
1010
|
batchSize: input.batchSize,
|
|
1011
|
+
concurrency: input.concurrency,
|
|
961
1012
|
modulePath: input.module,
|
|
962
1013
|
}
|
|
963
1014
|
: input.operation === "requeue"
|
|
@@ -984,14 +1035,16 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
984
1035
|
dryRun: input.dryRun,
|
|
985
1036
|
modulePath: input.module,
|
|
986
1037
|
};
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1038
|
+
const result = await runIsolatedOperationalCommand({
|
|
1039
|
+
cwd,
|
|
1040
|
+
request,
|
|
1041
|
+
signal: ctx.mcpReq.signal,
|
|
1042
|
+
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
1043
|
+
});
|
|
1044
|
+
return operationalJsonResult(result, {
|
|
1045
|
+
isError:
|
|
1046
|
+
input.operation === "drain" && isUncertainOutboxDrainReport(result),
|
|
1047
|
+
});
|
|
995
1048
|
}),
|
|
996
1049
|
);
|
|
997
1050
|
|
|
@@ -1166,7 +1219,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
1166
1219
|
async function readAppGuidance(cwd: string): Promise<string> {
|
|
1167
1220
|
const guidancePath = await resolveAppGuidancePath(cwd);
|
|
1168
1221
|
if (!guidancePath) {
|
|
1169
|
-
return "# Beignet app guidance\n\nNo app-local AGENTS.md was found. Use
|
|
1222
|
+
return "# Beignet app guidance\n\nNo app-local AGENTS.md was found. Use app_map and explain before editing, prefer Beignet generators, use app_map with changed=true to inspect potential consumers after editing in Git, and then run check.\n";
|
|
1170
1223
|
}
|
|
1171
1224
|
|
|
1172
1225
|
let file: Awaited<ReturnType<typeof open>>;
|
package/src/outbox.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
OutboxMessageStatus,
|
|
11
11
|
OutboxRegistry,
|
|
12
12
|
} from "@beignet/core/outbox";
|
|
13
|
+
import type { TracingPort } from "@beignet/core/tracing";
|
|
13
14
|
import { createJiti } from "jiti";
|
|
14
15
|
import { loadBeignetConfig, normalizePath } from "./config.js";
|
|
15
16
|
import {
|
|
@@ -26,6 +27,7 @@ export type RunOutboxDrainOptions = {
|
|
|
26
27
|
cwd?: string;
|
|
27
28
|
modulePath?: string;
|
|
28
29
|
batchSize?: number;
|
|
30
|
+
concurrency?: number;
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -175,6 +177,7 @@ type OutboxDrainPorts = {
|
|
|
175
177
|
logger?: OutboxDrainLogger;
|
|
176
178
|
devtools?: OutboxDrainInstrumentation;
|
|
177
179
|
instrumentation?: OutboxDrainInstrumentation;
|
|
180
|
+
tracing?: TracingPort;
|
|
178
181
|
errorReporter?: ErrorReporterPort;
|
|
179
182
|
};
|
|
180
183
|
|
|
@@ -187,6 +190,7 @@ type OutboxDrainContext = {
|
|
|
187
190
|
type OutboxDrainContextArgs = {
|
|
188
191
|
registry: OutboxRegistry;
|
|
189
192
|
batchSize?: number;
|
|
193
|
+
concurrency?: number;
|
|
190
194
|
};
|
|
191
195
|
|
|
192
196
|
type OutboxAdminOperation = "list" | "show" | "requeue" | "purge" | "prune";
|
|
@@ -219,7 +223,11 @@ export async function runOutboxDrain(
|
|
|
219
223
|
const config = await loadBeignetConfig(cwd);
|
|
220
224
|
const modulePath = normalizePath(options.modulePath ?? config.paths.outbox);
|
|
221
225
|
const batchSize = options.batchSize;
|
|
226
|
+
const concurrency = options.concurrency;
|
|
222
227
|
if (batchSize !== undefined) assertPositiveInteger("batchSize", batchSize);
|
|
228
|
+
if (concurrency !== undefined) {
|
|
229
|
+
assertPositiveInteger("concurrency", concurrency);
|
|
230
|
+
}
|
|
223
231
|
|
|
224
232
|
const startedAt = performance.now();
|
|
225
233
|
const outboxModule = await loadOutboxModule(cwd, modulePath);
|
|
@@ -227,7 +235,7 @@ export async function runOutboxDrain(
|
|
|
227
235
|
outboxModule.outboxRegistry,
|
|
228
236
|
modulePath,
|
|
229
237
|
);
|
|
230
|
-
const contextArgs = { registry, batchSize };
|
|
238
|
+
const contextArgs = { registry, batchSize, concurrency };
|
|
231
239
|
const rawContext = outboxModule.createOutboxDrainContext
|
|
232
240
|
? await outboxModule.createOutboxDrainContext(contextArgs)
|
|
233
241
|
: undefined;
|
|
@@ -247,7 +255,11 @@ export async function runOutboxDrain(
|
|
|
247
255
|
eventBus: ctx.ports.eventBus,
|
|
248
256
|
jobs: ctx.ports.jobs,
|
|
249
257
|
batchSize,
|
|
250
|
-
|
|
258
|
+
concurrency,
|
|
259
|
+
instrumentation: {
|
|
260
|
+
instrumentation: ctx.ports.instrumentation ?? ctx.ports.devtools,
|
|
261
|
+
tracing: ctx.ports.tracing,
|
|
262
|
+
},
|
|
251
263
|
instrumentationContext: {
|
|
252
264
|
requestId: ctx.requestId,
|
|
253
265
|
traceId: ctx.traceId,
|
|
@@ -288,27 +300,65 @@ export async function runOutboxDrain(
|
|
|
288
300
|
},
|
|
289
301
|
});
|
|
290
302
|
},
|
|
291
|
-
|
|
303
|
+
onLeaseError(failure) {
|
|
304
|
+
const outcome =
|
|
305
|
+
failure.state === "lost"
|
|
306
|
+
? "leaseLost"
|
|
307
|
+
: failure.state === "recovered"
|
|
308
|
+
? "leaseRecovered"
|
|
309
|
+
: "leaseDegraded";
|
|
292
310
|
return reportOperationalFailure({
|
|
293
311
|
ctx,
|
|
294
|
-
error:
|
|
312
|
+
error: failure.error,
|
|
313
|
+
reportOptions: {
|
|
314
|
+
level: failure.state === "lost" ? "error" : "warning",
|
|
315
|
+
mechanism: "beignet.outbox.cli",
|
|
316
|
+
handled: failure.state !== "lost",
|
|
317
|
+
tags: {
|
|
318
|
+
"beignet.kind": "outbox",
|
|
319
|
+
"beignet.outbox.kind": failure.message.kind,
|
|
320
|
+
"beignet.outbox.name": failure.message.name,
|
|
321
|
+
"beignet.outbox.outcome": outcome,
|
|
322
|
+
},
|
|
323
|
+
contexts: {
|
|
324
|
+
outbox: {
|
|
325
|
+
messageId: failure.message.id,
|
|
326
|
+
kind: failure.message.kind,
|
|
327
|
+
name: failure.message.name,
|
|
328
|
+
attempts: failure.message.attempts,
|
|
329
|
+
maxAttempts: failure.message.maxAttempts,
|
|
330
|
+
operation: failure.operation,
|
|
331
|
+
state: failure.state,
|
|
332
|
+
confirmedLost: failure.confirmedLost,
|
|
333
|
+
outcome,
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
onSettlementError(failure) {
|
|
340
|
+
return reportOperationalFailure({
|
|
341
|
+
ctx,
|
|
342
|
+
error: failure.error,
|
|
295
343
|
reportOptions: {
|
|
296
344
|
level: "error",
|
|
297
345
|
mechanism: "beignet.outbox.cli",
|
|
298
346
|
handled: false,
|
|
299
347
|
tags: {
|
|
300
348
|
"beignet.kind": "outbox",
|
|
301
|
-
"beignet.outbox.kind": message.kind,
|
|
302
|
-
"beignet.outbox.name": message.name,
|
|
349
|
+
"beignet.outbox.kind": failure.message.kind,
|
|
350
|
+
"beignet.outbox.name": failure.message.name,
|
|
303
351
|
"beignet.outbox.outcome": "settlementFailed",
|
|
304
352
|
},
|
|
305
353
|
contexts: {
|
|
306
354
|
outbox: {
|
|
307
|
-
messageId: message.id,
|
|
308
|
-
kind: message.kind,
|
|
309
|
-
name: message.name,
|
|
310
|
-
attempts: message.attempts,
|
|
311
|
-
maxAttempts: message.maxAttempts,
|
|
355
|
+
messageId: failure.message.id,
|
|
356
|
+
kind: failure.message.kind,
|
|
357
|
+
name: failure.message.name,
|
|
358
|
+
attempts: failure.message.attempts,
|
|
359
|
+
maxAttempts: failure.message.maxAttempts,
|
|
360
|
+
operation: failure.operation,
|
|
361
|
+
deliverySucceeded: failure.deliverySucceeded,
|
|
312
362
|
outcome: "settlementFailed",
|
|
313
363
|
},
|
|
314
364
|
},
|
|
@@ -620,6 +670,7 @@ function isOutboxPort(value: unknown): value is DrainOutboxOptions["outbox"] {
|
|
|
620
670
|
isRecord(value) &&
|
|
621
671
|
typeof value.enqueue === "function" &&
|
|
622
672
|
typeof value.claimBatch === "function" &&
|
|
673
|
+
typeof value.renewClaim === "function" &&
|
|
623
674
|
typeof value.markDelivered === "function" &&
|
|
624
675
|
typeof value.markFailed === "function"
|
|
625
676
|
);
|
|
@@ -677,7 +728,7 @@ async function recordOutboxDrain(
|
|
|
677
728
|
watcher: "outbox",
|
|
678
729
|
name: "outbox.drain",
|
|
679
730
|
label: "Outbox drain",
|
|
680
|
-
summary: `${result.delivered} delivered, ${result.retried} retried, ${result.deadLettered} dead-lettered`,
|
|
731
|
+
summary: `${result.delivered} delivered, ${result.retried} retried, ${result.deadLettered} dead-lettered, ${result.settlementFailed} settlement-uncertain, ${result.leaseLost} lease-lost`,
|
|
681
732
|
requestId: ctx.requestId,
|
|
682
733
|
traceId: ctx.traceId,
|
|
683
734
|
details: result,
|
package/src/templates/agents.ts
CHANGED
|
@@ -64,6 +64,8 @@ is a silent failure — the file exists but never runs:
|
|
|
64
64
|
fallback and rejects unmarked custom inline wiring as a conflict.
|
|
65
65
|
- Listeners must be added to the \`listeners\` array in \`server/listeners.ts\`
|
|
66
66
|
and wired through a \`registerListeners(...)\` call in server provider wiring.
|
|
67
|
+
Register them in \`start()\`, await \`registration.ready\`, and await
|
|
68
|
+
\`registration.unsubscribe()\` in \`stop()\`.
|
|
67
69
|
- Apps using Inngest must also spread feature job registries into
|
|
68
70
|
\`inngestJobs\` in \`server/inngest.ts\`.
|
|
69
71
|
- Apps using runtime integrity must list workflow registries in
|
|
@@ -105,7 +107,9 @@ re-exports and \`db\` for \`generate\`, \`migrate\`, \`seed\`, or \`reset\`
|
|
|
105
107
|
instead of falling back to a shell. Use the read-only \`db_status\` tool before
|
|
106
108
|
deploying. Use \`task_run\`, \`schedule_run\`,
|
|
107
109
|
\`outbox_inspect\`, and \`outbox_run\` for registered operational workflows
|
|
108
|
-
and outbox recovery.
|
|
110
|
+
and outbox recovery. Treat a drain with nonzero \`settlementFailed\` or
|
|
111
|
+
\`leaseLost\` as an operational failure. Parallel delivery is opt-in and does
|
|
112
|
+
not preserve message order.
|
|
109
113
|
|
|
110
114
|
## The framework already solves these
|
|
111
115
|
|
|
@@ -157,6 +161,15 @@ to one feature. The graph honors the app's complete TypeScript \`baseUrl\` and
|
|
|
157
161
|
edges as proof that no relationship exists. Registration diagnostics expose
|
|
158
162
|
the declaration as \`subject.file\` and \`subject.exportName\`.
|
|
159
163
|
|
|
164
|
+
After editing in a Git worktree, run \`${cli} map --changed\` before the full
|
|
165
|
+
check. It reports direct changes, reverse imports, one-hop semantic consumers,
|
|
166
|
+
feature context, and unresolved evidence without claiming that the change is
|
|
167
|
+
correct. Use \`--base <local-ref>\` for branch-wide impact from the merge base;
|
|
168
|
+
Beignet never fetches the ref. Recognized guide files and
|
|
169
|
+
root documentation-directory content stay non-impacting; other \`.mdx\` and
|
|
170
|
+
\`.txt\` paths remain application changes. Changed files with no mapped concept
|
|
171
|
+
appear as explicit gaps.
|
|
172
|
+
|
|
160
173
|
## Package skills
|
|
161
174
|
|
|
162
175
|
This app trusts Beignet's package-shipped agent skills through
|
|
@@ -173,6 +186,12 @@ skill-loading block.
|
|
|
173
186
|
|
|
174
187
|
## Placement rules
|
|
175
188
|
|
|
189
|
+
- Checked-in SQL migrations and Drizzle metadata live in root \`drizzle/\`.
|
|
190
|
+
- Seed generators compose feature seeds through the optional
|
|
191
|
+
\`server/seed.ts\` entrypoint.
|
|
192
|
+
- Long-running runtime entrypoints such as BullMQ workers live under optional
|
|
193
|
+
\`server/workers/\`; never start worker loops in lifecycle providers or
|
|
194
|
+
serverless route modules.
|
|
176
195
|
- Feature artifacts live under \`features/<feature>/\`, with tests in
|
|
177
196
|
\`features/<feature>/tests/\` (not \`__tests__/\`).
|
|
178
197
|
- Feature-specific client data-fetching helpers, query options, mutation
|
|
@@ -204,7 +223,10 @@ stable operation IDs, hashes, exact patches, and current diagnostics before
|
|
|
204
223
|
passing its \`planId\` and optional \`fixIds\` to \`doctor_fix\`. Guarded
|
|
205
224
|
apply returns the same plan metadata and inspection payload as the CLI. Use
|
|
206
225
|
\`app_map\` before broad code search and pass a feature
|
|
207
|
-
or node kinds to keep context focused.
|
|
226
|
+
or node kinds to keep context focused. After edits, pass
|
|
227
|
+
\`{ changed: true, base?: string }\` for the same report as
|
|
228
|
+
\`${cli} map --changed --json\`; changed mode rejects projection and diagnostic
|
|
229
|
+
inputs. Use \`explain\` for source-backed
|
|
208
230
|
relationships, conventions, findings, and suggested files for any mapped
|
|
209
231
|
concept or diagnostic; its kinds match the app map. Use \`check\` after edits
|
|
210
232
|
to run the complete validation loop; it returns the same versioned result as
|
|
@@ -222,7 +244,10 @@ and reports the script without executing it; it does not simulate SQL or data
|
|
|
222
244
|
changes. Use \`task_run\` and \`schedule_run\` for registered operational
|
|
223
245
|
workflows, \`outbox_inspect\` for read-only \`list\` and \`show\`, and
|
|
224
246
|
\`outbox_run\` for \`drain\`, \`requeue\`, \`purge\`, or \`prune\`; purge and
|
|
225
|
-
prune support \`dryRun\`.
|
|
247
|
+
prune support \`dryRun\`. A drain with nonzero \`settlementFailed\` or
|
|
248
|
+
\`leaseLost\` remains a complete JSON report but is an error result; inspect
|
|
249
|
+
storage health and clock synchronization before retrying. Operational commands
|
|
250
|
+
run in isolated process trees
|
|
226
251
|
with bounded results, cancellation, and timeouts. Optional \`module\` overrides
|
|
227
252
|
must remain inside the app root where the MCP server started. Cancellation and
|
|
228
253
|
timeouts cannot roll back side effects that already completed. Inspect app
|
package/src/templates/base.ts
CHANGED
|
@@ -324,7 +324,10 @@ Use \`${cli} make feature projects --recipe full-slice\` when you want a richer
|
|
|
324
324
|
|
|
325
325
|
Run \`${cli} map\` for the current architecture or \`${cli} map --json\` for
|
|
326
326
|
the versioned graph used by coding agents. Add \`--feature todos\` to focus on
|
|
327
|
-
one slice and its direct relationships.
|
|
327
|
+
one slice and its direct relationships. After editing in a Git worktree, run
|
|
328
|
+
\`${cli} map --changed\` to inspect direct changes, potential semantic
|
|
329
|
+
consumers, feature context, and unresolved evidence before \`${cli} check\`.
|
|
330
|
+
The changed map is report-only and does not prove correctness.
|
|
328
331
|
|
|
329
332
|
- \`features/todos/contracts.ts\` owns the HTTP contract and reuses shared feature schemas.
|
|
330
333
|
- \`features/todos/schemas.ts\` owns shared DTO and validation schemas used by contracts, use cases, ports, tests, and clients.
|