@fieldwangai/agentflow 0.1.123 → 0.1.125
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/bin/lib/ui-server.mjs
CHANGED
|
@@ -8169,11 +8169,186 @@ function prdWorkflowReviewDedentPlannedCode(lines) {
|
|
|
8169
8169
|
return lines.map((line) => String(line || "").slice(indent));
|
|
8170
8170
|
}
|
|
8171
8171
|
|
|
8172
|
+
const PRD_WORKFLOW_REVIEW_CODE_KEYWORDS = new Set([
|
|
8173
|
+
"abstract", "as", "async", "await", "break", "case", "catch", "class", "const",
|
|
8174
|
+
"continue", "default", "delete", "do", "else", "enum", "export", "extends",
|
|
8175
|
+
"final", "finally", "for", "from", "fun", "function", "goto", "if", "implements",
|
|
8176
|
+
"import", "in", "instanceof", "interface", "internal", "is", "native", "new",
|
|
8177
|
+
"object", "of", "open", "package", "private", "protected", "public", "return",
|
|
8178
|
+
"sealed", "static", "strictfp", "super", "switch", "synchronized", "throw",
|
|
8179
|
+
"throws", "transient", "try", "typeof", "val", "var", "void", "volatile",
|
|
8180
|
+
"when", "while", "with", "yield",
|
|
8181
|
+
]);
|
|
8182
|
+
|
|
8183
|
+
const PRD_WORKFLOW_REVIEW_CODE_LITERALS = new Set([
|
|
8184
|
+
"false", "null", "this", "true", "undefined",
|
|
8185
|
+
]);
|
|
8186
|
+
|
|
8187
|
+
const PRD_WORKFLOW_REVIEW_CODE_TYPES = new Set([
|
|
8188
|
+
"any", "boolean", "byte", "char", "double", "float", "int", "long", "never",
|
|
8189
|
+
"number", "short", "string", "unknown",
|
|
8190
|
+
]);
|
|
8191
|
+
|
|
8192
|
+
function prdWorkflowReviewCodeLanguage(filePath) {
|
|
8193
|
+
const extension = String(filePath || "").trim().toLowerCase().match(/\.([a-z0-9]+)$/)?.[1] || "";
|
|
8194
|
+
return {
|
|
8195
|
+
c: "c",
|
|
8196
|
+
cc: "cpp",
|
|
8197
|
+
cpp: "cpp",
|
|
8198
|
+
cs: "csharp",
|
|
8199
|
+
go: "go",
|
|
8200
|
+
java: "java",
|
|
8201
|
+
js: "javascript",
|
|
8202
|
+
json: "json",
|
|
8203
|
+
jsx: "jsx",
|
|
8204
|
+
kt: "kotlin",
|
|
8205
|
+
kts: "kotlin",
|
|
8206
|
+
m: "objective-c",
|
|
8207
|
+
mm: "objective-cpp",
|
|
8208
|
+
py: "python",
|
|
8209
|
+
rs: "rust",
|
|
8210
|
+
sh: "shell",
|
|
8211
|
+
swift: "swift",
|
|
8212
|
+
ts: "typescript",
|
|
8213
|
+
tsx: "tsx",
|
|
8214
|
+
}[extension] || "text";
|
|
8215
|
+
}
|
|
8216
|
+
|
|
8217
|
+
function prdWorkflowReviewSyntaxToken(kind, value) {
|
|
8218
|
+
const escaped = htmlEscapeAttribute(prdWorkflowReviewNormalizeText(value));
|
|
8219
|
+
return kind ? `<span class="syntax-${kind}">${escaped}</span>` : escaped;
|
|
8220
|
+
}
|
|
8221
|
+
|
|
8222
|
+
function prdWorkflowReviewHighlightCodeLine(line, language, state) {
|
|
8223
|
+
const source = prdWorkflowReviewNormalizeText(line);
|
|
8224
|
+
let html = "";
|
|
8225
|
+
let cursor = 0;
|
|
8226
|
+
const isIdentifierStart = (char) => /[A-Za-z_$]/.test(char || "");
|
|
8227
|
+
const isIdentifierPart = (char) => /[A-Za-z0-9_$]/.test(char || "");
|
|
8228
|
+
|
|
8229
|
+
while (cursor < source.length) {
|
|
8230
|
+
if (state.blockComment) {
|
|
8231
|
+
const end = source.indexOf("*/", cursor);
|
|
8232
|
+
if (end < 0) {
|
|
8233
|
+
html += prdWorkflowReviewSyntaxToken("comment", source.slice(cursor));
|
|
8234
|
+
cursor = source.length;
|
|
8235
|
+
continue;
|
|
8236
|
+
}
|
|
8237
|
+
html += prdWorkflowReviewSyntaxToken("comment", source.slice(cursor, end + 2));
|
|
8238
|
+
state.blockComment = false;
|
|
8239
|
+
cursor = end + 2;
|
|
8240
|
+
continue;
|
|
8241
|
+
}
|
|
8242
|
+
|
|
8243
|
+
if (source.startsWith("//", cursor) || (
|
|
8244
|
+
language === "shell"
|
|
8245
|
+
&& source[cursor] === "#"
|
|
8246
|
+
)) {
|
|
8247
|
+
html += prdWorkflowReviewSyntaxToken("comment", source.slice(cursor));
|
|
8248
|
+
break;
|
|
8249
|
+
}
|
|
8250
|
+
|
|
8251
|
+
if (source.startsWith("/*", cursor)) {
|
|
8252
|
+
const end = source.indexOf("*/", cursor + 2);
|
|
8253
|
+
if (end < 0) {
|
|
8254
|
+
html += prdWorkflowReviewSyntaxToken("comment", source.slice(cursor));
|
|
8255
|
+
state.blockComment = true;
|
|
8256
|
+
break;
|
|
8257
|
+
}
|
|
8258
|
+
html += prdWorkflowReviewSyntaxToken("comment", source.slice(cursor, end + 2));
|
|
8259
|
+
cursor = end + 2;
|
|
8260
|
+
continue;
|
|
8261
|
+
}
|
|
8262
|
+
|
|
8263
|
+
const char = source[cursor];
|
|
8264
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
8265
|
+
let end = cursor + 1;
|
|
8266
|
+
while (end < source.length) {
|
|
8267
|
+
if (source[end] === "\\") {
|
|
8268
|
+
end += 2;
|
|
8269
|
+
continue;
|
|
8270
|
+
}
|
|
8271
|
+
if (source[end] === char) {
|
|
8272
|
+
end += 1;
|
|
8273
|
+
break;
|
|
8274
|
+
}
|
|
8275
|
+
end += 1;
|
|
8276
|
+
}
|
|
8277
|
+
html += prdWorkflowReviewSyntaxToken("string", source.slice(cursor, end));
|
|
8278
|
+
cursor = end;
|
|
8279
|
+
continue;
|
|
8280
|
+
}
|
|
8281
|
+
|
|
8282
|
+
if (char === "@" && isIdentifierStart(source[cursor + 1])) {
|
|
8283
|
+
let end = cursor + 2;
|
|
8284
|
+
while (end < source.length && isIdentifierPart(source[end])) end += 1;
|
|
8285
|
+
html += prdWorkflowReviewSyntaxToken("annotation", source.slice(cursor, end));
|
|
8286
|
+
cursor = end;
|
|
8287
|
+
continue;
|
|
8288
|
+
}
|
|
8289
|
+
|
|
8290
|
+
if (/[0-9]/.test(char)) {
|
|
8291
|
+
const number = source.slice(cursor).match(/^(?:0[xX][\dA-Fa-f_]+|0[bB][01_]+|\d[\d_]*(?:\.\d[\d_]*)?(?:[eE][+-]?\d+)?[fFdDlL]?)/)?.[0] || char;
|
|
8292
|
+
html += prdWorkflowReviewSyntaxToken("number", number);
|
|
8293
|
+
cursor += number.length;
|
|
8294
|
+
continue;
|
|
8295
|
+
}
|
|
8296
|
+
|
|
8297
|
+
if (isIdentifierStart(char)) {
|
|
8298
|
+
let end = cursor + 1;
|
|
8299
|
+
while (end < source.length && isIdentifierPart(source[end])) end += 1;
|
|
8300
|
+
const word = source.slice(cursor, end);
|
|
8301
|
+
const next = source.slice(end).match(/^\s*(.)/)?.[1] || "";
|
|
8302
|
+
let kind = "";
|
|
8303
|
+
if (PRD_WORKFLOW_REVIEW_CODE_KEYWORDS.has(word)) kind = "keyword";
|
|
8304
|
+
else if (PRD_WORKFLOW_REVIEW_CODE_LITERALS.has(word)) kind = "literal";
|
|
8305
|
+
else if (/^[A-Z][A-Z0-9_]*$/.test(word)) kind = "constant";
|
|
8306
|
+
else if (PRD_WORKFLOW_REVIEW_CODE_TYPES.has(word) || /^[A-Z][A-Za-z0-9_$]*$/.test(word)) kind = "type";
|
|
8307
|
+
else if (next === "(") kind = "function";
|
|
8308
|
+
html += prdWorkflowReviewSyntaxToken(kind, word);
|
|
8309
|
+
cursor = end;
|
|
8310
|
+
continue;
|
|
8311
|
+
}
|
|
8312
|
+
|
|
8313
|
+
const operator = source.slice(cursor).match(/^(?:>>>=|===|!==|>>>|<<=|>>=|->|=>|==|!=|<=|>=|&&|\|\||\+\+|--|\+=|-=|\*=|\/=|%=|::|<<|>>|\?\.|\?:)/)?.[0];
|
|
8314
|
+
if (operator) {
|
|
8315
|
+
html += prdWorkflowReviewSyntaxToken("operator", operator);
|
|
8316
|
+
cursor += operator.length;
|
|
8317
|
+
continue;
|
|
8318
|
+
}
|
|
8319
|
+
if (/[+\-*/%=&|!<>?:~^]/.test(char)) {
|
|
8320
|
+
html += prdWorkflowReviewSyntaxToken("operator", char);
|
|
8321
|
+
cursor += 1;
|
|
8322
|
+
continue;
|
|
8323
|
+
}
|
|
8324
|
+
if (/[()[\]{},.;]/.test(char)) {
|
|
8325
|
+
html += prdWorkflowReviewSyntaxToken("punctuation", char);
|
|
8326
|
+
cursor += 1;
|
|
8327
|
+
continue;
|
|
8328
|
+
}
|
|
8329
|
+
|
|
8330
|
+
html += htmlEscapeAttribute(char);
|
|
8331
|
+
cursor += 1;
|
|
8332
|
+
}
|
|
8333
|
+
|
|
8334
|
+
return html || " ";
|
|
8335
|
+
}
|
|
8336
|
+
|
|
8337
|
+
function prdWorkflowReviewHighlightCodeLines(lines, filePath) {
|
|
8338
|
+
const language = prdWorkflowReviewCodeLanguage(filePath);
|
|
8339
|
+
const state = { blockComment: false };
|
|
8340
|
+
return {
|
|
8341
|
+
language,
|
|
8342
|
+
lines: lines.map((line) => prdWorkflowReviewHighlightCodeLine(line, language, state)),
|
|
8343
|
+
};
|
|
8344
|
+
}
|
|
8345
|
+
|
|
8172
8346
|
function prdWorkflowReviewRenderPlannedCode(filePath, codeLines, startLine = 0, endLine = 0) {
|
|
8173
8347
|
const safePath = htmlEscapeAttribute(
|
|
8174
8348
|
String(filePath || "").trim().replace(/^`|`$/g, ""),
|
|
8175
8349
|
);
|
|
8176
8350
|
const normalizedLines = prdWorkflowReviewDedentPlannedCode(codeLines);
|
|
8351
|
+
const highlighted = prdWorkflowReviewHighlightCodeLines(normalizedLines, filePath);
|
|
8177
8352
|
const firstLine = Number.isFinite(Number(startLine)) && Number(startLine) > 0
|
|
8178
8353
|
? Number(startLine)
|
|
8179
8354
|
: 0;
|
|
@@ -8183,11 +8358,11 @@ function prdWorkflowReviewRenderPlannedCode(filePath, codeLines, startLine = 0,
|
|
|
8183
8358
|
const lineLabel = firstLine
|
|
8184
8359
|
? `L${firstLine}${explicitEnd && explicitEnd !== firstLine ? `–L${explicitEnd}` : ""}`
|
|
8185
8360
|
: "拟修改";
|
|
8186
|
-
const rows =
|
|
8361
|
+
const rows = highlighted.lines.map((line, index) => {
|
|
8187
8362
|
const lineNumber = firstLine ? String(firstLine + index) : "·";
|
|
8188
|
-
return `<span class="planned-code__line"><span class="planned-code__number">${lineNumber}</span><span class="planned-code__text">${
|
|
8363
|
+
return `<span class="planned-code__line"><span class="planned-code__number">${lineNumber}</span><span class="planned-code__text">${line}</span></span>`;
|
|
8189
8364
|
}).join("");
|
|
8190
|
-
return `<section class="planned-code" data-file="${safePath}">
|
|
8365
|
+
return `<section class="planned-code" data-file="${safePath}" data-language="${highlighted.language}">
|
|
8191
8366
|
<div class="planned-code__header">
|
|
8192
8367
|
<code class="planned-code__file">${safePath}</code>
|
|
8193
8368
|
<span class="planned-code__anchor">${htmlEscapeAttribute(lineLabel)}</span>
|
|
@@ -8211,7 +8386,8 @@ function prdWorkflowReviewParseChangeIntent(lines, startIndex) {
|
|
|
8211
8386
|
base: "",
|
|
8212
8387
|
insertNear: "",
|
|
8213
8388
|
destination: "",
|
|
8214
|
-
|
|
8389
|
+
references: [],
|
|
8390
|
+
annotations: [],
|
|
8215
8391
|
proposals: [],
|
|
8216
8392
|
};
|
|
8217
8393
|
for (let i = startIndex + 1; i < lines.length; i += 1) {
|
|
@@ -8240,11 +8416,31 @@ function prdWorkflowReviewParseChangeIntent(lines, startIndex) {
|
|
|
8240
8416
|
end += 1;
|
|
8241
8417
|
}
|
|
8242
8418
|
if (end >= lines.length) return null;
|
|
8243
|
-
change.
|
|
8419
|
+
change.references.push({
|
|
8244
8420
|
startLine: Number(reference[1]),
|
|
8245
8421
|
endLine: Number(reference[2] || reference[1]),
|
|
8246
8422
|
lines: body,
|
|
8247
|
-
};
|
|
8423
|
+
});
|
|
8424
|
+
i = end;
|
|
8425
|
+
continue;
|
|
8426
|
+
}
|
|
8427
|
+
const annotation = trimmed.match(
|
|
8428
|
+
/^#annotation\s+line\s*(\d+)(?:\s*-\s*(\d+))?\s+(problem|change|preserve)\s*$/i,
|
|
8429
|
+
);
|
|
8430
|
+
if (annotation) {
|
|
8431
|
+
const body = [];
|
|
8432
|
+
let end = i + 1;
|
|
8433
|
+
while (end < lines.length && !/^#annotationend\s*$/i.test(String(lines[end] || "").trim())) {
|
|
8434
|
+
body.push(lines[end]);
|
|
8435
|
+
end += 1;
|
|
8436
|
+
}
|
|
8437
|
+
if (end >= lines.length) return null;
|
|
8438
|
+
change.annotations.push({
|
|
8439
|
+
startLine: Number(annotation[1]),
|
|
8440
|
+
endLine: Number(annotation[2] || annotation[1]),
|
|
8441
|
+
type: annotation[3].toLowerCase(),
|
|
8442
|
+
lines: body,
|
|
8443
|
+
});
|
|
8248
8444
|
i = end;
|
|
8249
8445
|
continue;
|
|
8250
8446
|
}
|
|
@@ -8286,6 +8482,11 @@ function prdWorkflowReviewRenderChangeIntent(change) {
|
|
|
8286
8482
|
pseudocode: "方案伪代码",
|
|
8287
8483
|
code: "拟议代码 · 未写入",
|
|
8288
8484
|
};
|
|
8485
|
+
const annotationLabels = {
|
|
8486
|
+
problem: "当前问题",
|
|
8487
|
+
change: "计划修改",
|
|
8488
|
+
preserve: "保持不变",
|
|
8489
|
+
};
|
|
8289
8490
|
const operation = operationLabels[change.operation] || "变更";
|
|
8290
8491
|
const target = targetLabels[change.target] || "目标";
|
|
8291
8492
|
const locator = change.module || change.file || "未定位";
|
|
@@ -8300,23 +8501,66 @@ function prdWorkflowReviewRenderChangeIntent(change) {
|
|
|
8300
8501
|
safeDestination ? `<span><strong>移动到</strong> <code>${safeDestination}</code></span>` : "",
|
|
8301
8502
|
].filter(Boolean).join("");
|
|
8302
8503
|
|
|
8504
|
+
const references = Array.isArray(change.references)
|
|
8505
|
+
? change.references
|
|
8506
|
+
: (change.reference ? [change.reference] : []);
|
|
8507
|
+
const annotations = Array.isArray(change.annotations) ? change.annotations : [];
|
|
8508
|
+
const sourceLanguage = prdWorkflowReviewCodeLanguage(change.file || change.module);
|
|
8509
|
+
const sourceLineLabel = (item) => `L${item.startLine}${
|
|
8510
|
+
item.endLine !== item.startLine
|
|
8511
|
+
? `–L${item.endLine}`
|
|
8512
|
+
: ""
|
|
8513
|
+
}`;
|
|
8303
8514
|
let referenceHtml = "";
|
|
8304
|
-
if (
|
|
8305
|
-
const
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8515
|
+
if (references.length) {
|
|
8516
|
+
const totalLines = references.reduce(
|
|
8517
|
+
(total, reference) => total + Math.max(0, reference.endLine - reference.startLine + 1),
|
|
8518
|
+
0,
|
|
8519
|
+
);
|
|
8520
|
+
const hunks = references.map((reference, referenceIndex) => {
|
|
8521
|
+
const normalized = prdWorkflowReviewDedentPlannedCode(reference.lines);
|
|
8522
|
+
const highlighted = prdWorkflowReviewHighlightCodeLines(normalized, change.file || change.module);
|
|
8523
|
+
const rows = highlighted.lines.map((line, index) => (
|
|
8524
|
+
`<span class="change-intent__source-line">`
|
|
8525
|
+
+ `<span class="change-intent__source-number">${reference.startLine + index}</span>`
|
|
8526
|
+
+ `<span class="change-intent__source-text">${line}</span>`
|
|
8527
|
+
+ "</span>"
|
|
8528
|
+
)).join("");
|
|
8529
|
+
const annotationHtml = annotations
|
|
8530
|
+
.filter((annotation) => (
|
|
8531
|
+
reference.startLine <= annotation.startLine
|
|
8532
|
+
&& annotation.endLine <= reference.endLine
|
|
8533
|
+
))
|
|
8534
|
+
.map((annotation) => {
|
|
8535
|
+
const type = annotation.type || "change";
|
|
8536
|
+
const label = annotationLabels[type] || "Review 指引";
|
|
8537
|
+
const body = prdWorkflowReviewMarkdownLinesToHtml(
|
|
8538
|
+
prdWorkflowReviewDedentPlannedCode(annotation.lines),
|
|
8539
|
+
);
|
|
8540
|
+
return `<aside class="change-intent__annotation is-${htmlEscapeAttribute(type)}">
|
|
8541
|
+
<div class="change-intent__annotation-header">
|
|
8542
|
+
<span class="change-intent__annotation-title">Review 指引</span>
|
|
8543
|
+
<span class="change-intent__annotation-badge">${htmlEscapeAttribute(label)}</span>
|
|
8544
|
+
<span class="change-intent__annotation-anchor">${htmlEscapeAttribute(sourceLineLabel(annotation))}</span>
|
|
8545
|
+
</div>
|
|
8546
|
+
<div class="change-intent__annotation-body">${body}</div>
|
|
8547
|
+
</aside>`;
|
|
8548
|
+
}).join("");
|
|
8549
|
+
return `<section class="change-intent__hunk">
|
|
8550
|
+
<div class="change-intent__hunk-header">
|
|
8551
|
+
<span>片段 ${referenceIndex + 1}</span>
|
|
8552
|
+
<span class="change-intent__line-anchor">${htmlEscapeAttribute(sourceLineLabel(reference))}</span>
|
|
8553
|
+
</div>
|
|
8554
|
+
<pre class="change-intent__source" data-language="${highlighted.language}"><code>${rows}</code></pre>
|
|
8555
|
+
${annotationHtml}
|
|
8556
|
+
</section>`;
|
|
8557
|
+
}).join("");
|
|
8558
|
+
referenceHtml = `<details class="change-intent__context" data-language="${sourceLanguage}" open>
|
|
8559
|
+
<summary>
|
|
8560
|
+
<span>当前上下文</span>
|
|
8561
|
+
<span class="change-intent__context-stats">${references.length} 个片段 · ${totalLines} 行</span>
|
|
8562
|
+
</summary>
|
|
8563
|
+
<div class="change-intent__hunks">${hunks}</div>
|
|
8320
8564
|
</details>`;
|
|
8321
8565
|
}
|
|
8322
8566
|
|
|
@@ -8328,13 +8572,14 @@ function prdWorkflowReviewRenderChangeIntent(change) {
|
|
|
8328
8572
|
if (type === "natural") {
|
|
8329
8573
|
body = `<div class="change-intent__natural">${prdWorkflowReviewMarkdownLinesToHtml(normalized)}</div>`;
|
|
8330
8574
|
} else if (type === "code") {
|
|
8331
|
-
const
|
|
8575
|
+
const highlighted = prdWorkflowReviewHighlightCodeLines(normalized, change.file || change.module);
|
|
8576
|
+
const rows = highlighted.lines.map((line) => (
|
|
8332
8577
|
`<span class="change-intent__proposal-line is-code">`
|
|
8333
8578
|
+ '<span class="change-intent__proposal-mark">+</span>'
|
|
8334
|
-
+ `<span class="change-intent__proposal-text">${
|
|
8579
|
+
+ `<span class="change-intent__proposal-text">${line}</span>`
|
|
8335
8580
|
+ "</span>"
|
|
8336
8581
|
)).join("");
|
|
8337
|
-
body = `<pre class="change-intent__proposal-body is-code"><code>${rows}</code></pre>`;
|
|
8582
|
+
body = `<pre class="change-intent__proposal-body is-code" data-language="${highlighted.language}"><code>${rows}</code></pre>`;
|
|
8338
8583
|
} else {
|
|
8339
8584
|
const rows = normalized.map((line) => (
|
|
8340
8585
|
`<span class="change-intent__proposal-line">`
|
|
@@ -8681,6 +8926,17 @@ export function prdWorkflowReviewHtml(title, markdown, meta = {}) {
|
|
|
8681
8926
|
--change-modify: #7dcfff;
|
|
8682
8927
|
--change-remove: #f7768e;
|
|
8683
8928
|
--change-move: #bb9af7;
|
|
8929
|
+
--syntax-comment: #737aa2;
|
|
8930
|
+
--syntax-keyword: #bb9af7;
|
|
8931
|
+
--syntax-literal: #ff9e64;
|
|
8932
|
+
--syntax-type: #2ac3de;
|
|
8933
|
+
--syntax-function: #7aa2f7;
|
|
8934
|
+
--syntax-string: #9ece6a;
|
|
8935
|
+
--syntax-number: #ff9e64;
|
|
8936
|
+
--syntax-annotation: #e0af68;
|
|
8937
|
+
--syntax-constant: #ff9e64;
|
|
8938
|
+
--syntax-operator: #89ddff;
|
|
8939
|
+
--syntax-punctuation: #a9b1d6;
|
|
8684
8940
|
--action-bg: #1f2335;
|
|
8685
8941
|
--action-header: #24283b;
|
|
8686
8942
|
--pending-bg: rgba(224,175,104,.10);
|
|
@@ -8726,6 +8982,17 @@ export function prdWorkflowReviewHtml(title, markdown, meta = {}) {
|
|
|
8726
8982
|
--change-modify: #007197;
|
|
8727
8983
|
--change-remove: #c64343;
|
|
8728
8984
|
--change-move: #7847bd;
|
|
8985
|
+
--syntax-comment: #8990a7;
|
|
8986
|
+
--syntax-keyword: #7847bd;
|
|
8987
|
+
--syntax-literal: #965027;
|
|
8988
|
+
--syntax-type: #007197;
|
|
8989
|
+
--syntax-function: #2e7de9;
|
|
8990
|
+
--syntax-string: #587539;
|
|
8991
|
+
--syntax-number: #965027;
|
|
8992
|
+
--syntax-annotation: #9a5200;
|
|
8993
|
+
--syntax-constant: #965027;
|
|
8994
|
+
--syntax-operator: #007197;
|
|
8995
|
+
--syntax-punctuation: #565a6e;
|
|
8729
8996
|
--action-bg: #f3f3f5;
|
|
8730
8997
|
--action-header: #e9e9ed;
|
|
8731
8998
|
--pending-bg: rgba(177,92,0,.08);
|
|
@@ -8767,9 +9034,9 @@ export function prdWorkflowReviewHtml(title, markdown, meta = {}) {
|
|
|
8767
9034
|
.planned-code__file { min-width: 0; border: 0; background: transparent; color: var(--link); padding: 0; font-weight: 800; }
|
|
8768
9035
|
.planned-code__anchor, .planned-code__badge { border: 1px solid var(--border); border-radius: 999px; padding: 3px 8px; color: var(--muted); font: 800 11px/1.35 "SFMono-Regular", Consolas, monospace; }
|
|
8769
9036
|
.planned-code__badge { margin-left: auto; border-color: var(--planned-border); color: var(--complete-text); }
|
|
8770
|
-
.planned-code__body { margin: 0; border: 0; border-radius: 0; padding: 10px 0; }
|
|
8771
|
-
.planned-code__body code { display: block; }
|
|
8772
|
-
.planned-code__line { display: grid; grid-template-columns:
|
|
9037
|
+
.planned-code__body { margin: 0; border: 0; border-radius: 0; padding: 10px 0; font: 500 13px/1.55 "SFMono-Regular", "JetBrains Mono", Consolas, monospace; }
|
|
9038
|
+
.planned-code__body code { display: block; font: inherit; }
|
|
9039
|
+
.planned-code__line { display: grid; grid-template-columns: 3.75rem minmax(max-content, 1fr); min-height: 1.55em; }
|
|
8773
9040
|
.planned-code__line:hover { background: var(--planned-line); }
|
|
8774
9041
|
.planned-code__number { border-right: 1px solid var(--border-soft); color: var(--planned-gutter); padding: 0 .8rem 0 .5rem; text-align: right; user-select: none; }
|
|
8775
9042
|
.planned-code__text { padding: 0 1rem; white-space: pre; }
|
|
@@ -8787,13 +9054,26 @@ export function prdWorkflowReviewHtml(title, markdown, meta = {}) {
|
|
|
8787
9054
|
.change-intent__meta code { border: 0; background: transparent; padding: 0; color: var(--muted); }
|
|
8788
9055
|
.change-intent__context { border-bottom: 1px solid var(--border-soft); background: var(--change-context); }
|
|
8789
9056
|
.change-intent__context summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; cursor: pointer; padding: 10px 14px; color: var(--body); font-size: 13px; font-weight: 900; list-style-position: inside; }
|
|
8790
|
-
.change-intent__line-anchor, .change-intent__proposal-badge { margin-left: auto; border: 1px solid var(--border); border-radius: 999px; padding: 3px 8px; color: var(--muted); font: 800 11px/1.35 "SFMono-Regular", Consolas, monospace; white-space: nowrap; }
|
|
8791
|
-
.change-
|
|
8792
|
-
.change-
|
|
8793
|
-
.change-intent__source
|
|
9057
|
+
.change-intent__context-stats, .change-intent__line-anchor, .change-intent__proposal-badge, .change-intent__annotation-anchor { margin-left: auto; border: 1px solid var(--border); border-radius: 999px; padding: 3px 8px; color: var(--muted); font: 800 11px/1.35 "SFMono-Regular", Consolas, monospace; white-space: nowrap; }
|
|
9058
|
+
.change-intent__hunk + .change-intent__hunk { border-top: 8px solid var(--panel-strong); }
|
|
9059
|
+
.change-intent__hunk-header { display: flex; align-items: center; gap: 10px; border-top: 1px solid var(--border-soft); padding: 8px 14px; color: var(--muted); font-size: 12px; font-weight: 800; }
|
|
9060
|
+
.change-intent__source { margin: 0; border: 0; border-top: 1px solid var(--border-soft); border-radius: 0; padding: 10px 0; background: var(--code-block); font: 500 13px/1.55 "SFMono-Regular", "JetBrains Mono", Consolas, monospace; }
|
|
9061
|
+
.change-intent__source code, .change-intent__proposal-body code { display: block; font: inherit; }
|
|
9062
|
+
.change-intent__source-line { display: grid; grid-template-columns: 3.75rem minmax(max-content, 1fr); min-height: 1.55em; }
|
|
8794
9063
|
.change-intent__source-line:hover { background: var(--planned-line); }
|
|
8795
9064
|
.change-intent__source-number { border-right: 1px solid var(--border-soft); color: var(--planned-gutter); padding: 0 .8rem 0 .5rem; text-align: right; user-select: none; }
|
|
8796
9065
|
.change-intent__source-text { padding: 0 1rem; white-space: pre; }
|
|
9066
|
+
.change-intent__annotation { border-top: 1px solid var(--border-soft); border-left: 3px solid var(--change-modify); background: var(--change-proposal); padding: 10px 14px 11px; }
|
|
9067
|
+
.change-intent__annotation.is-problem { border-left-color: var(--change-remove); }
|
|
9068
|
+
.change-intent__annotation.is-preserve { border-left-color: var(--change-add); }
|
|
9069
|
+
.change-intent__annotation-header { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; color: var(--body); }
|
|
9070
|
+
.change-intent__annotation-title { font-size: 12px; font-weight: 900; }
|
|
9071
|
+
.change-intent__annotation-badge { border: 1px solid currentColor; border-radius: 999px; color: var(--change-modify); padding: 3px 8px; font-size: 11px; font-weight: 900; line-height: 1.3; }
|
|
9072
|
+
.change-intent__annotation.is-problem .change-intent__annotation-badge { color: var(--change-remove); }
|
|
9073
|
+
.change-intent__annotation.is-preserve .change-intent__annotation-badge { color: var(--change-add); }
|
|
9074
|
+
.change-intent__annotation-body { margin-top: 7px; }
|
|
9075
|
+
.change-intent__annotation-body > *:first-child { margin-top: 0; }
|
|
9076
|
+
.change-intent__annotation-body > *:last-child { margin-bottom: 0; }
|
|
8797
9077
|
.change-intent__proposal { background: var(--change-proposal); }
|
|
8798
9078
|
.change-intent__proposal + .change-intent__proposal { border-top: 1px solid var(--border-soft); }
|
|
8799
9079
|
.change-intent__proposal-header { display: flex; align-items: center; gap: 10px; padding: 10px 14px; color: var(--heading); font-size: 13px; font-weight: 900; }
|
|
@@ -8802,11 +9082,22 @@ export function prdWorkflowReviewHtml(title, markdown, meta = {}) {
|
|
|
8802
9082
|
.change-intent__natural { border-top: 1px solid var(--border-soft); padding: 11px 14px 13px; }
|
|
8803
9083
|
.change-intent__natural > *:first-child { margin-top: 0; }
|
|
8804
9084
|
.change-intent__natural > *:last-child { margin-bottom: 0; }
|
|
8805
|
-
.change-intent__proposal-body { margin: 0; border: 0; border-top: 1px solid var(--border-soft); border-radius: 0; padding: 11px 14px 13px; background: var(--code-block); }
|
|
8806
|
-
.change-intent__proposal-line { display: block; min-height: 1.
|
|
9085
|
+
.change-intent__proposal-body { margin: 0; border: 0; border-top: 1px solid var(--border-soft); border-radius: 0; padding: 11px 14px 13px; background: var(--code-block); font: 500 13px/1.55 "SFMono-Regular", "JetBrains Mono", Consolas, monospace; }
|
|
9086
|
+
.change-intent__proposal-line { display: block; min-height: 1.55em; }
|
|
8807
9087
|
.change-intent__proposal-line.is-code { display: grid; grid-template-columns: 2rem minmax(max-content, 1fr); margin: 0 -14px; padding: 0 14px; background: var(--change-code); }
|
|
8808
9088
|
.change-intent__proposal-mark { color: var(--change-add); font-weight: 900; text-align: center; user-select: none; }
|
|
8809
9089
|
.change-intent__proposal-text { white-space: pre; }
|
|
9090
|
+
.syntax-comment { color: var(--syntax-comment); font-style: italic; }
|
|
9091
|
+
.syntax-keyword { color: var(--syntax-keyword); font-weight: 700; }
|
|
9092
|
+
.syntax-literal { color: var(--syntax-literal); font-weight: 650; }
|
|
9093
|
+
.syntax-type { color: var(--syntax-type); }
|
|
9094
|
+
.syntax-function { color: var(--syntax-function); }
|
|
9095
|
+
.syntax-string { color: var(--syntax-string); }
|
|
9096
|
+
.syntax-number { color: var(--syntax-number); }
|
|
9097
|
+
.syntax-annotation { color: var(--syntax-annotation); }
|
|
9098
|
+
.syntax-constant { color: var(--syntax-constant); }
|
|
9099
|
+
.syntax-operator { color: var(--syntax-operator); }
|
|
9100
|
+
.syntax-punctuation { color: var(--syntax-punctuation); }
|
|
8810
9101
|
blockquote { margin: 1rem 0; border-left: 3px solid var(--purple); background: var(--panel-soft); padding: .75rem 1rem; color: var(--body); }
|
|
8811
9102
|
a { color: var(--link); text-decoration-thickness: .08em; text-underline-offset: .16em; overflow-wrap: anywhere; }
|
|
8812
9103
|
.table-wrap { max-width: 100%; overflow-x: auto; margin: 1rem 0 1.25rem; border: 1px solid var(--border-soft); border-radius: 10px; background: var(--panel-strong); }
|
|
@@ -8840,7 +9131,7 @@ export function prdWorkflowReviewHtml(title, markdown, meta = {}) {
|
|
|
8840
9131
|
main { padding: 28px 14px 48px; }
|
|
8841
9132
|
.change-intent__header { align-items: flex-start; flex-direction: column; }
|
|
8842
9133
|
.change-intent__locator { width: 100%; }
|
|
8843
|
-
.change-intent__source-line { grid-template-columns: 3.
|
|
9134
|
+
.change-intent__source-line, .planned-code__line { grid-template-columns: 3.25rem minmax(max-content, 1fr); }
|
|
8844
9135
|
header { display: block; }
|
|
8845
9136
|
.toolbar { justify-content: flex-start; margin-top: 14px; }
|
|
8846
9137
|
article { padding: 18px; }
|
|
@@ -9000,6 +9291,50 @@ function prdWorkflowAppendAudit(scopedRoot, tapdId, event = {}) {
|
|
|
9000
9291
|
} catch (_) {}
|
|
9001
9292
|
}
|
|
9002
9293
|
|
|
9294
|
+
function prdWorkflowReadAuditEntries(scopedRoot, tapdId, limit = 500) {
|
|
9295
|
+
try {
|
|
9296
|
+
const p = prdWorkflowAuditPath(scopedRoot, tapdId);
|
|
9297
|
+
if (!fs.existsSync(p)) return [];
|
|
9298
|
+
return fs.readFileSync(p, "utf-8")
|
|
9299
|
+
.split(/\r?\n/)
|
|
9300
|
+
.filter(Boolean)
|
|
9301
|
+
.slice(-Math.max(1, Number(limit) || 500))
|
|
9302
|
+
.map((line) => {
|
|
9303
|
+
try {
|
|
9304
|
+
return JSON.parse(line);
|
|
9305
|
+
} catch {
|
|
9306
|
+
return null;
|
|
9307
|
+
}
|
|
9308
|
+
})
|
|
9309
|
+
.filter(Boolean);
|
|
9310
|
+
} catch {
|
|
9311
|
+
return [];
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9314
|
+
|
|
9315
|
+
function prdWorkflowReadRecentActionAudit(scopedRoot, tapdId, limit = 24) {
|
|
9316
|
+
return prdWorkflowReadAuditEntries(scopedRoot, tapdId, 500)
|
|
9317
|
+
.filter((item) => item?.type === "snapshot-action-change")
|
|
9318
|
+
.slice(-Math.max(1, Number(limit) || 24));
|
|
9319
|
+
}
|
|
9320
|
+
|
|
9321
|
+
function prdWorkflowFirstPointerObservation(scopedRoot, tapdId, snapshot = {}) {
|
|
9322
|
+
const phase = String(snapshot?.phase || "").trim();
|
|
9323
|
+
const pointer = String(snapshot?.pointer || "").trim();
|
|
9324
|
+
if (!phase && !pointer) return "";
|
|
9325
|
+
let earliest = "";
|
|
9326
|
+
for (const entry of prdWorkflowReadAuditEntries(scopedRoot, tapdId, 5000)) {
|
|
9327
|
+
if (entry?.type !== "client-observation-stored") continue;
|
|
9328
|
+
if (phase && String(entry.phase || "").trim() !== phase) continue;
|
|
9329
|
+
if (pointer && String(entry.pointer || "").trim() !== pointer) continue;
|
|
9330
|
+
const candidate = String(entry.observedAt || entry.reportedAt || entry.at || "").trim();
|
|
9331
|
+
const candidateTime = Date.parse(candidate);
|
|
9332
|
+
if (!Number.isFinite(candidateTime)) continue;
|
|
9333
|
+
if (!earliest || candidateTime < Date.parse(earliest)) earliest = candidate;
|
|
9334
|
+
}
|
|
9335
|
+
return earliest;
|
|
9336
|
+
}
|
|
9337
|
+
|
|
9003
9338
|
function prdWorkflowReadProjectState(scopedRoot, tapdId) {
|
|
9004
9339
|
return prdWorkflowReadJsonFile(prdWorkflowProjectPath(scopedRoot, tapdId), {
|
|
9005
9340
|
version: 1,
|
|
@@ -9092,6 +9427,160 @@ function prdWorkflowWriteClientObservation(scopedRoot, tapdId, meta, snapshot) {
|
|
|
9092
9427
|
});
|
|
9093
9428
|
}
|
|
9094
9429
|
|
|
9430
|
+
const PRD_WORKFLOW_SNAPSHOT_ACTION_ARRAY_KEYS = [
|
|
9431
|
+
"actions",
|
|
9432
|
+
"workflowActions",
|
|
9433
|
+
"workflow_actions",
|
|
9434
|
+
"timeline",
|
|
9435
|
+
"history",
|
|
9436
|
+
];
|
|
9437
|
+
|
|
9438
|
+
function prdWorkflowSnapshotActionKey(action = {}) {
|
|
9439
|
+
const stageKey = String(
|
|
9440
|
+
action.stageKey ||
|
|
9441
|
+
action.stage_key ||
|
|
9442
|
+
action.stage ||
|
|
9443
|
+
action.actionId ||
|
|
9444
|
+
action.action_id ||
|
|
9445
|
+
action.action ||
|
|
9446
|
+
action.id ||
|
|
9447
|
+
"",
|
|
9448
|
+
).trim();
|
|
9449
|
+
const issueKey = String(action.issueKey || action.issue_key || action.issue || "").trim();
|
|
9450
|
+
const platform = String(action.platform || "").trim().toLowerCase();
|
|
9451
|
+
return [stageKey, issueKey, platform].filter(Boolean).join("|");
|
|
9452
|
+
}
|
|
9453
|
+
|
|
9454
|
+
function prdWorkflowSnapshotActionTime(action = {}) {
|
|
9455
|
+
return String(
|
|
9456
|
+
action.stageEnteredAt ||
|
|
9457
|
+
action.stage_entered_at ||
|
|
9458
|
+
action.time ||
|
|
9459
|
+
action.at ||
|
|
9460
|
+
action.observedAt ||
|
|
9461
|
+
action.observed_at ||
|
|
9462
|
+
action.startedAt ||
|
|
9463
|
+
action.started_at ||
|
|
9464
|
+
action.completedAt ||
|
|
9465
|
+
action.completed_at ||
|
|
9466
|
+
action.updatedAt ||
|
|
9467
|
+
action.updated_at ||
|
|
9468
|
+
action.createdAt ||
|
|
9469
|
+
action.created_at ||
|
|
9470
|
+
"",
|
|
9471
|
+
).trim();
|
|
9472
|
+
}
|
|
9473
|
+
|
|
9474
|
+
function prdWorkflowSnapshotSourceActionTime(action = {}) {
|
|
9475
|
+
return String(
|
|
9476
|
+
action.time ||
|
|
9477
|
+
action.at ||
|
|
9478
|
+
action.observedAt ||
|
|
9479
|
+
action.observed_at ||
|
|
9480
|
+
action.startedAt ||
|
|
9481
|
+
action.started_at ||
|
|
9482
|
+
action.completedAt ||
|
|
9483
|
+
action.completed_at ||
|
|
9484
|
+
action.updatedAt ||
|
|
9485
|
+
action.updated_at ||
|
|
9486
|
+
action.createdAt ||
|
|
9487
|
+
action.created_at ||
|
|
9488
|
+
"",
|
|
9489
|
+
).trim();
|
|
9490
|
+
}
|
|
9491
|
+
|
|
9492
|
+
function prdWorkflowSnapshotActionMap(snapshot = {}) {
|
|
9493
|
+
const out = new Map();
|
|
9494
|
+
for (const key of PRD_WORKFLOW_SNAPSHOT_ACTION_ARRAY_KEYS) {
|
|
9495
|
+
const rows = Array.isArray(snapshot?.[key]) ? snapshot[key] : [];
|
|
9496
|
+
for (const action of rows) {
|
|
9497
|
+
if (!action || typeof action !== "object" || Array.isArray(action)) continue;
|
|
9498
|
+
const actionKey = prdWorkflowSnapshotActionKey(action);
|
|
9499
|
+
if (actionKey && !out.has(actionKey)) out.set(actionKey, action);
|
|
9500
|
+
}
|
|
9501
|
+
}
|
|
9502
|
+
return out;
|
|
9503
|
+
}
|
|
9504
|
+
|
|
9505
|
+
function prdWorkflowSnapshotActionChanges(previousSnapshot = {}, nextSnapshot = {}) {
|
|
9506
|
+
const previous = prdWorkflowSnapshotActionMap(previousSnapshot);
|
|
9507
|
+
const next = prdWorkflowSnapshotActionMap(nextSnapshot);
|
|
9508
|
+
const changes = [];
|
|
9509
|
+
const compact = (kind, action, previousAction = null) => ({
|
|
9510
|
+
kind,
|
|
9511
|
+
stageKey: String(action?.stageKey || action?.stage_key || action?.stage || action?.id || "").trim(),
|
|
9512
|
+
issueKey: String(action?.issueKey || action?.issue_key || action?.issue || "").trim(),
|
|
9513
|
+
platform: String(action?.platform || "").trim(),
|
|
9514
|
+
title: String(action?.title || action?.label || action?.name || "").trim(),
|
|
9515
|
+
status: String(action?.status || "").trim(),
|
|
9516
|
+
previousStatus: String(previousAction?.status || "").trim(),
|
|
9517
|
+
actionAt: prdWorkflowSnapshotActionTime(action),
|
|
9518
|
+
previousActionAt: prdWorkflowSnapshotActionTime(previousAction || {}),
|
|
9519
|
+
sourceActionAt: prdWorkflowSnapshotSourceActionTime(action),
|
|
9520
|
+
previousSourceActionAt: prdWorkflowSnapshotSourceActionTime(previousAction || {}),
|
|
9521
|
+
});
|
|
9522
|
+
for (const [key, action] of next) {
|
|
9523
|
+
const previousAction = previous.get(key);
|
|
9524
|
+
if (!previousAction) {
|
|
9525
|
+
changes.push(compact("added", action));
|
|
9526
|
+
continue;
|
|
9527
|
+
}
|
|
9528
|
+
const statusChanged = String(previousAction.status || "") !== String(action.status || "");
|
|
9529
|
+
const timeChanged =
|
|
9530
|
+
prdWorkflowSnapshotActionTime(previousAction) !== prdWorkflowSnapshotActionTime(action) ||
|
|
9531
|
+
prdWorkflowSnapshotSourceActionTime(previousAction) !== prdWorkflowSnapshotSourceActionTime(action);
|
|
9532
|
+
const titleChanged = String(previousAction.title || previousAction.label || "") !== String(action.title || action.label || "");
|
|
9533
|
+
if (statusChanged || timeChanged || titleChanged) {
|
|
9534
|
+
changes.push(compact(
|
|
9535
|
+
statusChanged ? "status-changed" : timeChanged ? "time-changed" : "title-changed",
|
|
9536
|
+
action,
|
|
9537
|
+
previousAction,
|
|
9538
|
+
));
|
|
9539
|
+
}
|
|
9540
|
+
}
|
|
9541
|
+
for (const [key, action] of previous) {
|
|
9542
|
+
if (!next.has(key)) changes.push(compact("removed", action, action));
|
|
9543
|
+
}
|
|
9544
|
+
return changes.slice(0, 80);
|
|
9545
|
+
}
|
|
9546
|
+
|
|
9547
|
+
function prdWorkflowStampCurrentActionEntryTimes(scopedRoot, tapdId, snapshot = {}, clientState = {}, meta = {}) {
|
|
9548
|
+
const existingTimes = new Map();
|
|
9549
|
+
for (const client of Object.values(clientState?.clients || {})) {
|
|
9550
|
+
const observedAt = String(client?.observedAt || client?.reportedAt || "").trim();
|
|
9551
|
+
for (const [key, action] of prdWorkflowSnapshotActionMap(client?.snapshot || {})) {
|
|
9552
|
+
if (String(action?.status || "").trim().toLowerCase() !== "current") continue;
|
|
9553
|
+
const value = String(action.stageEnteredAt || action.stage_entered_at || observedAt).trim();
|
|
9554
|
+
if (!value || !Number.isFinite(Date.parse(value))) continue;
|
|
9555
|
+
const previous = existingTimes.get(key);
|
|
9556
|
+
if (!previous || Date.parse(value) < Date.parse(previous)) existingTimes.set(key, value);
|
|
9557
|
+
}
|
|
9558
|
+
}
|
|
9559
|
+
const auditedAt = prdWorkflowFirstPointerObservation(scopedRoot, tapdId, snapshot);
|
|
9560
|
+
const observedAt = String(meta.observedAt || meta.reportedAt || new Date().toISOString()).trim();
|
|
9561
|
+
const stamp = (action) => {
|
|
9562
|
+
if (!action || typeof action !== "object" || Array.isArray(action)) return action;
|
|
9563
|
+
if (String(action.status || "").trim().toLowerCase() !== "current") return action;
|
|
9564
|
+
const actionKey = prdWorkflowSnapshotActionKey(action);
|
|
9565
|
+
const candidates = [
|
|
9566
|
+
String(action.stageEnteredAt || action.stage_entered_at || "").trim(),
|
|
9567
|
+
existingTimes.get(actionKey) || "",
|
|
9568
|
+
auditedAt,
|
|
9569
|
+
observedAt,
|
|
9570
|
+
].filter((value) => Number.isFinite(Date.parse(value)));
|
|
9571
|
+
const stageEnteredAt = candidates.sort((left, right) => Date.parse(left) - Date.parse(right))[0] || observedAt;
|
|
9572
|
+
return {
|
|
9573
|
+
...action,
|
|
9574
|
+
stageEnteredAt,
|
|
9575
|
+
};
|
|
9576
|
+
};
|
|
9577
|
+
const next = { ...snapshot };
|
|
9578
|
+
for (const key of PRD_WORKFLOW_SNAPSHOT_ACTION_ARRAY_KEYS) {
|
|
9579
|
+
if (Array.isArray(snapshot?.[key])) next[key] = snapshot[key].map(stamp);
|
|
9580
|
+
}
|
|
9581
|
+
return next;
|
|
9582
|
+
}
|
|
9583
|
+
|
|
9095
9584
|
const PRD_WORKFLOW_PROJECTION_SOURCE_KEYS = new Set([
|
|
9096
9585
|
"projectionMode",
|
|
9097
9586
|
"projectCacheScope",
|
|
@@ -9121,6 +9610,7 @@ function prdWorkflowStoredObservationSnapshot(snapshot, sourcePatch = {}) {
|
|
|
9121
9610
|
delete clean.collaboration;
|
|
9122
9611
|
delete clean.clientObservations;
|
|
9123
9612
|
delete clean.clients;
|
|
9613
|
+
delete clean.snapshotAudit;
|
|
9124
9614
|
clean.sources = prdWorkflowStoredObservationSources(snapshot.sources, sourcePatch);
|
|
9125
9615
|
return clean;
|
|
9126
9616
|
}
|
|
@@ -9351,6 +9841,7 @@ function prdWorkflowMaterializeSnapshot(root, scopedRoot, tapdId, userCtx = {},
|
|
|
9351
9841
|
revision: String(materialized.revision || ""),
|
|
9352
9842
|
},
|
|
9353
9843
|
];
|
|
9844
|
+
materialized.snapshotAudit = prdWorkflowReadRecentActionAudit(scopedRoot, tapdId);
|
|
9354
9845
|
prdWorkflowAppendAudit(scopedRoot, tapdId, {
|
|
9355
9846
|
type: "projection-materialized",
|
|
9356
9847
|
flowSource,
|
|
@@ -11969,7 +12460,21 @@ export function startUiServer({
|
|
|
11969
12460
|
issueKey: reportMeta.issueKey,
|
|
11970
12461
|
stageKey: reportMeta.stageKey,
|
|
11971
12462
|
};
|
|
11972
|
-
const
|
|
12463
|
+
const existingClientState = prdWorkflowReadClientState(scopedRoot, tapdId);
|
|
12464
|
+
const existingClientId = prdWorkflowSafeStateId(reportMeta.clientId || "anonymous");
|
|
12465
|
+
const previousClientSnapshot = existingClientState.clients?.[existingClientId]?.snapshot || null;
|
|
12466
|
+
const stampedSnapshot = prdWorkflowStampCurrentActionEntryTimes(
|
|
12467
|
+
scopedRoot,
|
|
12468
|
+
tapdId,
|
|
12469
|
+
normalizedSnapshot,
|
|
12470
|
+
existingClientState,
|
|
12471
|
+
reportMeta,
|
|
12472
|
+
);
|
|
12473
|
+
const storedObservationSnapshot = prdWorkflowStoredObservationSnapshot(stampedSnapshot, reportSource);
|
|
12474
|
+
const actionChanges = prdWorkflowSnapshotActionChanges(
|
|
12475
|
+
previousClientSnapshot || {},
|
|
12476
|
+
storedObservationSnapshot,
|
|
12477
|
+
);
|
|
11973
12478
|
prdWorkflowWriteClientObservation(scopedRoot, tapdId, reportMeta, storedObservationSnapshot);
|
|
11974
12479
|
prdWorkflowAppendAudit(scopedRoot, tapdId, {
|
|
11975
12480
|
type: "client-observation-stored",
|
|
@@ -11988,12 +12493,51 @@ export function startUiServer({
|
|
|
11988
12493
|
persistence: "runtime",
|
|
11989
12494
|
note: "ordinary current snapshot stored as client observation; it must not overwrite project state",
|
|
11990
12495
|
});
|
|
12496
|
+
for (const change of actionChanges) {
|
|
12497
|
+
const changeLabel = {
|
|
12498
|
+
added: "新增",
|
|
12499
|
+
removed: "移除",
|
|
12500
|
+
"status-changed": "状态变更",
|
|
12501
|
+
"time-changed": "时间更正",
|
|
12502
|
+
"title-changed": "标题变更",
|
|
12503
|
+
}[change.kind] || "变更";
|
|
12504
|
+
prdWorkflowAppendAudit(scopedRoot, tapdId, {
|
|
12505
|
+
type: "snapshot-action-change",
|
|
12506
|
+
change: change.kind,
|
|
12507
|
+
title: `Workflow Action ${changeLabel}${change.title ? `:${change.title}` : ""}`,
|
|
12508
|
+
detail: [
|
|
12509
|
+
change.stageKey,
|
|
12510
|
+
change.previousStatus && change.previousStatus !== change.status
|
|
12511
|
+
? `${change.previousStatus} -> ${change.status}`
|
|
12512
|
+
: change.status,
|
|
12513
|
+
change.previousActionAt && change.previousActionAt !== change.actionAt
|
|
12514
|
+
? `${change.previousActionAt} -> ${change.actionAt || "无时间"}`
|
|
12515
|
+
: change.actionAt,
|
|
12516
|
+
change.previousSourceActionAt !== change.sourceActionAt
|
|
12517
|
+
? `来源时间 ${change.previousSourceActionAt || "无"} -> ${change.sourceActionAt || "无"}`
|
|
12518
|
+
: "",
|
|
12519
|
+
].filter(Boolean).join(" · "),
|
|
12520
|
+
auditStatus: "observed",
|
|
12521
|
+
truth: "audit",
|
|
12522
|
+
authority: "agentflow",
|
|
12523
|
+
persistence: "runtime",
|
|
12524
|
+
clientId: reportMeta.clientId,
|
|
12525
|
+
userId: reportMeta.userId,
|
|
12526
|
+
observedAt: reportMeta.observedAt,
|
|
12527
|
+
reportedAt: reportMeta.reportedAt,
|
|
12528
|
+
revision: String(storedObservationSnapshot.revision || ""),
|
|
12529
|
+
previousRevision: String(previousClientSnapshot?.revision || ""),
|
|
12530
|
+
pointer: String(storedObservationSnapshot.pointer || ""),
|
|
12531
|
+
previousPointer: String(previousClientSnapshot?.pointer || ""),
|
|
12532
|
+
...change,
|
|
12533
|
+
});
|
|
12534
|
+
}
|
|
11991
12535
|
|
|
11992
12536
|
const projectFactSource = reportMeta.scope === "project"
|
|
11993
12537
|
? prdWorkflowProjectFactSource(payload, rawSnapshot)
|
|
11994
12538
|
: null;
|
|
11995
12539
|
const projectFactSnapshot = projectFactSource
|
|
11996
|
-
? prdWorkflowStoredObservationSnapshot(
|
|
12540
|
+
? prdWorkflowStoredObservationSnapshot(stampedSnapshot, {
|
|
11997
12541
|
...reportSource,
|
|
11998
12542
|
...projectFactSource,
|
|
11999
12543
|
})
|