@fullstackhouse/cerber 0.38.0 → 0.39.1
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/dist/core/raised.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Undecided is not "post": a duplicate the user never looked at is exactly what
|
|
6
6
|
* this exists to keep off the PR. The one place undecided still posts is a
|
|
7
7
|
* resolved thread — somebody marked that problem settled, and a review that
|
|
8
|
-
* found it again in the current code is saying it isn't.
|
|
8
|
+
* found it again in the current code is saying it isn't. A finding the user
|
|
9
|
+
* has rewritten is not undecided at all: `rewriteFinding` decides it.
|
|
9
10
|
*/
|
|
10
11
|
export function sendTreatment(comment) {
|
|
11
12
|
const raised = comment.alreadyRaised;
|
|
@@ -19,6 +20,43 @@ export function sendTreatment(comment) {
|
|
|
19
20
|
return raised.where.kind === "thread" ? "reply" : "hold";
|
|
20
21
|
return raised.where.kind === "thread" && raised.where.state === "resolved" ? "post" : "hold";
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Rewrite a finding's body — and, where somebody else had raised it, let the
|
|
25
|
+
* rewrite be the decision.
|
|
26
|
+
*
|
|
27
|
+
* An undecided match is Send leaving the duplicate out, and that default is
|
|
28
|
+
* about one thing: nobody has looked at it yet. Rewriting the finding is
|
|
29
|
+
* looking at it. The words that would post are no longer the ones the check
|
|
30
|
+
* compared — they are what the user asked for, by hand or through the chat —
|
|
31
|
+
* and cerber does not hold back what the user wrote. A finding is compared
|
|
32
|
+
* once for the same reason (`whatToCompare`): the user who rewrote it is the
|
|
33
|
+
* one deciding about it.
|
|
34
|
+
*
|
|
35
|
+
* The match itself stays, with who raised it, where, and why the two are the
|
|
36
|
+
* same defect: the cockpit still shows all of it, and "send anyway" toggles
|
|
37
|
+
* straight back off. A decision already made is left alone — including
|
|
38
|
+
* `reply`, which keeps answering in their thread, in the new words.
|
|
39
|
+
*
|
|
40
|
+
* Only a match that would otherwise be held is decided. On a resolved thread
|
|
41
|
+
* the default already posts, and writing the decision down there would trade a
|
|
42
|
+
* state that follows the thread for one that doesn't: the cockpit would stop
|
|
43
|
+
* saying *why* it posts, and the finding would keep posting if somebody
|
|
44
|
+
* reopened that thread. Nothing is held, so there is nothing to decide — which
|
|
45
|
+
* goes for a dropped comment too, where drop decides it and the cockpit shows
|
|
46
|
+
* no toggle to take it back with.
|
|
47
|
+
*/
|
|
48
|
+
export function rewriteFinding(comment, body) {
|
|
49
|
+
if (body === comment.body)
|
|
50
|
+
return comment;
|
|
51
|
+
const raised = comment.alreadyRaised;
|
|
52
|
+
const held = comment.status !== "dropped" &&
|
|
53
|
+
raised != null &&
|
|
54
|
+
raised.decision === null &&
|
|
55
|
+
sendTreatment({ alreadyRaised: raised }) === "hold";
|
|
56
|
+
if (!held)
|
|
57
|
+
return { ...comment, body };
|
|
58
|
+
return { ...comment, body, alreadyRaised: { ...raised, decision: "send" } };
|
|
59
|
+
}
|
|
22
60
|
/**
|
|
23
61
|
* Which match a comment keeps when several remarks raised the same thing:
|
|
24
62
|
* the liveliest place first, because that is what decides whether posting it
|
package/dist/core/revise.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { rewriteFinding } from "./raised.js";
|
|
2
3
|
/** The review as it stands, for restoring later. */
|
|
3
4
|
export function snapshotReview(artifact, at) {
|
|
4
5
|
return {
|
|
@@ -43,10 +44,17 @@ export function mergeConcurrentEdits(before, after, current) {
|
|
|
43
44
|
};
|
|
44
45
|
const merged = current.comments.map((now) => {
|
|
45
46
|
const chosen = userTouched(now.id, now) ? now : fromTurn.get(now.id) ?? now;
|
|
47
|
+
if (chosen === now)
|
|
48
|
+
return now;
|
|
46
49
|
// Who else raised a comment is the check's to say, not the turn's: a check
|
|
47
50
|
// that landed while the turn ran — and a decision made about it — is only
|
|
48
51
|
// on disk, and the turn's copy predates both.
|
|
49
|
-
|
|
52
|
+
const onDisk = { ...chosen, body: now.body, alreadyRaised: now.alreadyRaised };
|
|
53
|
+
// The rewrite the turn made still decides an undecided match, as any
|
|
54
|
+
// rewrite does — measured against the body on disk, so a turn that changed
|
|
55
|
+
// nothing decides nothing. Never on a sent review: that is a record of what
|
|
56
|
+
// was posted, and nothing on it is re-decided, here or anywhere else.
|
|
57
|
+
return current.sent ? { ...onDisk, body: chosen.body } : rewriteFinding(onDisk, chosen.body);
|
|
50
58
|
});
|
|
51
59
|
// Comments the turn itself added are not in `current` yet. Only those: a
|
|
52
60
|
// comment present in both `before` and `after` but missing from `current` is
|
|
@@ -138,8 +146,10 @@ export function applyRevisions(artifact, revisions, opts = {}) {
|
|
|
138
146
|
...next,
|
|
139
147
|
comments: next.comments.map((c) => c.id === revision.commentId
|
|
140
148
|
? {
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
// A rewrite the user asked for decides a duplicate nobody
|
|
150
|
+
// had decided about: what posts is their words now, not the
|
|
151
|
+
// ones the check compared (`rewriteFinding`).
|
|
152
|
+
...rewriteFinding(c, revision.body),
|
|
143
153
|
// Omitted = the turn is not re-grading; null = "not a finding".
|
|
144
154
|
severity: revision.severity === undefined ? c.severity : revision.severity,
|
|
145
155
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { refreshArtifact, userOwnsStatus } from "../core/refresh.js";
|
|
|
16
16
|
import { withWriter } from "../core/history.js";
|
|
17
17
|
import { TrustRuleError, describeRule, explainRule, parseTrustRule } from "../core/trust.js";
|
|
18
18
|
import { buildReviewPayload, computeCalibration, describeReplyFailures, recordReplies, } from "../core/send.js";
|
|
19
|
-
import { raisedChanged, startRaisedCheck } from "../core/raised.js";
|
|
19
|
+
import { raisedChanged, rewriteFinding, startRaisedCheck } from "../core/raised.js";
|
|
20
20
|
import { checkAlreadyRaised, claimRaisedCheck, finishRaisedCheck, isRaisedCheckRunning, prepareRaisedCheck, releaseRaisedCheck, } from "../runner/raised.js";
|
|
21
21
|
import { listArtifacts, loadArtifact, loadArtifactByKey, saveArtifact, updateArtifactByKey, } from "../core/state.js";
|
|
22
22
|
import { isReviewRunning } from "../runner/inflight.js";
|
|
@@ -465,11 +465,19 @@ export async function buildApp(opts) {
|
|
|
465
465
|
...a,
|
|
466
466
|
comments: a.comments.map((cm) => cm.id === id
|
|
467
467
|
? {
|
|
468
|
-
|
|
468
|
+
// Rewriting a finding somebody else raised decides it: the words
|
|
469
|
+
// that post are the user's, not the ones the check compared. An
|
|
470
|
+
// explicit decision in the same request still wins, below. On a
|
|
471
|
+
// sent review nothing is re-decided — it is a record of what was
|
|
472
|
+
// posted, and `foldRaised` holds the same line.
|
|
473
|
+
...(body.body === undefined
|
|
474
|
+
? cm
|
|
475
|
+
: a.sent
|
|
476
|
+
? { ...cm, body: String(body.body) }
|
|
477
|
+
: rewriteFinding(cm, String(body.body))),
|
|
469
478
|
...(decision.success && body.raisedDecision !== undefined && cm.alreadyRaised
|
|
470
479
|
? { alreadyRaised: { ...cm.alreadyRaised, decision: decision.data } }
|
|
471
480
|
: {}),
|
|
472
|
-
...(body.body !== undefined ? { body: String(body.body) } : {}),
|
|
473
481
|
...(body.severity !== undefined
|
|
474
482
|
? { severity: SeveritySchema.nullable().parse(body.severity) }
|
|
475
483
|
: {}),
|
package/package.json
CHANGED