@orcalang/orca-lang 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/compiler/dt-compiler.d.ts +3 -0
  2. package/dist/compiler/dt-compiler.d.ts.map +1 -1
  3. package/dist/compiler/dt-compiler.js +205 -1
  4. package/dist/compiler/dt-compiler.js.map +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +5 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/parser/ast-to-markdown.d.ts.map +1 -1
  9. package/dist/parser/ast-to-markdown.js +3 -1
  10. package/dist/parser/ast-to-markdown.js.map +1 -1
  11. package/dist/parser/ast.d.ts +1 -0
  12. package/dist/parser/ast.d.ts.map +1 -1
  13. package/dist/parser/markdown-parser.d.ts.map +1 -1
  14. package/dist/parser/markdown-parser.js +14 -4
  15. package/dist/parser/markdown-parser.js.map +1 -1
  16. package/dist/skills.d.ts +1 -0
  17. package/dist/skills.d.ts.map +1 -1
  18. package/dist/skills.js +287 -23
  19. package/dist/skills.js.map +1 -1
  20. package/dist/verifier/dt-verifier.d.ts +28 -1
  21. package/dist/verifier/dt-verifier.d.ts.map +1 -1
  22. package/dist/verifier/dt-verifier.js +332 -1
  23. package/dist/verifier/dt-verifier.js.map +1 -1
  24. package/dist/verifier/properties.d.ts +4 -0
  25. package/dist/verifier/properties.d.ts.map +1 -1
  26. package/dist/verifier/properties.js +56 -20
  27. package/dist/verifier/properties.js.map +1 -1
  28. package/dist/verifier/structural.d.ts.map +1 -1
  29. package/dist/verifier/structural.js +6 -1
  30. package/dist/verifier/structural.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/compiler/dt-compiler.ts +223 -1
  33. package/src/index.ts +5 -1
  34. package/src/parser/ast-to-markdown.ts +2 -1
  35. package/src/parser/ast.ts +1 -0
  36. package/src/parser/markdown-parser.ts +11 -3
  37. package/src/skills.ts +319 -23
  38. package/src/verifier/dt-verifier.ts +367 -1
  39. package/src/verifier/properties.ts +78 -23
  40. package/src/verifier/structural.ts +5 -1
package/src/skills.ts CHANGED
@@ -7,8 +7,15 @@ import { checkProperties } from './verifier/properties.js';
7
7
  import { compileToXState } from './compiler/xstate.js';
8
8
  import { compileToMermaid } from './compiler/mermaid.js';
9
9
  import { MachineDef, StateDef, GuardExpression, Type } from './parser/ast.js';
10
- import { verifyDecisionTable, verifyDecisionTables } from './verifier/dt-verifier.js';
11
- import { compileDecisionTableToTypeScript, compileDecisionTableToJSON } from './compiler/dt-compiler.js';
10
+ import { DecisionTableDef } from './parser/dt-ast.js';
11
+ import { verifyDecisionTable, verifyDecisionTables, checkFileContextAlignment, checkDTMachineIntegration, computeAlignedDTOutputDomain } from './verifier/dt-verifier.js';
12
+ import {
13
+ compileDecisionTableToTypeScript,
14
+ compileDecisionTableToPython,
15
+ compileDecisionTableToGo,
16
+ compileDecisionTableToJSON,
17
+ toSnakeCase,
18
+ } from './compiler/dt-compiler.js';
12
19
  import { loadConfig, resolveConfigOverrides } from './config/index.js';
13
20
  import { createProvider } from './llm/index.js';
14
21
  import type { LLMProvider } from './llm/index.js';
@@ -88,6 +95,7 @@ export interface GenerateActionsResult {
88
95
  machine: string;
89
96
  actions: ActionScaffold[];
90
97
  scaffolds: Record<string, string>;
98
+ decisionTableCode?: Record<string, string>; // DT name → compiled evaluator code
91
99
  tests?: Record<string, string>;
92
100
  }
93
101
 
@@ -247,8 +255,13 @@ export async function verifySkill(input: SkillInput): Promise<VerifySkillResult>
247
255
  const label = resolveLabel(input);
248
256
 
249
257
  let machine: MachineDef;
258
+ let fileDecisionTables: import('./parser/dt-ast.js').DecisionTableDef[] = [];
250
259
  try {
251
- machine = parseSource(label, source);
260
+ const { file } = parseMarkdown(source);
261
+ if (file.machines.length === 0) throw new Error(`${label} contains no machine definition.`);
262
+ if (file.machines.length > 1) throw new Error(`${label} contains multiple machines.`);
263
+ machine = file.machines[0];
264
+ fileDecisionTables = file.decisionTables;
252
265
  } catch (err) {
253
266
  // Parse error - return as verification error
254
267
  const message = err instanceof Error ? err.message : String(err);
@@ -270,15 +283,28 @@ export async function verifySkill(input: SkillInput): Promise<VerifySkillResult>
270
283
  const structural = checkStructural(machine);
271
284
  const completeness = checkCompleteness(machine);
272
285
  const determinism = checkDeterminism(machine);
273
- const properties = checkProperties(machine);
274
286
 
275
- const mapError = (e: { code: string; message: string; severity: 'error' | 'warning'; location?: { state?: string; event?: string }; suggestion?: string }): SkillError => ({
287
+ // Check co-located decision table alignment and machine integration (single-machine files only)
288
+ const orcaFile = { machines: [machine], decisionTables: fileDecisionTables };
289
+ const dtOutputDomain = fileDecisionTables.length > 0 ? computeAlignedDTOutputDomain(orcaFile) : undefined;
290
+ const properties = checkProperties(machine, { dtOutputDomain });
291
+ const dtAlignment = fileDecisionTables.length > 0
292
+ ? checkFileContextAlignment(orcaFile)
293
+ : [];
294
+ const dtIntegration = fileDecisionTables.length > 0
295
+ ? checkDTMachineIntegration(orcaFile)
296
+ : [];
297
+
298
+ const mapError = (e: { code: string; message: string; severity: 'error' | 'warning'; location?: { state?: string; event?: string; decisionTable?: string; condition?: string; action?: string }; suggestion?: string }): SkillError => ({
276
299
  code: e.code,
277
300
  message: e.message,
278
301
  severity: e.severity,
279
302
  location: e.location ? {
280
303
  state: e.location.state,
281
304
  event: e.location.event,
305
+ decisionTable: e.location.decisionTable,
306
+ condition: e.location.condition,
307
+ action: e.location.action,
282
308
  } : undefined,
283
309
  suggestion: e.suggestion,
284
310
  });
@@ -288,6 +314,8 @@ export async function verifySkill(input: SkillInput): Promise<VerifySkillResult>
288
314
  ...completeness.errors.map(mapError),
289
315
  ...determinism.errors.map(mapError),
290
316
  ...properties.errors.map(mapError),
317
+ ...dtAlignment.map(mapError),
318
+ ...dtIntegration.map(mapError),
291
319
  ];
292
320
 
293
321
  return {
@@ -347,7 +375,18 @@ export async function generateActionsSkill(
347
375
  generateTests: boolean = false
348
376
  ): Promise<GenerateActionsResult> {
349
377
  const source = resolveSource(input);
350
- const machine = parseSource(resolveLabel(input), source);
378
+
379
+ // Parse the full file to get both the machine and any co-located decision tables
380
+ const { file } = parseMarkdown(source);
381
+ const label = resolveLabel(input);
382
+ if (file.machines.length === 0) {
383
+ throw new Error(`${label} contains no machine definition.`);
384
+ }
385
+ if (file.machines.length > 1) {
386
+ throw new Error(`${label} contains multiple machines. Use a single-machine file for action generation.`);
387
+ }
388
+ const machine = file.machines[0];
389
+ const decisionTables = file.decisionTables;
351
390
 
352
391
  const actions: ActionScaffold[] = machine.actions.map(action => ({
353
392
  name: action.name,
@@ -359,6 +398,18 @@ export async function generateActionsSkill(
359
398
  context_used: extractContextFields(machine, action.name),
360
399
  }));
361
400
 
401
+ // Compile decision table evaluator code for each DT in the file
402
+ const decisionTableCode: Record<string, string> = {};
403
+ for (const dt of decisionTables) {
404
+ if (language === 'python') {
405
+ decisionTableCode[dt.name] = compileDecisionTableToPython(dt);
406
+ } else if (language === 'go') {
407
+ decisionTableCode[dt.name] = compileDecisionTableToGo(dt);
408
+ } else {
409
+ decisionTableCode[dt.name] = compileDecisionTableToTypeScript(dt);
410
+ }
411
+ }
412
+
362
413
  let scaffolds: Record<string, string> = {};
363
414
  let tests: Record<string, string> = {};
364
415
 
@@ -373,7 +424,7 @@ export async function generateActionsSkill(
373
424
  temperature: config.temperature,
374
425
  });
375
426
 
376
- scaffolds = await generateWithLLM(provider, actions, machine, language as CodeGeneratorType);
427
+ scaffolds = await generateWithLLM(provider, actions, machine, language as CodeGeneratorType, decisionTables);
377
428
 
378
429
  if (generateTests) {
379
430
  tests = await generateUnitTests(provider, actions, machine, language as CodeGeneratorType);
@@ -381,7 +432,13 @@ export async function generateActionsSkill(
381
432
  } else {
382
433
  // Use template-based scaffold generation
383
434
  for (const action of machine.actions) {
384
- scaffolds[action.name] = generateActionScaffold(action, machine, language);
435
+ const matchedDT = findMatchingDT(action.name, decisionTables);
436
+ if (matchedDT && !action.hasEffect && isDTFullyAligned(matchedDT, machine)) {
437
+ // All DT conditions and outputs exist in context — generate fully wired code
438
+ scaffolds[action.name] = generateFullyWiredActionScaffold(action, machine, language, matchedDT);
439
+ } else {
440
+ scaffolds[action.name] = generateActionScaffold(action, machine, language, matchedDT ?? undefined);
441
+ }
385
442
  }
386
443
 
387
444
  if (generateTests) {
@@ -394,6 +451,7 @@ export async function generateActionsSkill(
394
451
  machine: machine.name,
395
452
  actions,
396
453
  scaffolds,
454
+ decisionTableCode: Object.keys(decisionTableCode).length > 0 ? decisionTableCode : undefined,
397
455
  tests: Object.keys(tests).length > 0 ? tests : undefined,
398
456
  };
399
457
  }
@@ -402,22 +460,35 @@ async function generateWithLLM(
402
460
  provider: LLMProvider,
403
461
  actions: ActionScaffold[],
404
462
  machine: MachineDef,
405
- language: CodeGeneratorType
463
+ language: CodeGeneratorType,
464
+ decisionTables: DecisionTableDef[] = []
406
465
  ): Promise<Record<string, string>> {
407
466
  const generator = getCodeGenerator(language);
408
467
  const scaffolds: Record<string, string> = {};
409
468
 
469
+ const dtContext = decisionTables.length > 0
470
+ ? `\nDecision tables available:\n${decisionTables.map(dt =>
471
+ `- ${dt.name}: conditions=[${dt.conditions.map(c => c.name).join(', ')}] outputs=[${dt.actions.map(a => a.name).join(', ')}]`
472
+ ).join('\n')}`
473
+ : '';
474
+
410
475
  const systemPrompt = `You are an expert ${language} developer specializing in state machine action implementations.
411
476
  Given a machine definition and action signatures, generate complete action implementations.
412
477
  Follow the type signatures exactly. Use the provided context fields.
413
- If an action has an effect, return [newContext, effect] tuple.`;
478
+ If an action has an effect, return [newContext, effect] tuple.
479
+ If decision tables are listed, use their evaluator functions (e.g. evaluate${language === 'go' ? 'DtName' : 'DtName'}) when appropriate.`;
414
480
 
415
481
  for (const action of actions) {
482
+ const matchedDT = findMatchingDT(action.name, decisionTables);
483
+ const dtHint = matchedDT
484
+ ? `\nThis action should use the ${matchedDT.name} decision table evaluator.`
485
+ : '';
486
+
416
487
  const userPrompt = `Machine: ${machine.name}
417
- Context fields: ${machine.context.map(f => `${f.name}: ${f.type || 'unknown'}`).join(', ')}
488
+ Context fields: ${machine.context.map(f => `${f.name}: ${f.type || 'unknown'}`).join(', ')}${dtContext}
418
489
 
419
490
  Action: ${action.signature}
420
- Description: ${action.name}${action.hasEffect ? ` (effect type: ${action.effectType})` : ''}
491
+ Description: ${action.name}${action.hasEffect ? ` (effect type: ${action.effectType})` : ''}${dtHint}
421
492
 
422
493
  Generate the implementation:`;
423
494
 
@@ -436,7 +507,7 @@ Generate the implementation:`;
436
507
  } catch (err) {
437
508
  console.error(`LLM error for action ${action.name}: ${err instanceof Error ? err.message : String(err)}`);
438
509
  // Fall back to scaffold on error
439
- scaffolds[action.name] = generateActionScaffold(action, machine, language);
510
+ scaffolds[action.name] = generateActionScaffold(action, machine, language, matchedDT ?? undefined);
440
511
  }
441
512
  }
442
513
 
@@ -779,10 +850,222 @@ function toPascalCase(snake: string): string {
779
850
  return snake.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('');
780
851
  }
781
852
 
853
+ /**
854
+ * Find a decision table whose name tokens overlap with the action name tokens.
855
+ * E.g. action "apply_routing_decision" matches DT "PaymentRouting" via "routing".
856
+ */
857
+ function findMatchingDT(actionName: string, dts: DecisionTableDef[]): DecisionTableDef | null {
858
+ if (dts.length === 0) return null;
859
+ const actionTokens = new Set(
860
+ actionName.toLowerCase().split('_').filter(t => t.length > 2)
861
+ );
862
+ for (const dt of dts) {
863
+ const dtTokens = dt.name
864
+ .replace(/([A-Z])/g, ' $1')
865
+ .trim()
866
+ .toLowerCase()
867
+ .split(/\s+/)
868
+ .filter(t => t.length > 2);
869
+ if (dtTokens.some(t => actionTokens.has(t))) return dt;
870
+ }
871
+ return null;
872
+ }
873
+
874
+ /**
875
+ * Generate a commented example DT call block to include in an action stub.
876
+ */
877
+ function generateDTCallComment(dt: DecisionTableDef, language: string): string {
878
+ const dtName = dt.name;
879
+
880
+ if (language === 'python') {
881
+ const fnName = `evaluate_${toSnakeCase(dtName)}`;
882
+ const inputClass = `${toPascalCase(dtName)}Input`;
883
+ const inputArgs = dt.conditions.map(c => {
884
+ const hint = c.type === 'enum' && c.values.length > 0
885
+ ? `str (${c.values.join(', ')})`
886
+ : c.type === 'bool' ? 'bool' : 'str';
887
+ return ` # ${c.name}=..., # ${hint} — map from ctx`;
888
+ }).join('\n');
889
+ const outputFields = dt.actions.map(a =>
890
+ ` # # dt_result.${a.name} → ctx['${a.name}']`
891
+ ).join('\n');
892
+ return [
893
+ ` # Call ${fnName} to evaluate ${dtName} rules:`,
894
+ ` # dt_result = ${fnName}(${inputClass}(`,
895
+ inputArgs,
896
+ ` # ))`,
897
+ ` # if dt_result is not None:`,
898
+ outputFields,
899
+ ].join('\n');
900
+ }
901
+
902
+ if (language === 'go') {
903
+ const fnName = `Evaluate${toPascalCase(dtName)}`;
904
+ const inputStruct = `${toPascalCase(dtName)}Input`;
905
+ const inputArgs = dt.conditions.map(c => {
906
+ const goField = toPascalCase(c.name);
907
+ const hint = c.type === 'enum' && c.values.length > 0
908
+ ? `string (${c.values.join(', ')})`
909
+ : c.type === 'bool' ? 'bool' : 'string';
910
+ return `\t// \t${goField}: ..., // ${hint} — map from ctx`;
911
+ }).join('\n');
912
+ const outputFields = dt.actions.map(a =>
913
+ `\t// \tresult["${a.name}"] = dtResult.${toPascalCase(a.name)}`
914
+ ).join('\n');
915
+ return [
916
+ `\t// Call ${fnName} to evaluate ${dtName} rules:`,
917
+ `\t// dtResult := ${fnName}(${inputStruct}{`,
918
+ inputArgs,
919
+ `\t// })`,
920
+ `\t// if dtResult != nil {`,
921
+ outputFields,
922
+ `\t// }`,
923
+ ].join('\n');
924
+ }
925
+
926
+ // TypeScript (default)
927
+ const fnName = `evaluate${toPascalCase(dtName)}`;
928
+ const inputType = `${toPascalCase(dtName)}Input`;
929
+ const inputArgs = dt.conditions.map(c => {
930
+ const hint = c.type === 'enum' && c.values.length > 0
931
+ ? `enum: ${c.values.join(', ')}`
932
+ : c.type;
933
+ return ` // ${c.name}: /* ctx.? */ as ${inputType}['${c.name}'], // ${hint} — map from ctx`;
934
+ }).join('\n');
935
+ const outputFields = dt.actions.map(a =>
936
+ ` // ${a.name}: dtResult.${a.name},`
937
+ ).join('\n');
938
+ return [
939
+ ` // Call ${fnName} to evaluate ${dtName} rules:`,
940
+ ` // const dtResult = ${fnName}({`,
941
+ inputArgs,
942
+ ` // });`,
943
+ ` // if (dtResult !== null) {`,
944
+ ` // return { ...ctx,`,
945
+ outputFields,
946
+ ` // };`,
947
+ ` // }`,
948
+ ].join('\n');
949
+ }
950
+
951
+ /**
952
+ * Returns true when every DT condition name and output name exists as a
953
+ * context field in the machine — the full co-location contract is satisfied.
954
+ */
955
+ function isDTFullyAligned(dt: DecisionTableDef, machine: MachineDef): boolean {
956
+ const contextNames = new Set(machine.context.map(f => f.name));
957
+ return dt.conditions.every(c => contextNames.has(c.name)) &&
958
+ dt.actions.every(a => contextNames.has(a.name));
959
+ }
960
+
961
+ /** Generate a Go type assertion for reading a context value. */
962
+ function goCtxRead(fieldName: string, condType: string): string {
963
+ if (condType === 'bool') return `ctx["${fieldName}"].(bool)`;
964
+ if (condType === 'int_range') return `ctx["${fieldName}"].(int)`;
965
+ return `ctx["${fieldName}"].(string)`;
966
+ }
967
+
968
+ function generateFullyWiredActionScaffold(
969
+ action: { name: string; parameters: string[]; returnType: string; hasEffect: boolean; effectType?: string },
970
+ machine: MachineDef,
971
+ language: string,
972
+ dt: DecisionTableDef
973
+ ): string {
974
+ if (language === 'python') {
975
+ const dtFnName = `evaluate_${toSnakeCase(dt.name)}`;
976
+ const inputClass = `${toPascalCase(dt.name)}Input`;
977
+ const inputArgs = dt.conditions
978
+ .map(c => ` ${c.name}=ctx['${c.name}'],`)
979
+ .join('\n');
980
+ const outputAssigns = dt.actions
981
+ .map(a => ` '${a.name}': dt_result.${a.name},`)
982
+ .join('\n');
983
+ return `# Action: ${action.name}
984
+ # Decision table: ${dt.name}
985
+ # Register via: machine.register_action("${action.name}", ${action.name})
986
+
987
+ from typing import Any
988
+
989
+ async def ${action.name}(ctx: dict[str, Any], event: Any = None) -> dict[str, Any]:
990
+ dt_result = ${dtFnName}(${inputClass}(
991
+ ${inputArgs}
992
+ ))
993
+ if dt_result is not None:
994
+ return {**ctx,
995
+ ${outputAssigns}
996
+ }
997
+ return dict(ctx)
998
+ `;
999
+ }
1000
+
1001
+ if (language === 'go') {
1002
+ const fnName = toPascalCase(action.name);
1003
+ const dtFnName = `Evaluate${toPascalCase(dt.name)}`;
1004
+ const inputStruct = `${toPascalCase(dt.name)}Input`;
1005
+ const ctxReads = dt.conditions.map(c => {
1006
+ const varName = toPascalCase(c.name).charAt(0).toLowerCase() + toPascalCase(c.name).slice(1);
1007
+ return `\t${varName}, _ := ${goCtxRead(c.name, c.type)}`;
1008
+ }).join('\n');
1009
+ const inputFields = dt.conditions.map(c => {
1010
+ const goField = toPascalCase(c.name);
1011
+ const varName = goField.charAt(0).toLowerCase() + goField.slice(1);
1012
+ return `\t\t${goField}: ${varName},`;
1013
+ }).join('\n');
1014
+ const outputAssigns = dt.actions
1015
+ .map(a => `\t\tresult["${a.name}"] = dtResult.${toPascalCase(a.name)}`)
1016
+ .join('\n');
1017
+ return `// Action: ${action.name}
1018
+ // Decision table: ${dt.name}
1019
+ // Register via: machine.RegisterAction("${action.name}", ${fnName})
1020
+
1021
+ func ${fnName}(ctx orca.Context, event map[string]any) map[string]any {
1022
+ ${ctxReads}
1023
+ \tdtResult := ${dtFnName}(${inputStruct}{
1024
+ ${inputFields}
1025
+ \t})
1026
+ \tresult := make(orca.Context)
1027
+ \tfor k, v := range ctx {
1028
+ \t\tresult[k] = v
1029
+ \t}
1030
+ \tif dtResult != nil {
1031
+ ${outputAssigns}
1032
+ \t}
1033
+ \treturn result
1034
+ }
1035
+ `;
1036
+ }
1037
+
1038
+ // TypeScript (default)
1039
+ const dtFnName = `evaluate${toPascalCase(dt.name)}`;
1040
+ const inputArgs = dt.conditions
1041
+ .map(c => ` ${c.name}: ctx.${c.name},`)
1042
+ .join('\n');
1043
+ const outputAssigns = dt.actions
1044
+ .map(a => ` ${a.name}: dtResult.${a.name},`)
1045
+ .join('\n');
1046
+ return `// Action: ${action.name}
1047
+ // Decision table: ${dt.name}
1048
+
1049
+ export function ${action.name}(ctx: Context): Context {
1050
+ const dtResult = ${dtFnName}({
1051
+ ${inputArgs}
1052
+ });
1053
+ if (dtResult !== null) {
1054
+ return {
1055
+ ...ctx,
1056
+ ${outputAssigns}
1057
+ };
1058
+ }
1059
+ return { ...ctx };
1060
+ }
1061
+ `;
1062
+ }
1063
+
782
1064
  function generateActionScaffold(
783
1065
  action: { name: string; parameters: string[]; returnType: string; hasEffect: boolean; effectType?: string },
784
1066
  machine: MachineDef,
785
- language: string
1067
+ language: string,
1068
+ matchedDT?: DecisionTableDef
786
1069
  ): string {
787
1070
  if (language === 'python') {
788
1071
  if (action.hasEffect) {
@@ -800,13 +1083,17 @@ async def ${action.name}(effect: Effect) -> EffectResult:
800
1083
  return EffectResult(status=EffectStatus.SUCCESS, data={})
801
1084
  `;
802
1085
  }
803
- return `# Action: ${action.name}
1086
+ const pyDTComment = matchedDT ? '\n' + generateDTCallComment(matchedDT, 'python') + '\n' : '';
1087
+ const pyTodo = matchedDT
1088
+ ? ` # TODO: Implement action using ${matchedDT.name} decision table`
1089
+ : ' # TODO: Implement action';
1090
+ return `# Action: ${action.name}${matchedDT ? `\n# Decision table: ${matchedDT.name}` : ''}
804
1091
  # Register via: machine.register_action("${action.name}", ${action.name})
805
1092
 
806
1093
  from typing import Any
807
1094
 
808
- async def ${action.name}(ctx: dict[str, Any], event: Any = None) -> dict[str, Any]:
809
- # TODO: Implement action
1095
+ async def ${action.name}(ctx: dict[str, Any], event: Any = None) -> dict[str, Any]:${pyDTComment}
1096
+ ${pyTodo}
810
1097
  return dict(ctx)
811
1098
  `;
812
1099
  }
@@ -827,11 +1114,15 @@ func ${fnName}(effect orca.Effect) orca.EffectResult {
827
1114
  }
828
1115
  `;
829
1116
  }
830
- return `// Action: ${action.name}
1117
+ const goDTComment = matchedDT ? '\n' + generateDTCallComment(matchedDT, 'go') + '\n' : '';
1118
+ const goTodo = matchedDT
1119
+ ? `\t// TODO: Implement action using ${matchedDT.name} decision table`
1120
+ : '\t// TODO: Implement action';
1121
+ return `// Action: ${action.name}${matchedDT ? `\n// Decision table: ${matchedDT.name}` : ''}
831
1122
  // Register via: machine.RegisterAction("${action.name}", ${fnName})
832
1123
 
833
- func ${fnName}(ctx orca.Context, event map[string]any) map[string]any {
834
- \t// TODO: Implement action
1124
+ func ${fnName}(ctx orca.Context, event map[string]any) map[string]any {${goDTComment}
1125
+ ${goTodo}
835
1126
  \tresult := make(orca.Context)
836
1127
  \tfor k, v := range ctx {
837
1128
  \t\tresult[k] = v
@@ -863,10 +1154,15 @@ export function ${action.name}(${params}): [Context, Effect<${action.effectType}
863
1154
  // }
864
1155
  `;
865
1156
  }
866
- return `// Action: ${action.name}
867
1157
 
868
- export function ${action.name}(ctx: Context): Context {
869
- // TODO: Implement action
1158
+ const tsDTComment = matchedDT ? '\n' + generateDTCallComment(matchedDT, 'typescript') + '\n' : '';
1159
+ const tsTodo = matchedDT
1160
+ ? ` // TODO: Implement action using ${matchedDT.name} decision table`
1161
+ : ' // TODO: Implement action';
1162
+ return `// Action: ${action.name}${matchedDT ? `\n// Decision table: ${matchedDT.name}` : ''}
1163
+
1164
+ export function ${action.name}(ctx: Context): Context {${tsDTComment}
1165
+ ${tsTodo}
870
1166
  return { ...ctx };
871
1167
  }
872
1168
  `;