@programinglive/dev-workflow-mcp-server 1.13.11 → 1.13.12

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 (2) hide show
  1. package/package.json +1 -1
  2. package/workflow-state.js +71 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@programinglive/dev-workflow-mcp-server",
3
- "version": "1.13.11",
3
+ "version": "1.13.12",
4
4
  "description": "MCP server to enforce development workflow discipline",
5
5
  "homepage": "https://devworkflow.programinglive.com/",
6
6
  "mcpName": "io.github.programinglive/dev-workflow-mcp-server",
package/workflow-state.js CHANGED
@@ -33,6 +33,22 @@ let cachedUserStateDir = null;
33
33
  */
34
34
  const PROCESS_TOKEN = `proc-${process.pid}-${Math.random().toString(16).slice(2, 10)}`;
35
35
 
36
+ /**
37
+ * Langkah workflow yang hanya boleh MAJU dalam satu task.
38
+ *
39
+ * Hanya flag PENCAPAIAN. Gerbang kualitas (`readyToCommit`,
40
+ * `readyCheckCompleted`, `commitAndPushCompleted`, `released`) SENGAJA tidak
41
+ * masuk: keempatnya direset ketika langkah sebelumnya diulang, dan memaksa
42
+ * nilainya tetap true akan melemahkan gerbang yang justru ingin ditegakkan.
43
+ */
44
+ const MONOTONIC_STEP_FIELDS = [
45
+ "featureFlowCreated",
46
+ "bugFixed",
47
+ "testsCreated",
48
+ "testsPassed",
49
+ "documentationCreated",
50
+ ];
51
+
36
52
  function migrateOwnerMetadata(value) {
37
53
  if(value?.owner && !value.owner.processMarker && /^proc-[A-Za-z0-9-]+$/.test(value.owner.token || '')) {
38
54
  value.owner.processMarker=value.owner.token;
@@ -820,6 +836,44 @@ export class WorkflowState {
820
836
  return created;
821
837
  }
822
838
 
839
+ /**
840
+ * Gabungkan kemajuan langkah dari disk bila task-nya sama.
841
+ *
842
+ * Hanya menyentuh flag yang MONOTON dalam satu task. Field yang bisa turun
843
+ * dengan sengaja (mis. `readyCheckCompleted` yang direset `create_tests`)
844
+ * tidak digabung, karena itu bagian dari alur normal satu proses.
845
+ *
846
+ * `reconciled` sudah memuat state disk terbaru; `this.state` adalah milik
847
+ * proses ini. Kalau task berbeda atau disk idle, tidak ada yang digabung:
848
+ * itu task lain, atau task baru yang memang harus mulai dari nol.
849
+ */
850
+ mergeMonotonicProgress(reconciled) {
851
+ const diskTask = (reconciled?.diskTask || "").trim();
852
+ const ourTask = (this.state.taskDescription || "").trim();
853
+ if (!diskTask || !ourTask || diskTask !== ourTask) {
854
+ return;
855
+ }
856
+ if ((reconciled?.diskPhase || "idle") === "idle") {
857
+ return;
858
+ }
859
+
860
+ // Harus dari `reconciled` yang BARU dibaca. `lastReconcile` adalah hasil
861
+ // penyimpanan sebelumnya, sehingga bisa tertinggal satu putaran - dan
862
+ // justru itu yang membuat penggabungan gagal menangkap langkah terbaru.
863
+ const disk = reconciled?.diskState;
864
+ if (!disk) {
865
+ return;
866
+ }
867
+
868
+ // Kemajuan hanya boleh maju. Sekali sebuah langkah tercapai di proses mana
869
+ // pun, langkah itu tetap tercapai.
870
+ for (const field of MONOTONIC_STEP_FIELDS) {
871
+ if (disk[field] === true && this.state[field] !== true) {
872
+ this.state[field] = true;
873
+ }
874
+ }
875
+ }
876
+
823
877
  async save(options = {}) {
824
878
  assertSafeKnowledge(this.state);
825
879
  const reconciled = await this.reconcileWithDisk();
@@ -839,6 +893,19 @@ export class WorkflowState {
839
893
  }
840
894
  this.lastConflict = null;
841
895
 
896
+ // Dua proses MCP bisa melayani sesi yang SAMA (OpenCode men-spawn lebih dari
897
+ // satu instance), dan keduanya menulis berkas state yang sama. Tanpa ini,
898
+ // proses yang memegang state basi menimpa langkah yang sudah dicapai proses
899
+ // lain: `mark_bug_fixed` menjawab sukses, lalu `create_tests` menjawab
900
+ // "mark your feature/bug as fixed first". Gejalanya intermiten, sehingga
901
+ // mudah disalahkan pada hal lain.
902
+ //
903
+ // Langkah workflow bersifat monoton dalam satu task: sekali tercapai, tidak
904
+ // boleh mundur. Karena itu digabung dengan OR - tetapi HANYA bila task di
905
+ // disk memang task yang sama. `start_task` mengganti task, dan pada saat itu
906
+ // flag harus benar-benar direset, bukan dihidupkan kembali.
907
+ this.mergeMonotonicProgress(reconciled);
908
+
842
909
  this.state.owner = {
843
910
  processMarker: PROCESS_TOKEN,
844
911
  pid: process.pid,
@@ -988,11 +1055,15 @@ export class WorkflowState {
988
1055
  foreignToken: null,
989
1056
  diskTask: "",
990
1057
  diskPhase: "idle",
1058
+ // Dipakai mergeMonotonicProgress: state disk apa adanya, supaya flag
1059
+ // langkah yang sudah dicapai proses lain bisa digabung.
1060
+ diskState: null,
991
1061
  };
992
1062
  const disk = await this.readDiskState();
993
1063
  if (!disk) {
994
1064
  return result;
995
1065
  }
1066
+ result.diskState = disk;
996
1067
 
997
1068
  result.diskTask = disk.taskDescription || "";
998
1069
  result.diskPhase = disk.currentPhase || "idle";