@agentteams/runner 0.0.69-dev.141 → 0.0.69-dev.144

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.
@@ -36,6 +36,7 @@ const runtime = {
36
36
  apiKey: "api-key",
37
37
  teamId: "team-1",
38
38
  projectId: "project-1",
39
+ runnerPrompt: "API runner prompt\n- History path: {{AGENTRUNNER_CURRENT_HISTORY_PATH}}\n- Previous history path: {{AGENTRUNNER_PARENT_HISTORY_PATH}}",
39
40
  parentHistoryMarkdown: null,
40
41
  useWorktree: false,
41
42
  baseBranch: null,
@@ -108,10 +109,7 @@ test("createTriggerHandler runs the runner, reports history, and marks success",
108
109
  await handler(trigger);
109
110
  assert.deepEqual(discoveredAuthPaths, ["/auth/path"]);
110
111
  assert.equal(runnerInputs.length, 1);
111
- assert.match(runnerInputs[0]?.prompt ?? "", /Continuation context \(required\)/);
112
- assert.match(runnerInputs[0]?.prompt ?? "", /Previous history path: \/auth\/path\/\.agentteams\/runner\/history\/parent-1\.md/);
113
- assert.match(runnerInputs[0]?.prompt ?? "", /## Requests/);
114
- assert.match(runnerInputs[0]?.prompt ?? "", /## History \(latest\)/);
112
+ assert.equal(runnerInputs[0]?.prompt, "API runner prompt\n- History path: /auth/path/.agentteams/runner/history/trigger-1.md\n- Previous history path: /auth/path/.agentteams/runner/history/parent-1.md");
115
113
  assert.equal(logEntries.some((entry) => entry.level === "INFO" && entry.message.includes("stdout")), true);
116
114
  assert.equal(logEntries.some((entry) => entry.level === "WARN" && entry.message.includes("stderr")), true);
117
115
  assert.deepEqual(clientCalls.map((entry) => entry.method), [
@@ -563,25 +561,22 @@ const withTempDir = async (run) => {
563
561
  await rm(dir, { recursive: true, force: true });
564
562
  }
565
563
  };
564
+ const withHarnessRuntime = (authPath, overrides = {}) => ({
565
+ ...runtime,
566
+ authPath,
567
+ harnessConfigId: "harness-1",
568
+ ...overrides,
569
+ });
570
+ const fetchServerHarness = (config) => async () => ({ config });
566
571
  test("createTriggerHandler blocks trigger when pre-hook fails with onFailure=fail", async () => {
567
572
  await withTempDir(async (dir) => {
568
- // Write harness.yml with a failing pre-hook
569
- const harnessDir = join(dir, ".agentteams");
570
- await mkdir(harnessDir, { recursive: true });
571
- await writeFile(join(harnessDir, "harness.yml"), [
572
- "preHooks:",
573
- " - name: lint-check",
574
- " command: exit 1",
575
- " onFailure: fail",
576
- ].join("\n"));
577
573
  const clientCalls = [];
578
574
  let runnerCalled = false;
579
575
  const client = {
580
- fetchTriggerRuntime: async () => ({
581
- ...runtime,
582
- authPath: dir,
576
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir),
577
+ fetchHarnessConfigById: fetchServerHarness({
578
+ preHooks: [{ name: "lint-check", command: "exit 1", onFailure: "fail" }],
583
579
  }),
584
- fetchHarnessConfig: async () => null,
585
580
  isTriggerCancelRequested: async () => false,
586
581
  updateTriggerHistory: async (...args) => {
587
582
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -628,22 +623,13 @@ test("createTriggerHandler blocks trigger when pre-hook fails with onFailure=fai
628
623
  });
629
624
  test("createTriggerHandler continues when pre-hook fails with onFailure=warn", async () => {
630
625
  await withTempDir(async (dir) => {
631
- const harnessDir = join(dir, ".agentteams");
632
- await mkdir(harnessDir, { recursive: true });
633
- await writeFile(join(harnessDir, "harness.yml"), [
634
- "preHooks:",
635
- " - name: optional-check",
636
- " command: exit 1",
637
- " onFailure: warn",
638
- ].join("\n"));
639
626
  let runnerCalled = false;
640
627
  const clientCalls = [];
641
628
  const client = {
642
- fetchTriggerRuntime: async () => ({
643
- ...runtime,
644
- authPath: dir,
629
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir),
630
+ fetchHarnessConfigById: fetchServerHarness({
631
+ preHooks: [{ name: "optional-check", command: "exit 1", onFailure: "warn" }],
645
632
  }),
646
- fetchHarnessConfig: async () => null,
647
633
  isTriggerCancelRequested: async () => false,
648
634
  updateTriggerHistory: async (...args) => {
649
635
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -689,15 +675,10 @@ test("createTriggerHandler continues when pre-hook fails with onFailure=warn", a
689
675
  });
690
676
  test("createTriggerHandler runs normally when no pre-hooks are defined", async () => {
691
677
  await withTempDir(async (dir) => {
692
- // No harness.yml — should behave exactly as before
693
678
  let runnerCalled = false;
694
679
  const clientCalls = [];
695
680
  const client = {
696
- fetchTriggerRuntime: async () => ({
697
- ...runtime,
698
- authPath: dir,
699
- }),
700
- fetchHarnessConfig: async () => null,
681
+ fetchTriggerRuntime: async () => ({ ...runtime, authPath: dir }),
701
682
  isTriggerCancelRequested: async () => false,
702
683
  updateTriggerHistory: async (...args) => {
703
684
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -741,26 +722,76 @@ test("createTriggerHandler runs normally when no pre-hooks are defined", async (
741
722
  assert.equal(statusCall.args[1], "DONE");
742
723
  });
743
724
  });
744
- test("createTriggerHandler always runs pre-hooks without a convention trigger", async () => {
725
+ test("createTriggerHandler ignores local harness file when no server harness is selected", async () => {
745
726
  await withTempDir(async (dir) => {
746
727
  const harnessDir = join(dir, ".agentteams");
747
728
  await mkdir(harnessDir, { recursive: true });
748
729
  await writeFile(join(harnessDir, "harness.yml"), [
749
730
  "preHooks:",
750
- " - name: always-run",
751
- " command: sh -c 'echo always > always.txt && exit 1'",
731
+ " - name: local-should-not-run",
732
+ " command: sh -c 'echo local > local.txt && exit 1'",
752
733
  " onFailure: fail",
753
734
  ].join("\n"));
754
735
  let runnerCalled = false;
755
736
  const clientCalls = [];
756
737
  const client = {
757
- fetchTriggerRuntime: async () => ({
758
- ...runtime,
759
- authPath: dir,
738
+ fetchTriggerRuntime: async () => ({ ...runtime, authPath: dir, harnessConfigId: null }),
739
+ isTriggerCancelRequested: async () => false,
740
+ updateTriggerHistory: async (...args) => {
741
+ clientCalls.push({ method: "updateTriggerHistory", args });
742
+ },
743
+ updateTriggerStatus: async (...args) => {
744
+ clientCalls.push({ method: "updateTriggerStatus", args });
745
+ },
746
+ };
747
+ const handler = createTriggerHandler({
748
+ config: {
749
+ daemonToken: "daemon-token",
750
+ apiUrl: "https://api.example",
751
+ pollingIntervalMs: 5000,
752
+ timeoutMs: 1500,
753
+ idleTimeoutMs: 500,
754
+ runnerCmd: "opencode",
755
+ },
756
+ client: client,
757
+ }, {
758
+ createRunnerFactory: () => () => ({
759
+ run: async () => {
760
+ runnerCalled = true;
761
+ return { exitCode: 0 };
762
+ },
763
+ }),
764
+ createLogReporter: () => ({
765
+ start: () => undefined,
766
+ append: () => undefined,
767
+ stop: async () => undefined,
768
+ }),
769
+ readHistoryFile: async () => "### Summary\n- done\n",
770
+ resolveRunnerHistoryPaths: () => ({
771
+ currentHistoryPath: join(dir, ".agentteams/runner/history/trigger-1.md"),
772
+ parentHistoryPath: null,
773
+ }),
774
+ });
775
+ await handler({ ...trigger, parentTriggerId: null });
776
+ assert.equal(runnerCalled, true);
777
+ await assert.rejects(access(join(dir, "local.txt")));
778
+ const statusCall = clientCalls.find((c) => c.method === "updateTriggerStatus");
779
+ assert.ok(statusCall);
780
+ assert.equal(statusCall.args[1], "DONE");
781
+ });
782
+ });
783
+ test("createTriggerHandler always runs pre-hooks without a convention trigger", async () => {
784
+ await withTempDir(async (dir) => {
785
+ let runnerCalled = false;
786
+ const clientCalls = [];
787
+ const client = {
788
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
760
789
  conventions: [],
761
790
  planType: "BUG_FIX",
762
791
  }),
763
- fetchHarnessConfig: async () => null,
792
+ fetchHarnessConfigById: fetchServerHarness({
793
+ preHooks: [{ name: "always-run", command: "sh -c 'echo always > always.txt && exit 1'", onFailure: "fail" }],
794
+ }),
764
795
  isTriggerCancelRequested: async () => false,
765
796
  updateTriggerHistory: async (...args) => {
766
797
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -808,15 +839,6 @@ test("createTriggerHandler always runs pre-hooks without a convention trigger",
808
839
  });
809
840
  test("createTriggerHandler runs convention-linked pre-hooks when the convention matches", async () => {
810
841
  await withTempDir(async (dir) => {
811
- const harnessDir = join(dir, ".agentteams");
812
- await mkdir(harnessDir, { recursive: true });
813
- await writeFile(join(harnessDir, "harness.yml"), [
814
- "preHooks:",
815
- " - name: only-on-bugfix",
816
- " command: sh -c 'echo bugfix > bugfix.txt && exit 1'",
817
- " onFailure: fail",
818
- " conventionTrigger: task:BUG_FIX",
819
- ].join("\n"));
820
842
  let runnerCalled = false;
821
843
  const clientCalls = [];
822
844
  const conventions = [{
@@ -827,13 +849,18 @@ test("createTriggerHandler runs convention-linked pre-hooks when the convention
827
849
  description: "Rules for bug fix tasks",
828
850
  }];
829
851
  const client = {
830
- fetchTriggerRuntime: async () => ({
831
- ...runtime,
832
- authPath: dir,
852
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
833
853
  conventions,
834
854
  planType: "BUG_FIX",
835
855
  }),
836
- fetchHarnessConfig: async () => null,
856
+ fetchHarnessConfigById: fetchServerHarness({
857
+ preHooks: [{
858
+ name: "only-on-bugfix",
859
+ command: "sh -c 'echo bugfix > bugfix.txt && exit 1'",
860
+ onFailure: "fail",
861
+ conventionTrigger: "task:BUG_FIX",
862
+ }],
863
+ }),
837
864
  isTriggerCancelRequested: async () => false,
838
865
  updateTriggerHistory: async (...args) => {
839
866
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -881,15 +908,6 @@ test("createTriggerHandler runs convention-linked pre-hooks when the convention
881
908
  });
882
909
  test("createTriggerHandler skips convention-linked pre-hooks when the convention does not match", async () => {
883
910
  await withTempDir(async (dir) => {
884
- const harnessDir = join(dir, ".agentteams");
885
- await mkdir(harnessDir, { recursive: true });
886
- await writeFile(join(harnessDir, "harness.yml"), [
887
- "preHooks:",
888
- " - name: only-on-feature",
889
- " command: sh -c 'echo feature > feature.txt && exit 1'",
890
- " onFailure: fail",
891
- " conventionTrigger: task:FEATURE",
892
- ].join("\n"));
893
911
  let runnerCalled = false;
894
912
  const clientCalls = [];
895
913
  const conventions = [{
@@ -900,13 +918,18 @@ test("createTriggerHandler skips convention-linked pre-hooks when the convention
900
918
  description: "Rules for bug fix tasks",
901
919
  }];
902
920
  const client = {
903
- fetchTriggerRuntime: async () => ({
904
- ...runtime,
905
- authPath: dir,
921
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
906
922
  conventions,
907
923
  planType: "BUG_FIX",
908
924
  }),
909
- fetchHarnessConfig: async () => null,
925
+ fetchHarnessConfigById: fetchServerHarness({
926
+ preHooks: [{
927
+ name: "only-on-feature",
928
+ command: "sh -c 'echo feature > feature.txt && exit 1'",
929
+ onFailure: "fail",
930
+ conventionTrigger: "task:FEATURE",
931
+ }],
932
+ }),
910
933
  isTriggerCancelRequested: async () => false,
911
934
  updateTriggerHistory: async (...args) => {
912
935
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -953,18 +976,6 @@ test("createTriggerHandler skips convention-linked pre-hooks when the convention
953
976
  });
954
977
  test("createTriggerHandler runs unconditional hooks while skipping non-matching conditional hooks", async () => {
955
978
  await withTempDir(async (dir) => {
956
- const harnessDir = join(dir, ".agentteams");
957
- await mkdir(harnessDir, { recursive: true });
958
- await writeFile(join(harnessDir, "harness.yml"), [
959
- "preHooks:",
960
- " - name: always-run",
961
- " command: sh -c 'echo always > always.txt'",
962
- " onFailure: warn",
963
- " - name: only-on-feature",
964
- " command: sh -c 'echo feature > feature.txt && exit 1'",
965
- " onFailure: fail",
966
- " conventionTrigger: task:FEATURE",
967
- ].join("\n"));
968
979
  let runnerCalled = false;
969
980
  const clientCalls = [];
970
981
  const conventions = [{
@@ -975,13 +986,21 @@ test("createTriggerHandler runs unconditional hooks while skipping non-matching
975
986
  description: "Rules for bug fix tasks",
976
987
  }];
977
988
  const client = {
978
- fetchTriggerRuntime: async () => ({
979
- ...runtime,
980
- authPath: dir,
989
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
981
990
  conventions,
982
991
  planType: "BUG_FIX",
983
992
  }),
984
- fetchHarnessConfig: async () => null,
993
+ fetchHarnessConfigById: fetchServerHarness({
994
+ preHooks: [
995
+ { name: "always-run", command: "sh -c 'echo always > always.txt'", onFailure: "warn" },
996
+ {
997
+ name: "only-on-feature",
998
+ command: "sh -c 'echo feature > feature.txt && exit 1'",
999
+ onFailure: "fail",
1000
+ conventionTrigger: "task:FEATURE",
1001
+ },
1002
+ ],
1003
+ }),
985
1004
  isTriggerCancelRequested: async () => false,
986
1005
  updateTriggerHistory: async (...args) => {
987
1006
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -1027,24 +1046,15 @@ test("createTriggerHandler runs unconditional hooks while skipping non-matching
1027
1046
  assert.equal(statusCall.args[1], "DONE");
1028
1047
  });
1029
1048
  });
1030
- test("createTriggerHandler injects context-matched conventions into the runner prompt", async () => {
1049
+ test("createTriggerHandler passes the API-provided runner prompt unchanged", async () => {
1031
1050
  await withTempDir(async (dir) => {
1032
1051
  const runnerInputs = [];
1033
- const conventions = [{
1034
- id: "conv-bugfix",
1035
- filePath: ".agentteams/rules/bugfix.md",
1036
- trigger: "task:BUG_FIX",
1037
- title: "Bug Fix Convention",
1038
- description: "Rules for bug fix tasks",
1039
- }];
1040
1052
  const client = {
1041
1053
  fetchTriggerRuntime: async () => ({
1042
1054
  ...runtime,
1043
1055
  authPath: dir,
1044
- conventions,
1045
- planType: "BUG_FIX",
1056
+ runnerPrompt: "Prompt generated by API\n\nDo exactly this.",
1046
1057
  }),
1047
- fetchHarnessConfig: async () => null,
1048
1058
  isTriggerCancelRequested: async () => false,
1049
1059
  updateTriggerHistory: async () => undefined,
1050
1060
  updateTriggerStatus: async () => undefined,
@@ -1079,12 +1089,10 @@ test("createTriggerHandler injects context-matched conventions into the runner p
1079
1089
  });
1080
1090
  await handler({ ...trigger, parentTriggerId: null });
1081
1091
  assert.equal(runnerInputs.length, 1);
1082
- assert.match(runnerInputs[0]?.prompt ?? "", /\[IMPORTANT Convention Reference \(MUST READ\)\]/);
1083
- assert.match(runnerInputs[0]?.prompt ?? "", /\[Context-Matched Conventions \(AUTO-LOADED\)\]/);
1084
- assert.match(runnerInputs[0]?.prompt ?? "", /`\.agentteams\/rules\/bugfix\.md` — Rules for bug fix tasks/);
1092
+ assert.equal(runnerInputs[0]?.prompt, "Prompt generated by API\n\nDo exactly this.");
1085
1093
  });
1086
1094
  });
1087
- test("createTriggerHandler omits auto-loaded convention prompt section when no conventions match", async () => {
1095
+ test("createTriggerHandler does not append history or convention text to the API prompt", async () => {
1088
1096
  await withTempDir(async (dir) => {
1089
1097
  const runnerInputs = [];
1090
1098
  const conventions = [{
@@ -1100,8 +1108,8 @@ test("createTriggerHandler omits auto-loaded convention prompt section when no c
1100
1108
  authPath: dir,
1101
1109
  conventions,
1102
1110
  planType: "BUG_FIX",
1111
+ runnerPrompt: "Only the API prompt",
1103
1112
  }),
1104
- fetchHarnessConfig: async () => null,
1105
1113
  isTriggerCancelRequested: async () => false,
1106
1114
  updateTriggerHistory: async () => undefined,
1107
1115
  updateTriggerStatus: async () => undefined,
@@ -1136,7 +1144,7 @@ test("createTriggerHandler omits auto-loaded convention prompt section when no c
1136
1144
  });
1137
1145
  await handler({ ...trigger, parentTriggerId: null });
1138
1146
  assert.equal(runnerInputs.length, 1);
1139
- assert.match(runnerInputs[0]?.prompt ?? "", /\[IMPORTANT Convention Reference \(MUST READ\)\]/);
1147
+ assert.equal(runnerInputs[0]?.prompt, "Only the API prompt");
1140
1148
  assert.doesNotMatch(runnerInputs[0]?.prompt ?? "", /Context-Matched Conventions \(AUTO-LOADED\)/);
1141
1149
  });
1142
1150
  });
@@ -1145,18 +1153,12 @@ test("createTriggerHandler omits auto-loaded convention prompt section when no c
1145
1153
  // ---------------------------------------------------------------------------
1146
1154
  test("createTriggerHandler marks DONE when post-hook succeeds after runner success", async () => {
1147
1155
  await withTempDir(async (dir) => {
1148
- const harnessDir = join(dir, ".agentteams");
1149
- await mkdir(harnessDir, { recursive: true });
1150
- await writeFile(join(harnessDir, "harness.yml"), [
1151
- "postHooks:",
1152
- " - name: quality-gate",
1153
- " command: echo ok",
1154
- " onFailure: fail",
1155
- ].join("\n"));
1156
1156
  const clientCalls = [];
1157
1157
  const client = {
1158
- fetchTriggerRuntime: async () => ({ ...runtime, authPath: dir }),
1159
- fetchHarnessConfig: async () => null,
1158
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir),
1159
+ fetchHarnessConfigById: fetchServerHarness({
1160
+ postHooks: [{ name: "quality-gate", command: "echo ok", onFailure: "fail" }],
1161
+ }),
1160
1162
  isTriggerCancelRequested: async () => false,
1161
1163
  updateTriggerHistory: async (...args) => {
1162
1164
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -1182,18 +1184,12 @@ test("createTriggerHandler marks DONE when post-hook succeeds after runner succe
1182
1184
  });
1183
1185
  test("createTriggerHandler marks FAILED when post-hook fails with onFailure=fail", async () => {
1184
1186
  await withTempDir(async (dir) => {
1185
- const harnessDir = join(dir, ".agentteams");
1186
- await mkdir(harnessDir, { recursive: true });
1187
- await writeFile(join(harnessDir, "harness.yml"), [
1188
- "postHooks:",
1189
- " - name: quality-gate",
1190
- " command: exit 1",
1191
- " onFailure: fail",
1192
- ].join("\n"));
1193
1187
  const clientCalls = [];
1194
1188
  const client = {
1195
- fetchTriggerRuntime: async () => ({ ...runtime, authPath: dir }),
1196
- fetchHarnessConfig: async () => null,
1189
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir),
1190
+ fetchHarnessConfigById: fetchServerHarness({
1191
+ postHooks: [{ name: "quality-gate", command: "exit 1", onFailure: "fail" }],
1192
+ }),
1197
1193
  isTriggerCancelRequested: async () => false,
1198
1194
  updateTriggerHistory: async (...args) => {
1199
1195
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -1220,18 +1216,12 @@ test("createTriggerHandler marks FAILED when post-hook fails with onFailure=fail
1220
1216
  });
1221
1217
  test("createTriggerHandler marks NEEDS_REVIEW when post-hook fails with onFailure=needs_review", async () => {
1222
1218
  await withTempDir(async (dir) => {
1223
- const harnessDir = join(dir, ".agentteams");
1224
- await mkdir(harnessDir, { recursive: true });
1225
- await writeFile(join(harnessDir, "harness.yml"), [
1226
- "postHooks:",
1227
- " - name: review-gate",
1228
- " command: exit 1",
1229
- " onFailure: needs_review",
1230
- ].join("\n"));
1231
1219
  const clientCalls = [];
1232
1220
  const client = {
1233
- fetchTriggerRuntime: async () => ({ ...runtime, authPath: dir }),
1234
- fetchHarnessConfig: async () => null,
1221
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir),
1222
+ fetchHarnessConfigById: fetchServerHarness({
1223
+ postHooks: [{ name: "review-gate", command: "exit 1", onFailure: "needs_review" }],
1224
+ }),
1235
1225
  isTriggerCancelRequested: async () => false,
1236
1226
  updateTriggerHistory: async (...args) => {
1237
1227
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -1257,19 +1247,13 @@ test("createTriggerHandler marks NEEDS_REVIEW when post-hook fails with onFailur
1257
1247
  });
1258
1248
  test("createTriggerHandler skips post-hooks when runner fails", async () => {
1259
1249
  await withTempDir(async (dir) => {
1260
- const harnessDir = join(dir, ".agentteams");
1261
- await mkdir(harnessDir, { recursive: true });
1262
- await writeFile(join(harnessDir, "harness.yml"), [
1263
- "postHooks:",
1264
- " - name: should-not-run",
1265
- " command: echo unreachable",
1266
- " onFailure: fail",
1267
- ].join("\n"));
1268
1250
  const clientCalls = [];
1269
1251
  const logEntries = [];
1270
1252
  const client = {
1271
- fetchTriggerRuntime: async () => ({ ...runtime, authPath: dir }),
1272
- fetchHarnessConfig: async () => null,
1253
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir),
1254
+ fetchHarnessConfigById: fetchServerHarness({
1255
+ postHooks: [{ name: "should-not-run", command: "echo unreachable", onFailure: "fail" }],
1256
+ }),
1273
1257
  isTriggerCancelRequested: async () => false,
1274
1258
  updateTriggerHistory: async (...args) => {
1275
1259
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -1298,14 +1282,8 @@ test("createTriggerHandler skips post-hooks when runner fails", async () => {
1298
1282
  assert.equal(logEntries.some((l) => l.includes("post-execution")), false, "Post-hooks should not run when runner fails");
1299
1283
  });
1300
1284
  });
1301
- test("createTriggerHandler records injected conventions with auto-match and harness-pinned sources", async () => {
1285
+ test("createTriggerHandler leaves prompt convention audit recording to the API", async () => {
1302
1286
  await withTempDir(async (dir) => {
1303
- const harnessDir = join(dir, ".agentteams");
1304
- await mkdir(harnessDir, { recursive: true });
1305
- await writeFile(join(harnessDir, "harness.yml"), [
1306
- "conventionIds:",
1307
- " - conv-pinned",
1308
- ].join("\n"));
1309
1287
  const conventions = [
1310
1288
  {
1311
1289
  id: "conv-auto",
@@ -1329,20 +1307,20 @@ test("createTriggerHandler records injected conventions with auto-match and harn
1329
1307
  description: null,
1330
1308
  },
1331
1309
  ];
1332
- const recordedBatches = [];
1310
+ let recordCalled = false;
1333
1311
  const client = {
1334
- fetchTriggerRuntime: async () => ({
1335
- ...runtime,
1336
- authPath: dir,
1312
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
1337
1313
  conventions,
1338
1314
  planType: "BUG_FIX",
1339
1315
  }),
1340
- fetchHarnessConfig: async () => null,
1316
+ fetchHarnessConfigById: fetchServerHarness({
1317
+ conventionIds: ["conv-pinned"],
1318
+ }),
1341
1319
  isTriggerCancelRequested: async () => false,
1342
1320
  updateTriggerHistory: async () => undefined,
1343
1321
  updateTriggerStatus: async () => undefined,
1344
- recordInjectedConventions: async (triggerId, items) => {
1345
- recordedBatches.push({ triggerId, items });
1322
+ recordInjectedConventions: async () => {
1323
+ recordCalled = true;
1346
1324
  },
1347
1325
  };
1348
1326
  const handler = createTriggerHandler({
@@ -1371,23 +1349,11 @@ test("createTriggerHandler records injected conventions with auto-match and harn
1371
1349
  }),
1372
1350
  });
1373
1351
  await handler({ ...trigger, parentTriggerId: null });
1374
- assert.equal(recordedBatches.length, 1);
1375
- assert.equal(recordedBatches[0]?.triggerId, "trigger-1");
1376
- const items = recordedBatches[0]?.items ?? [];
1377
- const byId = new Map(items.map((i) => [i.conventionId, i.source]));
1378
- assert.equal(byId.get("conv-auto"), "AUTO_MATCH");
1379
- assert.equal(byId.get("conv-pinned"), "HARNESS_PINNED");
1380
- assert.equal(byId.has("conv-unrelated"), false);
1352
+ assert.equal(recordCalled, false);
1381
1353
  });
1382
1354
  });
1383
- test("createTriggerHandler swallows recordInjectedConventions failures without failing the trigger", async () => {
1355
+ test("createTriggerHandler ignores unavailable prompt convention recording client method", async () => {
1384
1356
  await withTempDir(async (dir) => {
1385
- const harnessDir = join(dir, ".agentteams");
1386
- await mkdir(harnessDir, { recursive: true });
1387
- await writeFile(join(harnessDir, "harness.yml"), [
1388
- "conventionIds:",
1389
- " - conv-pinned",
1390
- ].join("\n"));
1391
1357
  const conventions = [
1392
1358
  {
1393
1359
  id: "conv-pinned",
@@ -1399,13 +1365,13 @@ test("createTriggerHandler swallows recordInjectedConventions failures without f
1399
1365
  ];
1400
1366
  const clientCalls = [];
1401
1367
  const client = {
1402
- fetchTriggerRuntime: async () => ({
1403
- ...runtime,
1404
- authPath: dir,
1368
+ fetchTriggerRuntime: async () => withHarnessRuntime(dir, {
1405
1369
  conventions,
1406
1370
  planType: "BUG_FIX",
1407
1371
  }),
1408
- fetchHarnessConfig: async () => null,
1372
+ fetchHarnessConfigById: fetchServerHarness({
1373
+ conventionIds: ["conv-pinned"],
1374
+ }),
1409
1375
  isTriggerCancelRequested: async () => false,
1410
1376
  updateTriggerHistory: async (...args) => {
1411
1377
  clientCalls.push({ method: "updateTriggerHistory", args });
@@ -1413,9 +1379,6 @@ test("createTriggerHandler swallows recordInjectedConventions failures without f
1413
1379
  updateTriggerStatus: async (...args) => {
1414
1380
  clientCalls.push({ method: "updateTriggerStatus", args });
1415
1381
  },
1416
- recordInjectedConventions: async () => {
1417
- throw new Error("server unavailable");
1418
- },
1419
1382
  };
1420
1383
  const handler = createTriggerHandler({
1421
1384
  config: {