@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.
- package/package.json +1 -1
- 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.
|
|
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";
|