@mandujs/core 0.54.18 → 0.54.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.54.18",
3
+ "version": "0.54.20",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -240,9 +240,9 @@ describe("agent context", () => {
240
240
  expect(parsed.project.name).toBe("agent-app");
241
241
  });
242
242
 
243
- it("records changed file reasons and warns on internal API edits", async () => {
244
- const version = await runGit(root, ["--version"]);
245
- if (!version.ok) return;
243
+ it("records changed file reasons and warns on internal API edits", async () => {
244
+ const version = await runGit(root, ["--version"]);
245
+ if (!version.ok) return;
246
246
 
247
247
  expect((await runGit(root, ["init"])).ok).toBe(true);
248
248
  await runGit(root, ["config", "user.email", "agent@example.com"]);
@@ -264,12 +264,43 @@ describe("agent context", () => {
264
264
  expect(reason?.internalApi).toBe(true);
265
265
  expect(reason?.recommendedChecks).toContain("bun run check:public-api && bun run check:target-boundaries");
266
266
  expect(report.diagnostics.some((diag) => diag.code === "MANDU_VERIFY_INTERNAL_API_EDIT")).toBe(true);
267
- expect(report.suggestedCommands.map((cmd) => cmd.command)).toContain(
268
- "bun run check:public-api && bun run check:target-boundaries",
269
- );
270
- });
271
-
272
- it("turns a verify report into repair actions", async () => {
267
+ expect(report.suggestedCommands.map((cmd) => cmd.command)).toContain(
268
+ "bun run check:public-api && bun run check:target-boundaries",
269
+ );
270
+ });
271
+
272
+ it("recommends hydration gates for hydration-sensitive runtime edits", async () => {
273
+ const version = await runGit(root, ["--version"]);
274
+ if (!version.ok) return;
275
+
276
+ expect((await runGit(root, ["init"])).ok).toBe(true);
277
+ await runGit(root, ["config", "user.email", "agent@example.com"]);
278
+ await runGit(root, ["config", "user.name", "Agent"]);
279
+ expect((await runGit(root, ["add", "."])).ok).toBe(true);
280
+ expect((await runGit(root, ["commit", "-m", "initial"])).ok).toBe(true);
281
+
282
+ await writeFile(root, "packages/core/src/bundler/build.ts", "export const changed = true;\n");
283
+
284
+ const report = await buildAgentVerifyReport(root, {
285
+ includeDiagnose: false,
286
+ includeGuard: false,
287
+ includeContract: false,
288
+ });
289
+
290
+ const reason = report.changedFileReasons.find(
291
+ (entry) => entry.file === "packages/core/src/bundler/build.ts",
292
+ );
293
+ expect(reason?.recommendedChecks).toContain("bun run test:hydration-boundary");
294
+ expect(reason?.recommendedChecks).toContain("bun run test:hydration-e2e");
295
+ expect(reason?.recommendedChecks).toContain("bun run check:publish");
296
+
297
+ const suggestedCommands = report.suggestedCommands.map((cmd) => cmd.command);
298
+ expect(suggestedCommands).toContain("bun run test:hydration-boundary");
299
+ expect(suggestedCommands).toContain("bun run test:hydration-e2e");
300
+ expect(suggestedCommands).toContain("bun run check:publish");
301
+ });
302
+
303
+ it("turns a verify report into repair actions", async () => {
273
304
  await writeAgentVerifyReport(root, {
274
305
  schemaVersion: 1,
275
306
  framework: "mandu",
@@ -245,12 +245,17 @@ function commandSuggestions(
245
245
  if (files.some((file) => file.startsWith("packages/mcp/"))) {
246
246
  add("bun test packages/mcp/tests", "MCP package files changed.", true);
247
247
  }
248
- if (files.some((file) => file.includes("package.json") || file === "bun.lock")) {
249
- add("bun run check:publish", "Package metadata or lockfile changed.", true);
250
- }
251
- if (changedFileReasons.some((entry) => entry.internalApi)) {
252
- add("bun run check:public-api && bun run check:target-boundaries", "Internal framework boundaries changed.", true);
253
- }
248
+ if (files.some((file) => file.includes("package.json") || file === "bun.lock")) {
249
+ add("bun run check:publish", "Package metadata or lockfile changed.", true);
250
+ }
251
+ if (files.some(isHydrationSensitiveEdit)) {
252
+ add("bun run test:hydration-boundary", "Hydration-sensitive runtime/router/bundler files changed.", true);
253
+ add("bun run test:hydration-e2e", "Hydration-sensitive changes need browser-level validation.", true);
254
+ add("bun run check:publish", "Pre-publish hydration gates should pass before release.", true);
255
+ }
256
+ if (changedFileReasons.some((entry) => entry.internalApi)) {
257
+ add("bun run check:public-api && bun run check:target-boundaries", "Internal framework boundaries changed.", true);
258
+ }
254
259
  if (diagnostics.some((d) => d.severity === "error" || d.severity === "fatal")) {
255
260
  add("mandu agent repair --from .mandu/agent-verify.json", "Verification produced repairable diagnostics.", false);
256
261
  }
@@ -258,22 +263,40 @@ function commandSuggestions(
258
263
  return out;
259
264
  }
260
265
 
261
- function isInternalApiEdit(file: string): boolean {
262
- return [
263
- "packages/core/src/runtime/",
264
- "packages/core/src/bundler/",
266
+ function isInternalApiEdit(file: string): boolean {
267
+ return [
268
+ "packages/core/src/runtime/",
269
+ "packages/core/src/bundler/",
265
270
  "packages/core/src/server/",
266
271
  "packages/core/src/guard/",
267
272
  "packages/core/src/spec/",
268
273
  "packages/core/src/router/",
269
274
  "packages/core/src/internal/",
270
- ].some((prefix) => file.startsWith(prefix));
271
- }
272
-
273
- function changedFileReason(file: string): AgentChangedFileReason {
274
- const normalized = normalizePath(file) ?? file;
275
- const reasons: string[] = [];
276
- const recommendedChecks: string[] = [];
275
+ ].some((prefix) => file.startsWith(prefix));
276
+ }
277
+
278
+ function isHydrationSensitiveEdit(file: string): boolean {
279
+ return [
280
+ "packages/core/src/runtime/page-render-response.ts",
281
+ "packages/core/src/runtime/server.ts",
282
+ "packages/core/src/runtime/ssr.ts",
283
+ "packages/core/src/runtime/streaming-ssr.ts",
284
+ "packages/core/src/internal/client-boundary.ts",
285
+ "packages/core/src/bundler/build.ts",
286
+ "packages/core/src/bundler/client-boundary-transform.ts",
287
+ "packages/core/src/router/client-entry.ts",
288
+ "packages/core/src/router/fs-routes.ts",
289
+ "packages/core/src/router/fs-scanner.ts",
290
+ "packages/core/src/client/serialize.ts",
291
+ "packages/core/src/client/props-serialization.ts",
292
+ "packages/core/src/client/runtime-entry.ts",
293
+ ].includes(file);
294
+ }
295
+
296
+ function changedFileReason(file: string): AgentChangedFileReason {
297
+ const normalized = normalizePath(file) ?? file;
298
+ const reasons: string[] = [];
299
+ const recommendedChecks: string[] = [];
277
300
 
278
301
  if (/\.(ts|tsx|js|jsx)$/.test(normalized)) {
279
302
  reasons.push("Source code changed.");
@@ -295,13 +318,21 @@ function changedFileReason(file: string): AgentChangedFileReason {
295
318
  reasons.push("User-facing documentation changed.");
296
319
  recommendedChecks.push("bun run check:docs-drift");
297
320
  }
298
- if (normalized === "package.json" || normalized === "bun.lock" || normalized.endsWith("/package.json")) {
299
- reasons.push("Package metadata or dependency graph changed.");
300
- recommendedChecks.push("bun run check:publish");
301
- }
302
-
303
- const internalApi = isInternalApiEdit(normalized);
304
- if (internalApi) {
321
+ if (normalized === "package.json" || normalized === "bun.lock" || normalized.endsWith("/package.json")) {
322
+ reasons.push("Package metadata or dependency graph changed.");
323
+ recommendedChecks.push("bun run check:publish");
324
+ }
325
+ if (isHydrationSensitiveEdit(normalized)) {
326
+ reasons.push("Hydration/runtime/client boundary behavior changed.");
327
+ recommendedChecks.push(
328
+ "bun run test:hydration-boundary",
329
+ "bun run test:hydration-e2e",
330
+ "bun run check:publish",
331
+ );
332
+ }
333
+
334
+ const internalApi = isInternalApiEdit(normalized);
335
+ if (internalApi) {
305
336
  reasons.push("Framework internal API changed.");
306
337
  recommendedChecks.push("bun run check:public-api && bun run check:target-boundaries");
307
338
  }