@akanjs/cli 2.3.9-rc.4 → 2.3.9-rc.5

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/index.js CHANGED
@@ -3394,28 +3394,28 @@ class SysExecutor extends Executor {
3394
3394
  return scalarModules;
3395
3395
  }
3396
3396
  async getViewComponents() {
3397
- const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.View.tsx`).exists());
3397
+ const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.View.tsx`).exists());
3398
3398
  return viewComponents;
3399
3399
  }
3400
3400
  async getUnitComponents() {
3401
- const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Unit.tsx`).exists());
3401
+ const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Unit.tsx`).exists());
3402
3402
  return unitComponents;
3403
3403
  }
3404
3404
  async getTemplateComponents() {
3405
- const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Template.tsx`).exists());
3405
+ const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Template.tsx`).exists());
3406
3406
  return templateComponents;
3407
3407
  }
3408
3408
  async getViewsSourceCode() {
3409
3409
  const viewComponents = await this.getViewComponents();
3410
- return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`)));
3410
+ return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`)));
3411
3411
  }
3412
3412
  async getUnitsSourceCode() {
3413
3413
  const unitComponents = await this.getUnitComponents();
3414
- return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`)));
3414
+ return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`)));
3415
3415
  }
3416
3416
  async getTemplatesSourceCode() {
3417
3417
  const templateComponents = await this.getTemplateComponents();
3418
- return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${templateComponent}.Template.tsx`)));
3418
+ return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`)));
3419
3419
  }
3420
3420
  async getScalarConstantFiles() {
3421
3421
  const scalarModules = await this.getScalarModules();
@@ -4887,6 +4887,12 @@ var statusForScope = (commands, diagnostics, scopes, expectedScope) => {
4887
4887
  return "unknown";
4888
4888
  return hasScopeFailure(scopes, expectedScope, diagnostics) ? "failed" : "passed";
4889
4889
  };
4890
+ var statusForValidationKind = (commands, kind) => {
4891
+ const matching = commands.filter((command) => command.kind === kind);
4892
+ if (matching.length === 0)
4893
+ return "unknown";
4894
+ return matching.some((command) => command.status === "failed") ? "failed" : "passed";
4895
+ };
4890
4896
  var createKnownBlockers = (commands, diagnostics) => {
4891
4897
  const commandBlockers = commands.filter((command) => command.status === "failed" && (command.failureScope === "workspace-config" || command.failureScope === "environment")).map((command) => ({
4892
4898
  code: `workflow-validation-${command.failureScope}`,
@@ -4920,7 +4926,17 @@ var createValidationStatuses = (commands, diagnostics) => {
4920
4926
  const sourceStatus = statusForScope(commands, diagnostics, scopes, "source-change");
4921
4927
  const workspaceStatus = hasScopeFailure(scopes, "workspace-config", diagnostics) || hasScopeFailure(scopes, "environment", diagnostics) ? "failed" : commands.length || diagnostics.length ? "passed" : "unknown";
4922
4928
  const overallStatus = hasScopeFailure(scopes, "source-change", diagnostics) ? "failed" : hasScopeFailure(scopes, "workspace-config", diagnostics) ? "blocked-by-workspace-config" : hasScopeFailure(scopes, "environment", diagnostics) ? "blocked-by-environment" : workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed";
4923
- return { sourceStatus, workspaceStatus, overallStatus };
4929
+ return {
4930
+ sourceStatus,
4931
+ workspaceStatus,
4932
+ overallStatus,
4933
+ summary: {
4934
+ sourceChange: sourceStatus,
4935
+ generatedSync: statusForValidationKind(commands, "sync"),
4936
+ workspaceConfig: statusForScope(commands, diagnostics, scopes, "workspace-config"),
4937
+ environment: statusForScope(commands, diagnostics, scopes, "environment")
4938
+ }
4939
+ };
4924
4940
  };
4925
4941
  var createWorkflowValidationRunReport = async ({
4926
4942
  runId = createWorkflowRunId("validation"),
@@ -5030,221 +5046,6 @@ var createRepairReport = ({
5030
5046
  });
5031
5047
  // pkgs/@akanjs/devkit/workflow/executor.ts
5032
5048
  import { capitalize as capitalize2 } from "akanjs/common";
5033
- var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
5034
- var primitiveReportToWorkflowStepResult = (report) => ({
5035
- changedFiles: report.changedFiles,
5036
- generatedFiles: report.generatedFiles,
5037
- commands: report.validationCommands,
5038
- diagnostics: report.diagnostics,
5039
- recommendations: [],
5040
- nextActions: report.nextActions
5041
- });
5042
- var workflowStringInput = (value) => typeof value === "string" ? value : null;
5043
- var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
5044
- var resolveWorkflowSys = async (workspace, target) => {
5045
- if (!target)
5046
- return null;
5047
- const [apps, libs] = await workspace.getSyss();
5048
- if (apps.includes(target))
5049
- return AppExecutor.from(workspace, target);
5050
- if (libs.includes(target))
5051
- return LibExecutor.from(workspace, target);
5052
- return null;
5053
- };
5054
- var targetMissing = (input2 = "app") => ({
5055
- severity: "error",
5056
- code: "workflow-target-missing",
5057
- input: input2,
5058
- message: "Workflow target app or library was not found."
5059
- });
5060
- var inputMissing = (input2) => ({
5061
- severity: "error",
5062
- code: "workflow-input-missing",
5063
- input: input2,
5064
- message: `Workflow input "${input2}" is required for apply.`
5065
- });
5066
- var unsupportedInput = (input2, message) => ({
5067
- severity: "error",
5068
- code: "workflow-input-unsupported",
5069
- input: input2,
5070
- message
5071
- });
5072
- var addFieldUiSurfaceInspection = (plan) => {
5073
- const app = workflowStringInput(plan.inputs.app);
5074
- const module = workflowStringInput(plan.inputs.module) ?? "<module>";
5075
- const field = workflowStringInput(plan.inputs.field) ?? "<field>";
5076
- const typeName = workflowStringInput(plan.inputs.type);
5077
- const isNumeric = typeName === "Int" || typeName === "Float" || typeName === "integer" || typeName === "float";
5078
- const moduleClassName = capitalize2(module);
5079
- const target = `${app ? `apps/${app}` : "*"}/lib/${module}/${moduleClassName}.Template.tsx`;
5080
- return {
5081
- recommendations: [
5082
- {
5083
- code: "add-field-ui-surface-review",
5084
- kind: "manual-action",
5085
- target,
5086
- action: isNumeric ? `Review ${moduleClassName} Template/Unit/View/Store surfaces before adding ${field}; confirm the local Field.Text numeric parser/formatter, validation rule, and dictionary label pattern.` : `Add ${field} to ${moduleClassName} UI surfaces only when the local Field.* pattern is clear.`,
5087
- confidence: "medium",
5088
- message: isNumeric ? `No UI file was modified automatically because a safe numeric input component pattern is not yet detected for ${module}.${field}.` : `Review Template/Unit/View/Store surfaces for ${module}.${field}; no UI file was modified automatically.`
5089
- }
5090
- ],
5091
- nextActions: [
5092
- {
5093
- command: `akan workflow explain ${plan.workflow}`,
5094
- reason: "Review UI surface guidance before manually editing ambiguous UI files."
5095
- }
5096
- ]
5097
- };
5098
- };
5099
- var createWorkflowStepRegistry = ({
5100
- workspace,
5101
- createModule,
5102
- createScalar,
5103
- createUi,
5104
- addField,
5105
- addEnumField
5106
- }) => {
5107
- const inspect = async () => {
5108
- return;
5109
- };
5110
- const commandOnly = async () => {
5111
- return;
5112
- };
5113
- return {
5114
- inspectSystem: inspect,
5115
- inspectModule: inspect,
5116
- syncTarget: commandOnly,
5117
- lintTarget: commandOnly,
5118
- [workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
5119
- const app = workflowStringInput(plan.inputs.app);
5120
- const module = workflowStringInput(plan.inputs.module);
5121
- const sys2 = await resolveWorkflowSys(workspace, app);
5122
- if (!sys2 || !module)
5123
- return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
5124
- return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
5125
- },
5126
- [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
5127
- const app = workflowStringInput(plan.inputs.app);
5128
- const scalar = workflowStringInput(plan.inputs.scalar);
5129
- const sys2 = await resolveWorkflowSys(workspace, app);
5130
- if (!sys2 || !scalar)
5131
- return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
5132
- return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
5133
- },
5134
- [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
5135
- const surface = workflowStringInput(plan.inputs.surface);
5136
- if (surface !== "view" && surface !== "unit" && surface !== "template") {
5137
- return {
5138
- diagnostics: [
5139
- unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
5140
- ]
5141
- };
5142
- }
5143
- return primitiveReportToWorkflowStepResult(await createUi({
5144
- app: workflowStringInput(plan.inputs.app),
5145
- module: workflowStringInput(plan.inputs.module),
5146
- surface
5147
- }));
5148
- },
5149
- [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
5150
- if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
5151
- return primitiveReportToWorkflowStepResult(await addEnumField({
5152
- app: workflowStringInput(plan.inputs.app),
5153
- module: workflowStringInput(plan.inputs.module),
5154
- field: workflowStringInput(plan.inputs.field),
5155
- values: workflowStringListInput(plan.inputs.values),
5156
- defaultValue: workflowStringInput(plan.inputs.default)
5157
- }));
5158
- }
5159
- return primitiveReportToWorkflowStepResult(await addField({
5160
- app: workflowStringInput(plan.inputs.app),
5161
- module: workflowStringInput(plan.inputs.module),
5162
- field: workflowStringInput(plan.inputs.field),
5163
- type: workflowStringInput(plan.inputs.type),
5164
- defaultValue: workflowStringInput(plan.inputs.default)
5165
- }));
5166
- },
5167
- [workflowStepKey("add-field", "update-dictionary")]: inspect,
5168
- [workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
5169
- [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
5170
- app: workflowStringInput(plan.inputs.app),
5171
- module: workflowStringInput(plan.inputs.module),
5172
- field: workflowStringInput(plan.inputs.field),
5173
- values: workflowStringListInput(plan.inputs.values),
5174
- defaultValue: workflowStringInput(plan.inputs.default)
5175
- })),
5176
- [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
5177
- [workflowStepKey("add-enum-field", "update-option")]: inspect
5178
- };
5179
- };
5180
- class WorkflowExecutor {
5181
- registry;
5182
- constructor(registry) {
5183
- this.registry = registry;
5184
- }
5185
- async apply(plan) {
5186
- const changedFiles = [];
5187
- const generatedFiles = [];
5188
- const recommendedValidationCommands = [];
5189
- const diagnostics = [...plan.diagnostics];
5190
- const recommendations = [...plan.recommendations];
5191
- const nextActions = [];
5192
- if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5193
- return createWorkflowApplyReport({
5194
- workflow: plan.workflow,
5195
- mode: "apply",
5196
- changedFiles,
5197
- generatedFiles,
5198
- recommendedValidationCommands,
5199
- diagnostics,
5200
- recommendations,
5201
- nextActions,
5202
- plan
5203
- });
5204
- }
5205
- recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
5206
- nextActions.push(...workflowCommandsForPlan(plan));
5207
- for (const step of plan.steps) {
5208
- const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
5209
- if (!runner) {
5210
- diagnostics.push({
5211
- severity: "error",
5212
- code: "workflow-step-unsupported",
5213
- message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
5214
- });
5215
- nextActions.push({
5216
- command: `akan workflow explain ${plan.workflow}`,
5217
- reason: "Review the unsupported workflow step before retrying apply."
5218
- });
5219
- break;
5220
- }
5221
- const result = await runner(step, plan);
5222
- if (!result)
5223
- continue;
5224
- changedFiles.push(...result.changedFiles ?? []);
5225
- generatedFiles.push(...result.generatedFiles ?? []);
5226
- recommendedValidationCommands.push(...result.commands ?? []);
5227
- diagnostics.push(...result.diagnostics ?? []);
5228
- recommendations.push(...result.recommendations ?? []);
5229
- nextActions.push(...result.nextActions ?? []);
5230
- if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
5231
- break;
5232
- }
5233
- return createWorkflowApplyReport({
5234
- workflow: plan.workflow,
5235
- mode: "apply",
5236
- changedFiles,
5237
- generatedFiles,
5238
- recommendedValidationCommands,
5239
- diagnostics,
5240
- recommendations,
5241
- nextActions,
5242
- plan
5243
- });
5244
- }
5245
- }
5246
- // pkgs/@akanjs/devkit/workflow/plan.ts
5247
- import { capitalize as capitalize3 } from "akanjs/common";
5248
5049
 
5249
5050
  // pkgs/@akanjs/devkit/workflow/primitive.ts
5250
5051
  var createPrimitiveWriteReport = ({
@@ -5296,6 +5097,23 @@ var sourceFile = (sys2, path10, action, reason) => ({
5296
5097
  action,
5297
5098
  reason
5298
5099
  });
5100
+ var moduleComponentName = (moduleName) => `${moduleName.slice(0, 1).toUpperCase()}${moduleName.slice(1)}`;
5101
+ var moduleSourcePaths = (moduleName) => {
5102
+ const componentName = moduleComponentName(moduleName);
5103
+ return {
5104
+ abstract: `lib/${moduleName}/${moduleName}.abstract.md`,
5105
+ constant: `lib/${moduleName}/${moduleName}.constant.ts`,
5106
+ dictionary: `lib/${moduleName}/${moduleName}.dictionary.ts`,
5107
+ service: `lib/${moduleName}/${moduleName}.service.ts`,
5108
+ signal: `lib/${moduleName}/${moduleName}.signal.ts`,
5109
+ store: `lib/${moduleName}/${moduleName}.store.ts`,
5110
+ template: `lib/${moduleName}/${componentName}.Template.tsx`,
5111
+ unit: `lib/${moduleName}/${componentName}.Unit.tsx`,
5112
+ util: `lib/${moduleName}/${componentName}.Util.tsx`,
5113
+ view: `lib/${moduleName}/${componentName}.View.tsx`,
5114
+ zone: `lib/${moduleName}/${componentName}.Zone.tsx`
5115
+ };
5116
+ };
5299
5117
  var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
5300
5118
  var validationCommandsForTarget = (target) => [
5301
5119
  { command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
@@ -5322,6 +5140,50 @@ var createPassedPrimitiveReport = ({
5322
5140
  var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
5323
5141
  var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
5324
5142
  var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
5143
+ var koLabels = {
5144
+ amount: "\uAE08\uC561",
5145
+ budget: "\uC608\uC0B0",
5146
+ category: "\uCE74\uD14C\uACE0\uB9AC",
5147
+ content: "\uB0B4\uC6A9",
5148
+ count: "\uAC1C\uC218",
5149
+ createdAt: "\uC0DD\uC131\uC77C",
5150
+ date: "\uB0A0\uC9DC",
5151
+ description: "\uC124\uBA85",
5152
+ due: "\uB9C8\uAC10\uC77C",
5153
+ dueAt: "\uB9C8\uAC10\uC77C",
5154
+ email: "\uC774\uBA54\uC77C",
5155
+ enabled: "\uD65C\uC131\uD654",
5156
+ endAt: "\uC885\uB8CC\uC77C",
5157
+ id: "ID",
5158
+ name: "\uC774\uB984",
5159
+ owner: "\uB2F4\uB2F9\uC790",
5160
+ priority: "\uC6B0\uC120\uC21C\uC704",
5161
+ project: "\uD504\uB85C\uC81D\uD2B8",
5162
+ rating: "\uD3C9\uC810",
5163
+ startAt: "\uC2DC\uC791\uC77C",
5164
+ status: "\uC0C1\uD0DC",
5165
+ title: "\uC81C\uBAA9",
5166
+ updatedAt: "\uC218\uC815\uC77C"
5167
+ };
5168
+ var splitFieldWords = (fieldName) => fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").split(/\s+/).map((word) => word.trim()).filter(Boolean);
5169
+ var koLabelForField = (fieldName) => {
5170
+ if (koLabels[fieldName])
5171
+ return koLabels[fieldName];
5172
+ const words = splitFieldWords(fieldName);
5173
+ const translated = words.map((word) => koLabels[word] ?? koLabels[lowerlize(word)] ?? null);
5174
+ return translated.every(Boolean) ? translated.join(" ") : null;
5175
+ };
5176
+ var bilingualLabelForField = (fieldName) => {
5177
+ const en = titleize(fieldName);
5178
+ return { en, ko: koLabelForField(fieldName) ?? en };
5179
+ };
5180
+ var bilingualDescriptionForField = (fieldName) => {
5181
+ const label = bilingualLabelForField(fieldName);
5182
+ return {
5183
+ en: `Enter ${label.en.toLowerCase()}.`,
5184
+ ko: `${label.ko} \uAC12\uC744 \uC785\uB825\uD569\uB2C8\uB2E4.`
5185
+ };
5186
+ };
5325
5187
  var normalizeFieldType = (typeName) => {
5326
5188
  const normalizedTypes = {
5327
5189
  string: "String",
@@ -5349,6 +5211,15 @@ var ensureBaseTypeImport = (content, typeName) => {
5349
5211
  ${content}`;
5350
5212
  };
5351
5213
  var numericDefault = (typeName, rawDefault) => {
5214
+ if (typeof rawDefault === "number") {
5215
+ if (!Number.isFinite(rawDefault))
5216
+ return null;
5217
+ if (typeName === "Int" && !Number.isInteger(rawDefault))
5218
+ return null;
5219
+ return String(rawDefault);
5220
+ }
5221
+ if (typeof rawDefault !== "string")
5222
+ return null;
5352
5223
  const trimmed = rawDefault.trim();
5353
5224
  if (!trimmed || !Number.isFinite(Number(trimmed)))
5354
5225
  return null;
@@ -5356,16 +5227,41 @@ var numericDefault = (typeName, rawDefault) => {
5356
5227
  return null;
5357
5228
  return trimmed;
5358
5229
  };
5359
- var coerceFieldDefault = (typeName, defaultValue) => {
5360
- if (defaultValue === undefined || defaultValue === null || defaultValue === "")
5361
- return { expression: null };
5362
- const normalizedType = normalizeFieldType(typeName);
5230
+ var booleanDefault = (rawDefault) => {
5231
+ if (typeof rawDefault === "boolean")
5232
+ return String(rawDefault);
5233
+ if (typeof rawDefault !== "string")
5234
+ return null;
5235
+ const lowered = rawDefault.trim().toLowerCase();
5236
+ return lowered === "true" || lowered === "false" ? lowered : null;
5237
+ };
5238
+ var dateDefault = (rawDefault) => {
5239
+ if (typeof rawDefault === "number" && Number.isFinite(rawDefault))
5240
+ return `new Date(${rawDefault})`;
5241
+ if (typeof rawDefault !== "string")
5242
+ return null;
5243
+ const trimmed = rawDefault.trim();
5244
+ if (!trimmed)
5245
+ return null;
5246
+ if (trimmed === "now")
5247
+ return "new Date()";
5248
+ if (!Number.isNaN(Date.parse(trimmed)))
5249
+ return `new Date(${JSON.stringify(trimmed)})`;
5250
+ return null;
5251
+ };
5252
+ var coerceFieldDefault = (typeName, defaultValue, options = {}) => {
5253
+ const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5254
+ if (defaultValue === undefined || defaultValue === null || defaultValue === "") {
5255
+ return { expression: null, normalized: false, normalizedType };
5256
+ }
5363
5257
  if (normalizedType === "Int" || normalizedType === "Float") {
5364
5258
  const expression = numericDefault(normalizedType, defaultValue);
5365
5259
  if (expression !== null)
5366
- return { expression };
5260
+ return { expression, normalized: typeof defaultValue === "string", normalizedType };
5367
5261
  return {
5368
5262
  expression: null,
5263
+ normalized: false,
5264
+ normalizedType,
5369
5265
  diagnostic: {
5370
5266
  severity: "error",
5371
5267
  code: "primitive-default-value-invalid",
@@ -5376,11 +5272,13 @@ var coerceFieldDefault = (typeName, defaultValue) => {
5376
5272
  };
5377
5273
  }
5378
5274
  if (normalizedType === "Boolean") {
5379
- const lowered = defaultValue.trim().toLowerCase();
5380
- if (lowered === "true" || lowered === "false")
5381
- return { expression: lowered };
5275
+ const expression = booleanDefault(defaultValue);
5276
+ if (expression !== null)
5277
+ return { expression, normalized: typeof defaultValue === "string", normalizedType };
5382
5278
  return {
5383
5279
  expression: null,
5280
+ normalized: false,
5281
+ normalizedType,
5384
5282
  diagnostic: {
5385
5283
  severity: "error",
5386
5284
  code: "primitive-default-value-invalid",
@@ -5390,11 +5288,49 @@ var coerceFieldDefault = (typeName, defaultValue) => {
5390
5288
  }
5391
5289
  };
5392
5290
  }
5393
- return { expression: JSON.stringify(defaultValue) };
5291
+ if (normalizedType === "Date") {
5292
+ const expression = dateDefault(defaultValue);
5293
+ if (expression !== null)
5294
+ return { expression, normalized: true, normalizedType };
5295
+ return {
5296
+ expression: null,
5297
+ normalized: false,
5298
+ normalizedType,
5299
+ diagnostic: {
5300
+ severity: "error",
5301
+ code: "primitive-default-value-invalid",
5302
+ input: "default",
5303
+ failureScope: "source-change",
5304
+ message: `Default value for Date must be "now", a timestamp, or a parseable date string. Received: ${JSON.stringify(defaultValue)}.`
5305
+ }
5306
+ };
5307
+ }
5308
+ if (normalizedType === "enum") {
5309
+ if (typeof defaultValue === "string" && options.enumValues?.includes(defaultValue)) {
5310
+ return { expression: JSON.stringify(defaultValue), normalized: false, normalizedType };
5311
+ }
5312
+ return {
5313
+ expression: null,
5314
+ normalized: false,
5315
+ normalizedType,
5316
+ diagnostic: {
5317
+ severity: "error",
5318
+ code: "primitive-default-value-invalid",
5319
+ input: "default",
5320
+ failureScope: "source-change",
5321
+ message: `Default value for enum must be one of: ${(options.enumValues ?? []).join(", ")}. Received: ${JSON.stringify(defaultValue)}.`
5322
+ }
5323
+ };
5324
+ }
5325
+ return {
5326
+ expression: JSON.stringify(String(defaultValue)),
5327
+ normalized: typeof defaultValue !== "string",
5328
+ normalizedType
5329
+ };
5394
5330
  };
5395
- var fieldExpression = (typeName, defaultValue) => {
5331
+ var fieldExpression = (typeName, defaultValue, options = {}) => {
5396
5332
  const typeExpression = normalizeFieldType(typeName);
5397
- const defaultExpression = coerceFieldDefault(typeExpression, defaultValue).expression;
5333
+ const defaultExpression = coerceFieldDefault(options.enumValues ? "enum" : typeExpression, defaultValue, options).expression;
5398
5334
  const defaultOption = defaultExpression ? `, { default: ${defaultExpression} }` : "";
5399
5335
  return `field(${typeExpression}${defaultOption})`;
5400
5336
  };
@@ -5414,6 +5350,50 @@ var insertIntoObject = (content, className, line) => {
5414
5350
  `;
5415
5351
  return `${prefix}${insertion}${suffix}`;
5416
5352
  };
5353
+ var insertLightProjectionField = (content, moduleClassName, fieldName) => {
5354
+ const classIndex = content.indexOf(`export class Light${moduleClassName} extends via`);
5355
+ if (classIndex < 0)
5356
+ return null;
5357
+ const arrayMatch = /\[([\s\S]*?)\]\s+as const/.exec(content.slice(classIndex));
5358
+ if (!arrayMatch || arrayMatch.index === undefined)
5359
+ return null;
5360
+ const arrayStart = classIndex + arrayMatch.index;
5361
+ const arrayEnd = arrayStart + arrayMatch[0].length;
5362
+ const fields = [...arrayMatch[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]).filter(Boolean);
5363
+ if (fields.includes(fieldName))
5364
+ return content;
5365
+ const nextFields = [...fields, fieldName];
5366
+ const nextArray = nextFields.length === 0 ? "[] as const" : `[
5367
+ ${nextFields.map((field) => ` ${JSON.stringify(field)},`).join(`
5368
+ `)}
5369
+ ] as const`;
5370
+ return `${content.slice(0, arrayStart)}${nextArray}${content.slice(arrayEnd)}`;
5371
+ };
5372
+ var insertTemplateField = ({
5373
+ content,
5374
+ moduleName,
5375
+ moduleClassName,
5376
+ fieldName,
5377
+ component
5378
+ }) => {
5379
+ if (content.includes(`l("${moduleName}.${fieldName}")`) || content.includes(`.${fieldName}`))
5380
+ return content;
5381
+ const layoutEndIndex = content.indexOf(" </Layout.Template>");
5382
+ if (layoutEndIndex < 0)
5383
+ return null;
5384
+ const formName = `${moduleName}Form`;
5385
+ if (!content.includes(`const ${formName} = st.use.${moduleName}Form();`))
5386
+ return null;
5387
+ const fieldSetter = `st.do.set${moduleComponentName(fieldName)}On${moduleClassName}`;
5388
+ const fieldBlock = ` <${component}
5389
+ label={l("${moduleName}.${fieldName}")}
5390
+ desc={l("${moduleName}.${fieldName}.desc")}
5391
+ value={${formName}.${fieldName}}
5392
+ onChange={${fieldSetter}}
5393
+ />
5394
+ `;
5395
+ return `${content.slice(0, layoutEndIndex)}${fieldBlock}${content.slice(layoutEndIndex)}`;
5396
+ };
5417
5397
  var ensureEnumImport = (content) => {
5418
5398
  if (content.includes("enumOf"))
5419
5399
  return content;
@@ -5443,45 +5423,312 @@ ${enumClass}`;
5443
5423
  var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
5444
5424
  if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
5445
5425
  return content;
5446
- const label = titleize(fieldName);
5426
+ const label = bilingualLabelForField(fieldName);
5427
+ const desc = bilingualDescriptionForField(fieldName);
5447
5428
  const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
5448
5429
  if (modelIndex < 0)
5449
5430
  return null;
5450
5431
  const objectEndIndex = content.indexOf(" }))", modelIndex);
5451
5432
  if (objectEndIndex < 0)
5452
5433
  return null;
5453
- return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label)}, ${JSON.stringify(label)}]).desc([${JSON.stringify(label)}, ${JSON.stringify(label)}]),
5434
+ return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label.en)}, ${JSON.stringify(label.ko)}]).desc([${JSON.stringify(desc.en)}, ${JSON.stringify(desc.ko)}]),
5454
5435
  ${content.slice(objectEndIndex)}`;
5455
5436
  };
5456
- var ensureConstantTypeImport = (content, constantPath, typeName) => {
5457
- if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
5458
- return content;
5459
- const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
5460
- const existingImport = content.match(importPattern);
5461
- if (existingImport !== null) {
5462
- const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
5463
- return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
5437
+ var ensureConstantTypeImport = (content, constantPath, typeName) => {
5438
+ if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
5439
+ return content;
5440
+ const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
5441
+ const existingImport = content.match(importPattern);
5442
+ if (existingImport !== null) {
5443
+ const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
5444
+ return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
5445
+ }
5446
+ return `import type { ${typeName} } from "${constantPath}";
5447
+ ${content}`;
5448
+ };
5449
+ var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
5450
+ if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
5451
+ return content;
5452
+ const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
5453
+ ${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
5454
+ `)}
5455
+ }))
5456
+ `;
5457
+ const chainEndIndex = content.lastIndexOf(";");
5458
+ const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
5459
+ if (insertBeforeIndex === undefined)
5460
+ return null;
5461
+ return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
5462
+ };
5463
+ var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
5464
+
5465
+ // pkgs/@akanjs/devkit/workflow/uiPolicy.ts
5466
+ var addFieldUiPolicyForType = (typeName) => {
5467
+ const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5468
+ if (normalizedType === "Int" || normalizedType === "Float") {
5469
+ return {
5470
+ normalizedType,
5471
+ component: "Field.Number",
5472
+ confidence: "high",
5473
+ autoTemplateSupported: true
5474
+ };
5475
+ }
5476
+ if (normalizedType === "Date") {
5477
+ return {
5478
+ normalizedType,
5479
+ component: "Field.Date",
5480
+ confidence: "high",
5481
+ autoTemplateSupported: true
5482
+ };
5483
+ }
5484
+ if (normalizedType === "Boolean" || normalizedType === "enum") {
5485
+ return {
5486
+ normalizedType,
5487
+ component: "Field.ToggleSelect",
5488
+ confidence: "medium",
5489
+ autoTemplateSupported: false
5490
+ };
5491
+ }
5492
+ if (normalizedType === "String") {
5493
+ return {
5494
+ normalizedType,
5495
+ component: "Field.Text",
5496
+ confidence: "high",
5497
+ autoTemplateSupported: true
5498
+ };
5499
+ }
5500
+ return {
5501
+ normalizedType,
5502
+ component: "Field.Text",
5503
+ confidence: "low",
5504
+ autoTemplateSupported: false
5505
+ };
5506
+ };
5507
+
5508
+ // pkgs/@akanjs/devkit/workflow/executor.ts
5509
+ var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
5510
+ var primitiveReportToWorkflowStepResult = (report) => ({
5511
+ changedFiles: report.changedFiles,
5512
+ generatedFiles: report.generatedFiles,
5513
+ commands: report.validationCommands,
5514
+ diagnostics: report.diagnostics,
5515
+ recommendations: [],
5516
+ nextActions: report.nextActions
5517
+ });
5518
+ var workflowStringInput = (value) => typeof value === "string" ? value : null;
5519
+ var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
5520
+ var workflowStringArrayInput = (value) => Array.isArray(value) ? value : null;
5521
+ var workflowBooleanInput = (value) => typeof value === "boolean" ? value : null;
5522
+ var resolveWorkflowSys = async (workspace, target) => {
5523
+ if (!target)
5524
+ return null;
5525
+ const [apps, libs] = await workspace.getSyss();
5526
+ if (apps.includes(target))
5527
+ return AppExecutor.from(workspace, target);
5528
+ if (libs.includes(target))
5529
+ return LibExecutor.from(workspace, target);
5530
+ return null;
5531
+ };
5532
+ var targetMissing = (input2 = "app") => ({
5533
+ severity: "error",
5534
+ code: "workflow-target-missing",
5535
+ input: input2,
5536
+ message: "Workflow target app or library was not found."
5537
+ });
5538
+ var inputMissing = (input2) => ({
5539
+ severity: "error",
5540
+ code: "workflow-input-missing",
5541
+ input: input2,
5542
+ message: `Workflow input "${input2}" is required for apply.`
5543
+ });
5544
+ var unsupportedInput = (input2, message) => ({
5545
+ severity: "error",
5546
+ code: "workflow-input-unsupported",
5547
+ input: input2,
5548
+ message
5549
+ });
5550
+ var addFieldUiSurfaceInspection = (plan) => {
5551
+ const app = workflowStringInput(plan.inputs.app);
5552
+ const module = workflowStringInput(plan.inputs.module) ?? "<module>";
5553
+ const field = workflowStringInput(plan.inputs.field) ?? "<field>";
5554
+ const typeName = workflowStringInput(plan.inputs.type);
5555
+ const policy = addFieldUiPolicyForType(typeName ?? "String");
5556
+ const surfaces = workflowStringArrayInput(plan.inputs.surfaces);
5557
+ const templateRequested = surfaces?.includes("template") ?? false;
5558
+ const moduleClassName = capitalize2(module);
5559
+ const target = `${app ? `apps/${app}` : "*"}/${moduleSourcePaths(module).template}`;
5560
+ return {
5561
+ recommendations: [
5562
+ {
5563
+ code: "add-field-ui-surface-review",
5564
+ kind: "manual-action",
5565
+ target,
5566
+ action: templateRequested ? `Template was requested for ${field}. If no Template file changed, auto-edit was skipped because the file was missing, the generated ${module}Form/Layout.Template pattern was not found, or ${policy.component} needs option binding. Candidate position: inside Layout.Template near the existing Field components.` : `Template was not selected, so UI files are intentionally left unchanged. Candidate positions if you expose it later: Layout.Template field list for editing, Light${moduleClassName} projection for list/card data, and Unit/View card sections for display.`,
5567
+ confidence: "medium",
5568
+ message: `Review UI surfaces for ${module}.${field}; recommended component is ${policy.component}, with manual reasons and candidate positions in action.`
5569
+ }
5570
+ ],
5571
+ nextActions: [
5572
+ {
5573
+ command: `akan workflow explain ${plan.workflow}`,
5574
+ reason: "Review UI surface guidance before manually editing ambiguous UI files."
5575
+ }
5576
+ ]
5577
+ };
5578
+ };
5579
+ var createWorkflowStepRegistry = ({
5580
+ workspace,
5581
+ createModule,
5582
+ createScalar,
5583
+ createUi,
5584
+ addField,
5585
+ addEnumField
5586
+ }) => {
5587
+ const inspect = async () => {
5588
+ return;
5589
+ };
5590
+ const commandOnly = async () => {
5591
+ return;
5592
+ };
5593
+ return {
5594
+ inspectSystem: inspect,
5595
+ inspectModule: inspect,
5596
+ syncTarget: commandOnly,
5597
+ lintTarget: commandOnly,
5598
+ [workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
5599
+ const app = workflowStringInput(plan.inputs.app);
5600
+ const module = workflowStringInput(plan.inputs.module);
5601
+ const sys2 = await resolveWorkflowSys(workspace, app);
5602
+ if (!sys2 || !module)
5603
+ return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
5604
+ return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
5605
+ },
5606
+ [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
5607
+ const app = workflowStringInput(plan.inputs.app);
5608
+ const scalar = workflowStringInput(plan.inputs.scalar);
5609
+ const sys2 = await resolveWorkflowSys(workspace, app);
5610
+ if (!sys2 || !scalar)
5611
+ return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
5612
+ return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
5613
+ },
5614
+ [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
5615
+ const surface = workflowStringInput(plan.inputs.surface);
5616
+ if (surface !== "view" && surface !== "unit" && surface !== "template") {
5617
+ return {
5618
+ diagnostics: [
5619
+ unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
5620
+ ]
5621
+ };
5622
+ }
5623
+ return primitiveReportToWorkflowStepResult(await createUi({
5624
+ app: workflowStringInput(plan.inputs.app),
5625
+ module: workflowStringInput(plan.inputs.module),
5626
+ surface
5627
+ }));
5628
+ },
5629
+ [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
5630
+ if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
5631
+ return primitiveReportToWorkflowStepResult(await addEnumField({
5632
+ app: workflowStringInput(plan.inputs.app),
5633
+ module: workflowStringInput(plan.inputs.module),
5634
+ field: workflowStringInput(plan.inputs.field),
5635
+ values: workflowStringListInput(plan.inputs.values),
5636
+ defaultValue: workflowStringInput(plan.inputs.default),
5637
+ surfaces: workflowStringArrayInput(plan.inputs.surfaces),
5638
+ includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
5639
+ }));
5640
+ }
5641
+ return primitiveReportToWorkflowStepResult(await addField({
5642
+ app: workflowStringInput(plan.inputs.app),
5643
+ module: workflowStringInput(plan.inputs.module),
5644
+ field: workflowStringInput(plan.inputs.field),
5645
+ type: workflowStringInput(plan.inputs.type),
5646
+ defaultValue: workflowStringInput(plan.inputs.default),
5647
+ surfaces: workflowStringArrayInput(plan.inputs.surfaces),
5648
+ includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
5649
+ }));
5650
+ },
5651
+ [workflowStepKey("add-field", "update-dictionary")]: inspect,
5652
+ [workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
5653
+ [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
5654
+ app: workflowStringInput(plan.inputs.app),
5655
+ module: workflowStringInput(plan.inputs.module),
5656
+ field: workflowStringInput(plan.inputs.field),
5657
+ values: workflowStringListInput(plan.inputs.values),
5658
+ defaultValue: workflowStringInput(plan.inputs.default)
5659
+ })),
5660
+ [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
5661
+ [workflowStepKey("add-enum-field", "update-option")]: inspect
5662
+ };
5663
+ };
5664
+ class WorkflowExecutor {
5665
+ registry;
5666
+ constructor(registry) {
5667
+ this.registry = registry;
5464
5668
  }
5465
- return `import type { ${typeName} } from "${constantPath}";
5466
- ${content}`;
5467
- };
5468
- var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
5469
- if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
5470
- return content;
5471
- const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
5472
- ${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
5473
- `)}
5474
- }))
5475
- `;
5476
- const chainEndIndex = content.lastIndexOf(";");
5477
- const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
5478
- if (insertBeforeIndex === undefined)
5479
- return null;
5480
- return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
5481
- };
5482
- var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
5483
-
5669
+ async apply(plan) {
5670
+ const changedFiles = [];
5671
+ const generatedFiles = [];
5672
+ const recommendedValidationCommands = [];
5673
+ const diagnostics = [...plan.diagnostics];
5674
+ const recommendations = [...plan.recommendations];
5675
+ const nextActions = [];
5676
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5677
+ return createWorkflowApplyReport({
5678
+ workflow: plan.workflow,
5679
+ mode: "apply",
5680
+ changedFiles,
5681
+ generatedFiles,
5682
+ recommendedValidationCommands,
5683
+ diagnostics,
5684
+ recommendations,
5685
+ nextActions,
5686
+ plan
5687
+ });
5688
+ }
5689
+ recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
5690
+ nextActions.push(...workflowCommandsForPlan(plan));
5691
+ for (const step of plan.steps) {
5692
+ const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
5693
+ if (!runner) {
5694
+ diagnostics.push({
5695
+ severity: "error",
5696
+ code: "workflow-step-unsupported",
5697
+ message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
5698
+ });
5699
+ nextActions.push({
5700
+ command: `akan workflow explain ${plan.workflow}`,
5701
+ reason: "Review the unsupported workflow step before retrying apply."
5702
+ });
5703
+ break;
5704
+ }
5705
+ const result = await runner(step, plan);
5706
+ if (!result)
5707
+ continue;
5708
+ changedFiles.push(...result.changedFiles ?? []);
5709
+ generatedFiles.push(...result.generatedFiles ?? []);
5710
+ recommendedValidationCommands.push(...result.commands ?? []);
5711
+ diagnostics.push(...result.diagnostics ?? []);
5712
+ recommendations.push(...result.recommendations ?? []);
5713
+ nextActions.push(...result.nextActions ?? []);
5714
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
5715
+ break;
5716
+ }
5717
+ return createWorkflowApplyReport({
5718
+ workflow: plan.workflow,
5719
+ mode: "apply",
5720
+ changedFiles,
5721
+ generatedFiles,
5722
+ recommendedValidationCommands,
5723
+ diagnostics,
5724
+ recommendations,
5725
+ nextActions,
5726
+ plan
5727
+ });
5728
+ }
5729
+ }
5484
5730
  // pkgs/@akanjs/devkit/workflow/plan.ts
5731
+ import { capitalize as capitalize3 } from "akanjs/common";
5485
5732
  var surfaceModes = new Set(["infer", "include", "skip"]);
5486
5733
  var parseStringList = (value) => {
5487
5734
  if (Array.isArray(value)) {
@@ -5499,6 +5746,18 @@ var normalizeInputValue = (name, spec, value) => {
5499
5746
  const values = parseStringList(value);
5500
5747
  return values && values.length > 0 ? values : null;
5501
5748
  }
5749
+ if (spec.type === "boolean") {
5750
+ if (typeof value === "boolean")
5751
+ return value;
5752
+ if (typeof value !== "string")
5753
+ return null;
5754
+ const lowered = value.trim().toLowerCase();
5755
+ if (lowered === "true")
5756
+ return true;
5757
+ if (lowered === "false")
5758
+ return false;
5759
+ return null;
5760
+ }
5502
5761
  if (typeof value === "string" && surfaceModes.has(value))
5503
5762
  return value;
5504
5763
  throw new Error(`Unsupported workflow input value for ${name}`);
@@ -5506,17 +5765,45 @@ var normalizeInputValue = (name, spec, value) => {
5506
5765
  var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
5507
5766
  var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
5508
5767
  var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
5509
- var addFieldComponentForType = (typeName) => {
5510
- const normalizedType = normalizeFieldType(typeName);
5511
- if (normalizedType === "Boolean")
5512
- return "Field.ToggleSelect";
5513
- if (normalizedType === "Date")
5514
- return "Field.Date";
5515
- if (normalizedType === "Int" || normalizedType === "Float")
5516
- return "manual numeric input review";
5517
- if (typeName.toLowerCase() === "enum")
5518
- return "Field.ToggleSelect";
5519
- return "Field.Text";
5768
+ var addFieldTargetRoot = (app) => app ? `apps/${app}` : "*";
5769
+ var addFieldPaths = (inputs) => {
5770
+ const app = typeof inputs.app === "string" ? inputs.app : null;
5771
+ const module = typeof inputs.module === "string" ? inputs.module : "<module>";
5772
+ const paths = moduleSourcePaths(module);
5773
+ const root = addFieldTargetRoot(app);
5774
+ return {
5775
+ constant: `${root}/${paths.constant}`,
5776
+ dictionary: `${root}/${paths.dictionary}`,
5777
+ template: `${root}/${paths.template}`,
5778
+ unit: `${root}/${paths.unit}`,
5779
+ view: `${root}/${paths.view}`
5780
+ };
5781
+ };
5782
+ var selectedAddFieldSurfaces = (inputs) => Array.isArray(inputs.surfaces) ? new Set(inputs.surfaces) : null;
5783
+ var addFieldDefaultCoercion = (inputs) => {
5784
+ const typeName = typeof inputs.type === "string" ? inputs.type : null;
5785
+ const defaultValue = typeof inputs.default === "string" ? inputs.default : null;
5786
+ if (!typeName || defaultValue === null)
5787
+ return null;
5788
+ if (typeName.toLowerCase() === "number" || typeName.toLowerCase() === "numeric")
5789
+ return null;
5790
+ const enumValues = Array.isArray(inputs.values) ? inputs.values : null;
5791
+ return coerceFieldDefault(typeName.toLowerCase() === "enum" ? "enum" : typeName, defaultValue, { enumValues });
5792
+ };
5793
+ var createAddFieldDefaultRecommendations = (inputs) => {
5794
+ const field = typeof inputs.field === "string" ? inputs.field : "<field>";
5795
+ const coercion = addFieldDefaultCoercion(inputs);
5796
+ if (!coercion?.normalized || !coercion.expression)
5797
+ return [];
5798
+ return [
5799
+ {
5800
+ code: "add-field-default-normalized",
5801
+ kind: "manual-action",
5802
+ confidence: "high",
5803
+ message: `Default for ${field} will be normalized to ${coercion.normalizedType} literal ${coercion.expression}.`,
5804
+ action: "Review the normalized default in the apply report if this value should stay unset."
5805
+ }
5806
+ ];
5520
5807
  };
5521
5808
  var createAddFieldRecommendations = (inputs) => {
5522
5809
  const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
@@ -5525,16 +5812,18 @@ var createAddFieldRecommendations = (inputs) => {
5525
5812
  const typeName = typeof inputs.type === "string" ? inputs.type : null;
5526
5813
  if (!typeName)
5527
5814
  return [];
5528
- const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5529
- const constantPath = `*/lib/${module}/${module}.constant.ts`;
5530
- const dictionaryPath = `*/lib/${module}/${module}.dictionary.ts`;
5531
- const templatePath = `*/lib/${module}/${capitalize3(module)}.Template.tsx`;
5815
+ const policy = addFieldUiPolicyForType(typeName);
5816
+ const normalizedType = policy.normalizedType;
5817
+ const paths = addFieldPaths(inputs);
5818
+ const surfaces = selectedAddFieldSurfaces(inputs);
5819
+ const templateRequested = surfaces?.has("template") ?? false;
5820
+ const includeInLight = inputs.includeInLight === true;
5532
5821
  return [
5533
5822
  ...normalizedType === "Int" || normalizedType === "Float" ? [
5534
5823
  {
5535
5824
  code: "add-field-import",
5536
5825
  kind: "import",
5537
- target: constantPath,
5826
+ target: paths.constant,
5538
5827
  confidence: "high",
5539
5828
  message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
5540
5829
  }
@@ -5542,35 +5831,122 @@ var createAddFieldRecommendations = (inputs) => {
5542
5831
  {
5543
5832
  code: "add-field-placement-constant",
5544
5833
  kind: "placement",
5545
- target: constantPath,
5834
+ target: paths.constant,
5546
5835
  confidence: "high",
5547
5836
  message: `Insert ${field}: field(${normalizedType}) in ${capitalize3(module)}Input.`
5548
5837
  },
5549
5838
  {
5550
5839
  code: "add-field-placement-dictionary",
5551
5840
  kind: "placement",
5552
- target: dictionaryPath,
5841
+ target: paths.dictionary,
5553
5842
  confidence: "high",
5554
5843
  message: `Add dictionary labels for ${module}.${field}.`
5555
5844
  },
5556
5845
  {
5557
5846
  code: "add-field-component",
5558
5847
  kind: "ui-component",
5559
- target: templatePath,
5560
- confidence: normalizedType === "Int" || normalizedType === "Float" ? "low" : "high",
5561
- action: normalizedType === "Int" || normalizedType === "Float" ? "Do not auto-edit UI. Check local Template/Unit/View patterns for Field.Text parsing, formatting, validation rules, and dictionary labels before adding a numeric input." : undefined,
5562
- message: normalizedType === "Int" || normalizedType === "Float" ? `Numeric UI for ${field} (${normalizedType}) requires manual review; a safe numeric input component pattern is not yet detected.` : `Recommended UI component for ${field} (${normalizedType}): ${addFieldComponentForType(typeName)}.`
5848
+ target: paths.template,
5849
+ confidence: policy.confidence,
5850
+ action: templateRequested ? `apply_workflow will try to add ${policy.component} to Template when the existing Template has a safe generated field-list pattern.` : `Recommended component is ${policy.component}. Pass surfaces=["template"] when this field should be rendered in the Template form.`,
5851
+ message: `Recommended UI component for ${field} (${normalizedType}): ${policy.component}.`
5563
5852
  },
5853
+ ...!surfaces ? [
5854
+ {
5855
+ code: "add-field-template-surface-choice",
5856
+ kind: "manual-action",
5857
+ target: paths.template,
5858
+ action: `Choose whether to expose ${field} in Template by passing surfaces=["template"].`,
5859
+ confidence: "medium",
5860
+ message: `Template exposure for ${module}.${field} is not selected yet.`
5861
+ }
5862
+ ] : [],
5863
+ ...!includeInLight ? [
5864
+ {
5865
+ code: "add-field-light-projection-choice",
5866
+ kind: "manual-action",
5867
+ target: paths.constant,
5868
+ action: `Pass includeInLight=true when ${field} should appear in Light${capitalize3(module)} list/card projections.`,
5869
+ confidence: "medium",
5870
+ message: `Light projection exposure for ${module}.${field} is not selected yet.`
5871
+ }
5872
+ ] : [],
5873
+ ...includeInLight ? [
5874
+ {
5875
+ code: "add-field-light-projection",
5876
+ kind: "placement",
5877
+ target: paths.constant,
5878
+ confidence: "high",
5879
+ message: `Add ${field} to Light${capitalize3(module)} projection fields.`
5880
+ }
5881
+ ] : [],
5882
+ ...surfaces && !templateRequested ? [
5883
+ {
5884
+ code: "add-field-ui-surface-skip",
5885
+ kind: "manual-action",
5886
+ target: paths.template,
5887
+ confidence: "medium",
5888
+ message: `Template is not included in surfaces, so ${module}.${field} will not be auto-rendered there.`
5889
+ }
5890
+ ] : [],
5564
5891
  {
5565
5892
  code: "add-field-ui-manual-review",
5566
5893
  kind: "manual-action",
5567
- target: templatePath,
5568
- action: `Review ${app}:${module} Template/Unit/View/Store surfaces and add ${field} only where the existing pattern is clear. For numeric fields, confirm parser/formatter behavior and validation before using Field.Text.`,
5894
+ target: paths.template,
5895
+ action: `Review ${app}:${module} UI after apply. Template auto-edit requires a generated ${module}Form hook and Layout.Template field list; Unit/View are not auto-edited because list/card placement depends on local layout. Candidate positions: Layout.Template before the closing tag, Light${capitalize3(module)} projection array for list data, and Unit/View card sections for display.`,
5569
5896
  confidence: "medium",
5570
- message: normalizedType === "Int" || normalizedType === "Float" ? "UI surface edits stay manual because a safe numeric input component pattern is not yet detected." : "UI surface edits are planned as manual-review unless a safe existing field-list pattern is detected."
5897
+ message: "UI manual review includes the reason auto-edit may be skipped and candidate insertion positions."
5898
+ },
5899
+ ...createAddFieldDefaultRecommendations(inputs)
5900
+ ];
5901
+ };
5902
+ var createWorkflowPlanPredictedChanges = (spec, inputs) => {
5903
+ if (spec.name !== "add-field")
5904
+ return spec.predictedChanges;
5905
+ const paths = addFieldPaths(inputs);
5906
+ const surfaces = selectedAddFieldSurfaces(inputs);
5907
+ const includeInLight = inputs.includeInLight === true;
5908
+ return [
5909
+ {
5910
+ target: paths.constant,
5911
+ action: "modify",
5912
+ applyScope: "auto",
5913
+ reason: includeInLight ? "Field shape and Light projection are added." : "Field shape is added."
5914
+ },
5915
+ {
5916
+ target: paths.dictionary,
5917
+ action: "modify",
5918
+ applyScope: "auto",
5919
+ reason: "Field label is added."
5920
+ },
5921
+ ...!surfaces || surfaces.has("template") ? [
5922
+ {
5923
+ target: paths.template,
5924
+ action: "modify",
5925
+ applyScope: surfaces?.has("template") ? "auto" : "manual-review",
5926
+ reason: surfaces?.has("template") ? "Template form is selected for safe-pattern auto insertion." : "Form surface may include the field."
5927
+ }
5928
+ ] : [],
5929
+ {
5930
+ target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/cnst.ts`,
5931
+ action: "sync",
5932
+ applyScope: "generated-sync",
5933
+ reason: "Generated constants may change after sync."
5934
+ },
5935
+ {
5936
+ target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/dict.ts`,
5937
+ action: "sync",
5938
+ applyScope: "generated-sync",
5939
+ reason: "Generated dictionary barrel may change after sync."
5571
5940
  }
5572
5941
  ];
5573
5942
  };
5943
+ var createWorkflowPlanOptionalSurfaces = (spec, inputs) => {
5944
+ const optionalSurfaces = spec.optionalSurfaces ?? {};
5945
+ const surfaces = selectedAddFieldSurfaces(inputs);
5946
+ if (spec.name !== "add-field" || !surfaces)
5947
+ return optionalSurfaces;
5948
+ return Object.fromEntries(Object.entries(optionalSurfaces).map(([surface, mode]) => [surface, surfaces.has(surface) ? "include" : mode]));
5949
+ };
5574
5950
  var createWorkflowPlanRecommendations = (spec, inputs) => [
5575
5951
  {
5576
5952
  code: "workflow-apply-first",
@@ -5634,14 +6010,32 @@ var createWorkflowPlan = (spec, rawInputs) => {
5634
6010
  message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
5635
6011
  });
5636
6012
  }
6013
+ if (spec.name === "add-field") {
6014
+ const defaultCoercion = addFieldDefaultCoercion(inputs);
6015
+ if (defaultCoercion?.diagnostic) {
6016
+ diagnostics.push({
6017
+ ...defaultCoercion.diagnostic,
6018
+ code: "workflow-default-value-invalid",
6019
+ message: `Plan input default is not valid for ${defaultCoercion.normalizedType}: ${defaultCoercion.diagnostic.message}`
6020
+ });
6021
+ } else if (defaultCoercion?.normalized && defaultCoercion.expression) {
6022
+ diagnostics.push({
6023
+ severity: "warning",
6024
+ code: "workflow-default-value-normalized",
6025
+ input: "default",
6026
+ failureScope: "source-change",
6027
+ message: `Plan input default will be written as ${defaultCoercion.normalizedType} literal ${defaultCoercion.expression}.`
6028
+ });
6029
+ }
6030
+ }
5637
6031
  return {
5638
6032
  schemaVersion: 1,
5639
6033
  workflow: spec.name,
5640
6034
  mode: "plan",
5641
6035
  inputs,
5642
- optionalSurfaces: spec.optionalSurfaces ?? {},
6036
+ optionalSurfaces: createWorkflowPlanOptionalSurfaces(spec, inputs),
5643
6037
  steps: spec.steps,
5644
- predictedChanges: spec.predictedChanges,
6038
+ predictedChanges: createWorkflowPlanPredictedChanges(spec, inputs),
5645
6039
  validation: spec.validation,
5646
6040
  diagnostics,
5647
6041
  recommendations: createWorkflowPlanRecommendations(spec, inputs),
@@ -5710,35 +6104,55 @@ var renderWorkflowPlan = (plan) => [
5710
6104
  ""
5711
6105
  ].join(`
5712
6106
  `);
5713
- var renderWorkflowApplyReport = (report) => [
5714
- `# Workflow Apply: ${report.workflow}`,
5715
- "",
5716
- `- Mode: ${report.mode}`,
5717
- `- Status: ${report.status}`,
5718
- "",
5719
- "## Changed Files",
5720
- ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5721
- "",
5722
- "## Generated Files",
5723
- ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5724
- "",
5725
- "## Applied Commands",
5726
- ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
5727
- "",
5728
- "## Recommended Validation Commands",
5729
- ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
5730
- "",
5731
- "## Diagnostics",
5732
- ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
5733
- "",
5734
- "## Recommendations",
5735
- ...report.recommendations.length ? report.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
5736
- "",
5737
- "## Next Actions",
5738
- ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
5739
- ""
5740
- ].join(`
6107
+ var renderRecommendation = (recommendation) => {
6108
+ const target = recommendation.target ? ` ${recommendation.target}` : "";
6109
+ const action = recommendation.action ? ` Action: ${recommendation.action}` : "";
6110
+ return `- [${recommendation.kind}]${target} ${recommendation.message}${action}`;
6111
+ };
6112
+ var renderDiagnostic = (diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`;
6113
+ var renderWorkflowApplyReport = (report) => {
6114
+ const manualReviewItems = [
6115
+ ...report.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").map(renderDiagnostic),
6116
+ ...report.recommendations.filter((recommendation) => recommendation.kind === "manual-action").map(renderRecommendation)
6117
+ ];
6118
+ const validationBlockers = report.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment")).map(renderDiagnostic);
6119
+ return [
6120
+ `# Workflow Apply: ${report.workflow}`,
6121
+ "",
6122
+ `- Mode: ${report.mode}`,
6123
+ `- Status: ${report.status}`,
6124
+ ...report.validationTarget ? [`- Validation target: ${report.validationTarget}`] : [],
6125
+ "",
6126
+ "## Automatically Modified",
6127
+ ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6128
+ "",
6129
+ "## Generated Sync",
6130
+ ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6131
+ "",
6132
+ "## Applied Commands",
6133
+ ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6134
+ "",
6135
+ "## Recommended Validation Commands",
6136
+ ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6137
+ "",
6138
+ "## User Review Required",
6139
+ ...manualReviewItems.length ? manualReviewItems : ["- none"],
6140
+ "",
6141
+ "## Validation Blockers",
6142
+ ...validationBlockers.length ? validationBlockers : report.recommendedValidationCommands.length ? ["- none detected during apply; run validation with the validation target for command-level blockers."] : ["- none"],
6143
+ "",
6144
+ "## Diagnostics",
6145
+ ...report.diagnostics.length ? report.diagnostics.map(renderDiagnostic) : ["- none"],
6146
+ "",
6147
+ "## Recommendations",
6148
+ ...report.recommendations.length ? report.recommendations.map(renderRecommendation) : ["- none"],
6149
+ "",
6150
+ "## Next Actions",
6151
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
6152
+ ""
6153
+ ].join(`
5741
6154
  `);
6155
+ };
5742
6156
  var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
5743
6157
  var renderWorkflowValidationRunReport = (report) => [
5744
6158
  `# Workflow Validation: ${report.workflow}`,
@@ -5749,11 +6163,18 @@ var renderWorkflowValidationRunReport = (report) => [
5749
6163
  `- Workspace status: ${report.workspaceStatus}`,
5750
6164
  `- Overall status: ${report.overallStatus}`,
5751
6165
  "",
6166
+ "## Status Summary",
6167
+ `- Required source-change validation: ${report.summary.sourceChange}`,
6168
+ `- Generated sync validation: ${report.summary.generatedSync}`,
6169
+ `- Workspace configuration validation: ${report.summary.workspaceConfig}`,
6170
+ `- Environment validation: ${report.summary.environment}`,
6171
+ "",
5752
6172
  "## Known Blockers",
5753
6173
  ...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
5754
6174
  const command = blocker.command ? ` \`${blocker.command}\`` : "";
5755
6175
  const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
5756
- return `- [${blocker.failureScope}]${command}${count}: ${blocker.message}`;
6176
+ const known = blocker.known ? " known" : "";
6177
+ return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
5757
6178
  }) : ["- none"],
5758
6179
  "",
5759
6180
  "## Commands",
@@ -12661,6 +13082,8 @@ var NODE_NATIVE_MODULE_SET = new Set([
12661
13082
  ]);
12662
13083
  // pkgs/@akanjs/devkit/getCredentials.ts
12663
13084
  import yaml from "js-yaml";
13085
+ // pkgs/@akanjs/devkit/getModelFileData.ts
13086
+ import { capitalize as capitalize8 } from "akanjs/common";
12664
13087
  // pkgs/@akanjs/devkit/getRelatedCnsts.ts
12665
13088
  import { readFileSync as readFileSync4, realpathSync } from "fs";
12666
13089
  import ora2 from "ora";
@@ -14611,7 +15034,7 @@ import path43 from "path";
14611
15034
 
14612
15035
  // pkgs/@akanjs/cli/module/module.script.ts
14613
15036
  import { input as input6 } from "@inquirer/prompts";
14614
- import { capitalize as capitalize10, randomPicks } from "akanjs/common";
15037
+ import { capitalize as capitalize11, randomPicks } from "akanjs/common";
14615
15038
 
14616
15039
  // pkgs/@akanjs/cli/page/page.runner.ts
14617
15040
  class PageRunner extends runner("page") {
@@ -14641,7 +15064,7 @@ function pluralizeName(name) {
14641
15064
  }
14642
15065
 
14643
15066
  // pkgs/@akanjs/cli/module/module.request.ts
14644
- import { capitalize as capitalize8 } from "akanjs/common";
15067
+ import { capitalize as capitalize9 } from "akanjs/common";
14645
15068
 
14646
15069
  // pkgs/@akanjs/cli/module/module.prompt.ts
14647
15070
  var componentDefaultDescription = ({
@@ -14766,7 +15189,7 @@ var requestTemplate = ({
14766
15189
  ${componentDefaultDescription({
14767
15190
  sysName,
14768
15191
  modelName,
14769
- ModelName: ModelName ?? capitalize8(modelName),
15192
+ ModelName: ModelName ?? capitalize9(modelName),
14770
15193
  exampleFiles,
14771
15194
  constant,
14772
15195
  properties
@@ -14823,7 +15246,7 @@ var requestView = ({
14823
15246
  ${componentDefaultDescription({
14824
15247
  sysName,
14825
15248
  modelName,
14826
- ModelName: ModelName ?? capitalize8(modelName),
15249
+ ModelName: ModelName ?? capitalize9(modelName),
14827
15250
  exampleFiles,
14828
15251
  constant,
14829
15252
  properties
@@ -14884,7 +15307,7 @@ var requestUnit = ({
14884
15307
  ${componentDefaultDescription({
14885
15308
  sysName,
14886
15309
  modelName,
14887
- ModelName: ModelName ?? capitalize8(modelName),
15310
+ ModelName: ModelName ?? capitalize9(modelName),
14888
15311
  exampleFiles,
14889
15312
  constant,
14890
15313
  properties
@@ -14933,22 +15356,29 @@ var requestUnit = ({
14933
15356
  `;
14934
15357
 
14935
15358
  // pkgs/@akanjs/cli/module/module.runner.ts
14936
- import { capitalize as capitalize9 } from "akanjs/common";
15359
+ import { capitalize as capitalize10 } from "akanjs/common";
15360
+ var purposeByModule = {
15361
+ budget: "Budget represents planned or actual money allocated inside the app.",
15362
+ project: "Project represents a project workspace or business initiative managed by the app.",
15363
+ task: "Task represents work items that move through the app workflow."
15364
+ };
14937
15365
  var moduleAbstractContent = (moduleName) => {
14938
- const title = capitalize9(moduleName);
15366
+ const title = capitalize10(moduleName);
15367
+ const label = bilingualLabelForField(moduleName);
15368
+ const purpose = purposeByModule[moduleName] ?? `${title} represents ${label.en.toLowerCase()} records managed by the app.`;
14939
15369
  return `# ${title} Module Abstract
14940
15370
 
14941
15371
  ## Purpose
14942
15372
 
14943
- ${title} owns persisted ${moduleName} records.
15373
+ ${purpose}
14944
15374
 
14945
15375
  ## Domain Rules
14946
15376
 
14947
- - No durable business invariants yet.
15377
+ - Keep ${label.en.toLowerCase()} data consistent with user-facing dictionary labels.
14948
15378
 
14949
15379
  ## Data Meaning
14950
15380
 
14951
- No additional data meaning has been documented yet.
15381
+ ${label.en} (${label.ko}) is the primary business concept for this module.
14952
15382
 
14953
15383
  ## Workflows
14954
15384
 
@@ -14965,6 +15395,7 @@ No lifecycle workflow yet.
14965
15395
  - None yet.
14966
15396
  `;
14967
15397
  };
15398
+ var localModuleFilename = (moduleName, pathKey2) => moduleSourcePaths(moduleName)[pathKey2].replace(`lib/${moduleName}/`, "");
14968
15399
 
14969
15400
  class ModuleRunner extends runner("module") {
14970
15401
  async createService(module) {
@@ -14995,24 +15426,47 @@ class ModuleRunner extends runner("module") {
14995
15426
  async createComponentTemplate(module, type) {
14996
15427
  await module.sys.applyTemplate({
14997
15428
  basePath: `./lib/${module.name}`,
14998
- template: `module/__Model__.${capitalize9(type)}.tsx`,
15429
+ template: `module/__Model__.${capitalize10(type)}.tsx`,
14999
15430
  dict: { model: module.name, appName: module.sys.name }
15000
15431
  });
15001
15432
  return {
15002
15433
  component: {
15003
- filename: `${module.name}.${capitalize9(type)}.tsx`,
15004
- content: await module.sys.readFile(`lib/${module.name}/${capitalize9(module.name)}.${capitalize9(type)}.tsx`)
15434
+ filename: `${capitalize10(module.name)}.${capitalize10(type)}.tsx`,
15435
+ content: await module.sys.readFile(`lib/${module.name}/${capitalize10(module.name)}.${capitalize10(type)}.tsx`)
15005
15436
  }
15006
15437
  };
15007
15438
  }
15008
15439
  async createModuleTemplate(module) {
15009
15440
  const names = pluralizeName(module.name);
15441
+ const modelLabel = bilingualLabelForField(module.name);
15442
+ const modelDescription = bilingualDescriptionForField(module.name);
15443
+ const filenames = {
15444
+ abstract: localModuleFilename(module.name, "abstract"),
15445
+ constant: localModuleFilename(module.name, "constant"),
15446
+ dictionary: localModuleFilename(module.name, "dictionary"),
15447
+ service: localModuleFilename(module.name, "service"),
15448
+ store: localModuleFilename(module.name, "store"),
15449
+ signal: localModuleFilename(module.name, "signal"),
15450
+ unit: localModuleFilename(module.name, "unit"),
15451
+ view: localModuleFilename(module.name, "view"),
15452
+ template: localModuleFilename(module.name, "template"),
15453
+ zone: localModuleFilename(module.name, "zone"),
15454
+ util: localModuleFilename(module.name, "util")
15455
+ };
15010
15456
  await module.applyTemplate({
15011
15457
  basePath: `.`,
15012
15458
  template: "module",
15013
- dict: { model: module.name, models: names, sysName: module.sys.name }
15459
+ dict: {
15460
+ model: module.name,
15461
+ models: names,
15462
+ sysName: module.sys.name,
15463
+ modelLabelEn: modelLabel.en,
15464
+ modelLabelKo: modelLabel.ko,
15465
+ modelDescEn: modelDescription.en,
15466
+ modelDescKo: modelDescription.ko
15467
+ }
15014
15468
  });
15015
- await module.writeFile(`${module.name}.abstract.md`, moduleAbstractContent(module.name));
15469
+ await module.writeFile(filenames.abstract, moduleAbstractContent(module.name));
15016
15470
  const [
15017
15471
  abstractContent,
15018
15472
  constantContent,
@@ -15026,30 +15480,30 @@ class ModuleRunner extends runner("module") {
15026
15480
  zoneContent,
15027
15481
  utilContent
15028
15482
  ] = await Promise.all([
15029
- module.readFile(`${module.name}.abstract.md`),
15030
- module.readFile(`${module.name}.constant.ts`),
15031
- module.readFile(`${module.name}.dictionary.ts`),
15032
- module.readFile(`${module.name}.service.ts`),
15033
- module.readFile(`${module.name}.store.ts`),
15034
- module.readFile(`${module.name}.signal.ts`),
15035
- module.readFile(`${module.name}.Unit.tsx`),
15036
- module.readFile(`${module.name}.View.tsx`),
15037
- module.readFile(`${module.name}.Template.tsx`),
15038
- module.readFile(`${module.name}.Zone.tsx`),
15039
- module.readFile(`${module.name}.Util.tsx`)
15483
+ module.readFile(filenames.abstract),
15484
+ module.readFile(filenames.constant),
15485
+ module.readFile(filenames.dictionary),
15486
+ module.readFile(filenames.service),
15487
+ module.readFile(filenames.store),
15488
+ module.readFile(filenames.signal),
15489
+ module.readFile(filenames.unit),
15490
+ module.readFile(filenames.view),
15491
+ module.readFile(filenames.template),
15492
+ module.readFile(filenames.zone),
15493
+ module.readFile(filenames.util)
15040
15494
  ]);
15041
15495
  return {
15042
- abstract: { filename: `${module.name}.abstract.md`, content: abstractContent },
15043
- constant: { filename: `${module.name}.constant.ts`, content: constantContent },
15044
- dictionary: { filename: `${module.name}.dictionary.ts`, content: dictionaryContent },
15045
- service: { filename: `${module.name}.service.ts`, content: serviceContent },
15046
- store: { filename: `${module.name}.store.ts`, content: storeContent },
15047
- signal: { filename: `${module.name}.signal.ts`, content: signalContent },
15048
- unit: { filename: `${module.name}.Unit.tsx`, content: unitContent },
15049
- view: { filename: `${module.name}.View.tsx`, content: viewContent },
15050
- template: { filename: `${module.name}.Template.tsx`, content: templateContent },
15051
- zone: { filename: `${module.name}.Zone.tsx`, content: zoneContent },
15052
- util: { filename: `${module.name}.Util.tsx`, content: utilContent }
15496
+ abstract: { filename: filenames.abstract, content: abstractContent },
15497
+ constant: { filename: filenames.constant, content: constantContent },
15498
+ dictionary: { filename: filenames.dictionary, content: dictionaryContent },
15499
+ service: { filename: filenames.service, content: serviceContent },
15500
+ store: { filename: filenames.store, content: storeContent },
15501
+ signal: { filename: filenames.signal, content: signalContent },
15502
+ unit: { filename: filenames.unit, content: unitContent },
15503
+ view: { filename: filenames.view, content: viewContent },
15504
+ template: { filename: filenames.template, content: templateContent },
15505
+ zone: { filename: filenames.zone, content: zoneContent },
15506
+ util: { filename: filenames.util, content: utilContent }
15053
15507
  };
15054
15508
  }
15055
15509
  }
@@ -15151,8 +15605,9 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
15151
15605
  async createTest(workspace, name) {}
15152
15606
  async createTemplate(mod) {
15153
15607
  const { component: template } = await this.moduleRunner.createComponentTemplate(mod, "template");
15154
- const templateExampleFiles = (await mod.sys.getTemplatesSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.Template.tsx`));
15155
- const Name = capitalize10(mod.name);
15608
+ const paths = moduleSourcePaths(mod.name);
15609
+ const templateExampleFiles = (await mod.sys.getTemplatesSourceCode()).filter((f) => !f.filePath.includes(paths.template));
15610
+ const Name = capitalize11(mod.name);
15156
15611
  const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15157
15612
  const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15158
15613
  const session = new AiSession("createTemplate", { workspace: mod.sys.workspace, cacheKey: mod.name });
@@ -15178,8 +15633,9 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
15178
15633
  }
15179
15634
  async createUnit(mod) {
15180
15635
  const { component: unit } = await this.moduleRunner.createComponentTemplate(mod, "unit");
15181
- const Name = capitalize10(mod.name);
15182
- const unitExampleFiles = (await mod.sys.getUnitsSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.Unit.tsx`));
15636
+ const paths = moduleSourcePaths(mod.name);
15637
+ const Name = capitalize11(mod.name);
15638
+ const unitExampleFiles = (await mod.sys.getUnitsSourceCode()).filter((f) => !f.filePath.includes(paths.unit));
15183
15639
  const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15184
15640
  const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15185
15641
  const session = new AiSession("createUnit", { workspace: mod.sys.workspace, cacheKey: mod.name });
@@ -15203,8 +15659,9 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
15203
15659
  }
15204
15660
  async createView(mod) {
15205
15661
  const { component: view } = await this.moduleRunner.createComponentTemplate(mod, "view");
15206
- const viewExampleFiles = (await mod.sys.getViewsSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.View.tsx`));
15207
- const Name = capitalize10(mod.name);
15662
+ const paths = moduleSourcePaths(mod.name);
15663
+ const viewExampleFiles = (await mod.sys.getViewsSourceCode()).filter((f) => !f.filePath.includes(paths.view));
15664
+ const Name = capitalize11(mod.name);
15208
15665
  const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15209
15666
  const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15210
15667
  const session = new AiSession("createView", { workspace: mod.sys.workspace, cacheKey: mod.name });
@@ -15229,7 +15686,7 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
15229
15686
  }
15230
15687
 
15231
15688
  // pkgs/@akanjs/cli/primitive/primitive.script.ts
15232
- import { capitalize as capitalize11 } from "akanjs/common";
15689
+ import { capitalize as capitalize12 } from "akanjs/common";
15233
15690
  class PrimitiveScript extends script("primitive", [ModuleScript]) {
15234
15691
  async resolveSys(workspace, target) {
15235
15692
  if (!target)
@@ -15277,7 +15734,7 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15277
15734
  }
15278
15735
  async addEnumField(workspace, input7) {
15279
15736
  const values = parseValues(input7.values);
15280
- return await this.addFieldToSources(workspace, { ...input7, type: `${capitalize11(input7.field ?? "")}` }, { enumValues: values });
15737
+ return await this.addFieldToSources(workspace, { ...input7, type: `${capitalize12(input7.field ?? "")}` }, { enumValues: values });
15281
15738
  }
15282
15739
  async addFieldToSources(workspace, input7, { enumValues }) {
15283
15740
  const sys3 = await this.resolveSys(workspace, input7.app);
@@ -15321,10 +15778,12 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15321
15778
  nextActions: []
15322
15779
  });
15323
15780
  }
15324
- const moduleClassName = capitalize11(input7.module);
15781
+ const moduleClassName = capitalize12(input7.module);
15325
15782
  const inputClassName = `${moduleClassName}Input`;
15326
- const constantPath = `lib/${input7.module}/${input7.module}.constant.ts`;
15327
- const dictionaryPath = `lib/${input7.module}/${input7.module}.dictionary.ts`;
15783
+ const paths = moduleSourcePaths(input7.module);
15784
+ const constantPath = paths.constant;
15785
+ const dictionaryPath = paths.dictionary;
15786
+ const templatePath = paths.template;
15328
15787
  const changedFiles = [];
15329
15788
  const generatedFiles2 = generatedFilesForSync(sys3);
15330
15789
  const [hasConstant, hasDictionary] = await Promise.all([sys3.exists(constantPath), sys3.exists(dictionaryPath)]);
@@ -15363,8 +15822,8 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15363
15822
  });
15364
15823
  }
15365
15824
  if (enumValues) {
15366
- const enumClassName = `${moduleClassName}${capitalize11(input7.field)}`;
15367
- const enumName = `${lowerlize(moduleClassName)}${capitalize11(input7.field)}`;
15825
+ const enumClassName = `${moduleClassName}${capitalize12(input7.field)}`;
15826
+ const enumName = `${lowerlize(moduleClassName)}${capitalize12(input7.field)}`;
15368
15827
  constantContent = insertEnumClass(ensureEnumImport(constantContent), enumClassName, enumName, enumValues);
15369
15828
  dictionaryContent = ensureConstantTypeImport(dictionaryContent, `./${input7.module}.constant`, enumClassName);
15370
15829
  input7.type = enumClassName;
@@ -15386,9 +15845,15 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15386
15845
  diagnostics.push(defaultCoercion.diagnostic);
15387
15846
  constantContent = ensureBaseTypeImport(constantContent, input7.type);
15388
15847
  }
15389
- const nextConstantContent = insertIntoObject(constantContent, inputClassName, `${input7.field}: ${fieldExpression(input7.type, input7.defaultValue)},`);
15848
+ if (enumValues) {
15849
+ const defaultCoercion = coerceFieldDefault("enum", input7.defaultValue, { enumValues });
15850
+ if (defaultCoercion.diagnostic)
15851
+ diagnostics.push(defaultCoercion.diagnostic);
15852
+ }
15853
+ const nextConstantContent = insertIntoObject(constantContent, inputClassName, `${input7.field}: ${fieldExpression(input7.type, input7.defaultValue, { enumValues })},`);
15854
+ const nextConstantContentWithLight = nextConstantContent && input7.includeInLight ? insertLightProjectionField(nextConstantContent, moduleClassName, input7.field) : nextConstantContent;
15390
15855
  const nextDictionaryContent = insertDictionaryModelField(dictionaryContent, moduleClassName, input7.field);
15391
- if (nextConstantContent && (input7.type === "Int" || input7.type === "Float") && new RegExp(`\\b${input7.field}\\s*:\\s*field\\(${input7.type}, \\{ default: "`, "m").test(nextConstantContent)) {
15856
+ if (nextConstantContentWithLight && (input7.type === "Int" || input7.type === "Float") && new RegExp(`\\b${input7.field}\\s*:\\s*field\\(${input7.type}, \\{ default: "`, "m").test(nextConstantContentWithLight)) {
15392
15857
  diagnostics.push({
15393
15858
  severity: "error",
15394
15859
  code: "primitive-default-value-invalid",
@@ -15397,7 +15862,7 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15397
15862
  message: `Generated ${input7.type} default for "${input7.field}" would be a string literal; refusing to write source.`
15398
15863
  });
15399
15864
  }
15400
- if (nextConstantContent && (input7.type === "Int" || input7.type === "Float") && !new RegExp(`import \\{[^}]*\\b${input7.type}\\b[^}]*\\} from "akanjs/base";`).test(nextConstantContent)) {
15865
+ if (nextConstantContentWithLight && (input7.type === "Int" || input7.type === "Float") && !new RegExp(`import \\{[^}]*\\b${input7.type}\\b[^}]*\\} from "akanjs/base";`).test(nextConstantContentWithLight)) {
15401
15866
  diagnostics.push({
15402
15867
  severity: "error",
15403
15868
  code: "primitive-base-type-import-missing",
@@ -15412,6 +15877,14 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15412
15877
  message: `Could not find ${inputClassName} object shape in ${constantPath}.`
15413
15878
  });
15414
15879
  }
15880
+ if (nextConstantContent && input7.includeInLight && !nextConstantContentWithLight) {
15881
+ diagnostics.push({
15882
+ severity: "warning",
15883
+ code: "primitive-light-projection-shape-unsupported",
15884
+ failureScope: "source-change",
15885
+ message: `Could not find a safe Light${moduleClassName} projection insertion point in ${constantPath}.`
15886
+ });
15887
+ }
15415
15888
  if (!nextDictionaryContent) {
15416
15889
  diagnostics.push({
15417
15890
  severity: "error",
@@ -15419,10 +15892,51 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15419
15892
  message: `Could not find ${moduleClassName} dictionary model shape in ${dictionaryPath}.`
15420
15893
  });
15421
15894
  }
15422
- if (!diagnostics.some((diagnostic) => diagnostic.severity === "error") && nextConstantContent && nextDictionaryContent) {
15423
- await sys3.writeFile(constantPath, nextConstantContent);
15895
+ let nextTemplateContent;
15896
+ if (input7.surfaces?.includes("template")) {
15897
+ const policy = addFieldUiPolicyForType(enumValues ? "enum" : input7.type);
15898
+ const hasTemplate = await sys3.exists(templatePath);
15899
+ if (!hasTemplate) {
15900
+ diagnostics.push({
15901
+ severity: "warning",
15902
+ code: "primitive-template-missing",
15903
+ failureScope: "source-change",
15904
+ message: `Template source file was not found: ${templatePath}. Auto-edit skipped; add the field to the Template form manually if this module renders one.`
15905
+ });
15906
+ } else if (!policy.autoTemplateSupported || policy.component === "Field.ToggleSelect") {
15907
+ diagnostics.push({
15908
+ severity: "warning",
15909
+ code: "primitive-template-component-manual",
15910
+ failureScope: "source-change",
15911
+ message: `Template auto insertion for ${policy.component} is not supported because option binding must be confirmed. Candidate position: inside Layout.Template near existing Field components in ${templatePath}.`
15912
+ });
15913
+ } else {
15914
+ const templateContent = await sys3.readFile(templatePath);
15915
+ nextTemplateContent = insertTemplateField({
15916
+ content: templateContent,
15917
+ moduleName: input7.module,
15918
+ moduleClassName,
15919
+ fieldName: input7.field,
15920
+ component: policy.component
15921
+ });
15922
+ if (!nextTemplateContent) {
15923
+ diagnostics.push({
15924
+ severity: "warning",
15925
+ code: "primitive-template-shape-unsupported",
15926
+ failureScope: "source-change",
15927
+ message: `Could not find a safe Template insertion point in ${templatePath}. Expected generated ${input7.module}Form hook and Layout.Template closing tag; candidate position is the existing field list before </Layout.Template>.`
15928
+ });
15929
+ }
15930
+ }
15931
+ }
15932
+ if (!diagnostics.some((diagnostic) => diagnostic.severity === "error") && nextConstantContentWithLight && nextDictionaryContent) {
15933
+ await sys3.writeFile(constantPath, nextConstantContentWithLight);
15424
15934
  await sys3.writeFile(dictionaryPath, nextDictionaryContent);
15425
15935
  changedFiles.push(sourceFile(sys3, constantPath, "modify", "Field source shape was updated."), sourceFile(sys3, dictionaryPath, "modify", "Field dictionary labels were updated."));
15936
+ if (nextTemplateContent !== undefined && nextTemplateContent !== null) {
15937
+ await sys3.writeFile(templatePath, nextTemplateContent);
15938
+ changedFiles.push(sourceFile(sys3, templatePath, "modify", "Template field surface was updated."));
15939
+ }
15426
15940
  }
15427
15941
  return createPrimitiveWriteReport({
15428
15942
  command: enumValues ? "add-enum-field" : "add-field",
@@ -15791,7 +16305,7 @@ class ScalarScript extends script("scalar", [ScalarRunner]) {
15791
16305
  // pkgs/@akanjs/cli/workflow/workflow.runner.ts
15792
16306
  import { mkdir as mkdir12, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
15793
16307
  import path42 from "path";
15794
- import { capitalize as capitalize12 } from "akanjs/common";
16308
+ import { capitalize as capitalize13 } from "akanjs/common";
15795
16309
 
15796
16310
  // pkgs/@akanjs/cli/workflows/shared.ts
15797
16311
  var sysInputs = {
@@ -15907,7 +16421,15 @@ var addFieldWorkflowSpec = {
15907
16421
  values: { type: "string-list", description: "Comma-separated enum values when type is enum." },
15908
16422
  default: {
15909
16423
  type: "string",
15910
- description: "Optional default value. apply_workflow coerces by field type: Int/Float to numeric literals, Boolean to true/false, String/enum/scalar to string literals; invalid numeric or boolean defaults fail before source is written."
16424
+ description: "Optional default value. plan/apply coerce by field type: Int/Float to numeric literals, Boolean to true/false, Date to new Date(...), String/scalar to string literals, and enum only when the value is in values."
16425
+ },
16426
+ surfaces: {
16427
+ type: "string-list",
16428
+ description: "Optional UI surfaces to update, comma-separated. Use template to auto-update simple generated Template forms; View/Unit stay planned as manual review."
16429
+ },
16430
+ includeInLight: {
16431
+ type: "boolean",
16432
+ description: "Whether to add the field to the Light<Model> projection used by list/card displays."
15911
16433
  }
15912
16434
  },
15913
16435
  optionalSurfaces: {
@@ -16430,7 +16952,7 @@ var planInputString2 = (plan2, key) => {
16430
16952
  var workflowPathsForPlanLike = (plan2) => {
16431
16953
  const app = planInputString2(plan2, "app");
16432
16954
  const module = planInputString2(plan2, "module");
16433
- const moduleClass = module ? capitalize12(module) : "<Module>";
16955
+ const moduleClass = module ? capitalize13(module) : "<Module>";
16434
16956
  return plan2.predictedChanges.map((change) => change.target.replace(/^\*\//, app ? `apps/${app}/` : "").replaceAll("<module>", module || "<module>").replaceAll("<Module>", moduleClass));
16435
16957
  };
16436
16958
  var workflowDiagnosticFromDoctor = (diagnostic, fallbackScope) => ({
@@ -16441,6 +16963,43 @@ var workflowDiagnosticFromDoctor = (diagnostic, fallbackScope) => ({
16441
16963
  failureScope: (diagnostic.scope ?? fallbackScope) === "baseline" ? "workspace-config" : (diagnostic.scope ?? fallbackScope) === "workflow" ? "source-change" : "unknown",
16442
16964
  context: diagnostic.context
16443
16965
  });
16966
+ var baselineBlockerCachePath = (workflow2) => `.akan/workflows/baseline/${workflow2}.json`;
16967
+ var blockerFingerprint = (blocker) => [blocker.failureScope, blocker.code, blocker.command ?? "", blocker.kind ?? "", blocker.message].join("|");
16968
+ var applyBaselineBlockerCache = async (workspace, report) => {
16969
+ const cacheableBlockers = report.knownBlockers.filter((blocker) => blocker.failureScope === "workspace-config" || blocker.failureScope === "environment");
16970
+ if (cacheableBlockers.length === 0)
16971
+ return report;
16972
+ const cachePath = baselineBlockerCachePath(report.workflow);
16973
+ const cached = (await workspace.exists(cachePath) ? await workspace.readJson(cachePath) : null) ?? { schemaVersion: 1, workflow: report.workflow, blockers: [] };
16974
+ const knownFingerprints = new Set(cached.blockers.map((blocker) => blocker.fingerprint));
16975
+ const now = new Date().toISOString();
16976
+ const blockers = new Map(cached.blockers.map((blocker) => [blocker.fingerprint, blocker]));
16977
+ for (const blocker of cacheableBlockers) {
16978
+ const fingerprint = blockerFingerprint(blocker);
16979
+ blockers.set(fingerprint, {
16980
+ fingerprint,
16981
+ code: blocker.code,
16982
+ message: blocker.message,
16983
+ failureScope: blocker.failureScope,
16984
+ command: blocker.command,
16985
+ kind: blocker.kind,
16986
+ lastSeenAt: now
16987
+ });
16988
+ }
16989
+ await workspace.writeFile(cachePath, jsonText({ schemaVersion: 1, workflow: report.workflow, blockers: [...blockers.values()] }), { silent: true });
16990
+ return {
16991
+ ...report,
16992
+ knownBlockers: report.knownBlockers.map((blocker) => {
16993
+ if (!knownFingerprints.has(blockerFingerprint(blocker)))
16994
+ return blocker;
16995
+ return {
16996
+ ...blocker,
16997
+ known: true,
16998
+ message: `Known baseline blocker, unrelated to this source change: ${blocker.message}`
16999
+ };
17000
+ })
17001
+ };
17002
+ };
16444
17003
 
16445
17004
  class WorkflowRunner extends runner("workflow") {
16446
17005
  list({ format = "markdown" } = {}) {
@@ -16542,7 +17101,7 @@ class WorkflowRunner extends runner("workflow") {
16542
17101
  runIdOrPlan,
16543
17102
  changedFiles: loaded.changedFiles
16544
17103
  });
16545
- const report = await createWorkflowValidationRunReport({
17104
+ const report = await applyBaselineBlockerCache(workspace, await createWorkflowValidationRunReport({
16546
17105
  workflow: loaded.plan?.workflow ?? loaded.workflow,
16547
17106
  source: loaded.source,
16548
17107
  plan: loaded.plan,
@@ -16552,7 +17111,7 @@ class WorkflowRunner extends runner("workflow") {
16552
17111
  baselineDiagnostics: (doctor.baselineDiagnostics ?? []).map((diagnostic) => workflowDiagnosticFromDoctor(diagnostic, "baseline")),
16553
17112
  workflowDiagnostics: (doctor.workflowDiagnostics ?? []).map((diagnostic) => workflowDiagnosticFromDoctor(diagnostic, "workflow")),
16554
17113
  repairActions: loaded.repairActions
16555
- });
17114
+ }));
16556
17115
  await writeWorkflowRunArtifact(workspace, report);
16557
17116
  return renderWorkflowValidation(report, format);
16558
17117
  }