@yemi33/minions 0.1.1900 → 0.1.1902
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/engine/ado.js +10 -0
- package/engine/github.js +94 -1
- package/package.json +1 -1
package/engine/ado.js
CHANGED
|
@@ -1568,4 +1568,14 @@ module.exports = {
|
|
|
1568
1568
|
_resetAdoThrottle, // exported for testing
|
|
1569
1569
|
_setAdoThrottleForTest, // exported for testing
|
|
1570
1570
|
_setAdoTokenForTest, // exported for testing
|
|
1571
|
+
// Exported for unit tests — engine code MUST go through pollPrStatus / reconcilePrs.
|
|
1572
|
+
decodeUrlSegment,
|
|
1573
|
+
stripGitSuffix,
|
|
1574
|
+
encodeAdoPathSegment,
|
|
1575
|
+
buildAdoPrUrlBase,
|
|
1576
|
+
adoRepoCandidateFromParts,
|
|
1577
|
+
parseAdoRepoMetadata,
|
|
1578
|
+
parseCanonicalAdoPrId,
|
|
1579
|
+
isAdoRepairCandidateCompatible,
|
|
1580
|
+
getMissingAdoProjectConfigFields,
|
|
1571
1581
|
};
|
package/engine/github.js
CHANGED
|
@@ -88,6 +88,85 @@ function _isNonActionableComment(c, config = {}) {
|
|
|
88
88
|
return false;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
// W-mp2h696g000a7bc0 — Detect agent self-review-declined no-op comments.
|
|
92
|
+
//
|
|
93
|
+
// Per the documented contract (memory `subject: self-review`, docs/completion-reports.md:83-105):
|
|
94
|
+
// when an agent is dispatched to review a PR they implemented, they post a `gh pr comment`
|
|
95
|
+
// (no `VERDICT:` line) explaining the recusal and complete with `noop:true, verdict:null,
|
|
96
|
+
// needs_rerun:true`. Because `gh` authenticates as the shared PAT user (`yemi33`), the
|
|
97
|
+
// classifier can't tell the noop comment from a real human comment by author alone.
|
|
98
|
+
//
|
|
99
|
+
// Detection requires a stronger signal than author identity. Returns true only when BOTH:
|
|
100
|
+
// 1. Body has NO `VERDICT:` line (we never want to swallow a real verdict comment), AND
|
|
101
|
+
// 2. EITHER the body contains the canonical "Self-review declined" phrase AND references
|
|
102
|
+
// a dispatch id matching `<agent>-<type>-<uid>` that resolves to a same-agent
|
|
103
|
+
// review/implement dispatch in the completed history,
|
|
104
|
+
// OR the most recent review dispatch on this PR was assigned to the same agent as the
|
|
105
|
+
// PR author and completed with the noop:true / verdict:null / needs_rerun:true contract.
|
|
106
|
+
//
|
|
107
|
+
// This is a narrow allowlist for the noop pattern — generic PAT-user comments still flow
|
|
108
|
+
// through `_isNonActionableComment` and trigger `humanFeedback.pendingFix=true` as before.
|
|
109
|
+
function _isAgentSelfReviewDeclinedComment(c, { pr, dispatch } = {}) {
|
|
110
|
+
if (!c || !pr) return false;
|
|
111
|
+
const body = String(c.body || '');
|
|
112
|
+
if (!body) return false;
|
|
113
|
+
if (_hasMinionsReviewVerdict(body)) return false;
|
|
114
|
+
|
|
115
|
+
const prAuthorAgent = String(pr.agent || '').toLowerCase();
|
|
116
|
+
if (!prAuthorAgent) return false;
|
|
117
|
+
|
|
118
|
+
const completed = (dispatch && Array.isArray(dispatch.completed)) ? dispatch.completed : [];
|
|
119
|
+
const isNoopContract = (sc) => {
|
|
120
|
+
if (!sc || typeof sc !== 'object') return false;
|
|
121
|
+
if (sc.noop !== true && sc.noop !== 'true') return false;
|
|
122
|
+
if (sc.verdict !== null && sc.verdict !== undefined && sc.verdict !== '') return false;
|
|
123
|
+
if (sc.needs_rerun !== true && sc.needsRerun !== true) return false;
|
|
124
|
+
return true;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Signal A — explicit "Self-review declined" phrase + verifiable dispatch id pointing
|
|
128
|
+
// at a same-agent review/implement/fix dispatch in the completed history.
|
|
129
|
+
const phraseMatch = /self[\s\-_]+review\s+declined/i.test(body);
|
|
130
|
+
if (phraseMatch) {
|
|
131
|
+
const dispatchIdMatches = body.match(/\b[a-z][a-z0-9]*-[a-z][a-z0-9]*-[a-z0-9]{8,}\b/g) || [];
|
|
132
|
+
for (const id of dispatchIdMatches) {
|
|
133
|
+
const entry = completed.find(d => d && d.id === id);
|
|
134
|
+
if (!entry) continue;
|
|
135
|
+
const agent = String(entry.agent || '').toLowerCase();
|
|
136
|
+
const t = String(entry.type || '').toLowerCase();
|
|
137
|
+
if (agent !== prAuthorAgent) continue;
|
|
138
|
+
if (t === 'review' || t === 'implement' || t === 'implement-large' || t === 'fix') {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Signal B — most recent review dispatch on this PR was assigned to the PR author
|
|
145
|
+
// and completed with the documented noop contract. Catches the case where the agent
|
|
146
|
+
// forgot the canonical phrase but the completion-report contract still flags a noop.
|
|
147
|
+
const prId = String(pr.id || '');
|
|
148
|
+
const prNumberRaw = pr.prNumber;
|
|
149
|
+
const prNumber = prNumberRaw == null ? null : Number(prNumberRaw);
|
|
150
|
+
const reviewDispatches = completed.filter(d => {
|
|
151
|
+
if (!d || String(d.type || '').toLowerCase() !== 'review') return false;
|
|
152
|
+
const dpr = d.meta && d.meta.pr;
|
|
153
|
+
if (!dpr) return false;
|
|
154
|
+
if (prId && String(dpr.id || '') === prId) return true;
|
|
155
|
+
if (prNumber != null && Number(dpr.prNumber) === prNumber) return true;
|
|
156
|
+
return false;
|
|
157
|
+
});
|
|
158
|
+
if (reviewDispatches.length === 0) return false;
|
|
159
|
+
reviewDispatches.sort((a, b) => {
|
|
160
|
+
const ta = String(a.completed_at || a.created_at || '');
|
|
161
|
+
const tb = String(b.completed_at || b.created_at || '');
|
|
162
|
+
return tb.localeCompare(ta);
|
|
163
|
+
});
|
|
164
|
+
const latest = reviewDispatches[0];
|
|
165
|
+
if (!latest) return false;
|
|
166
|
+
if (String(latest.agent || '').toLowerCase() !== prAuthorAgent) return false;
|
|
167
|
+
return isNoopContract(latest.structuredCompletion);
|
|
168
|
+
}
|
|
169
|
+
|
|
91
170
|
// ─── Per-Repo Poll Backoff ──────────────────────────────────────────────────
|
|
92
171
|
// Tracks consecutive poll failures per repo slug to avoid spamming logs when
|
|
93
172
|
// a repo is inaccessible. Backoff doubles each failure: 2min, 4min, 8min, 16min, max 30min.
|
|
@@ -628,6 +707,14 @@ async function pollPrStatus(config) {
|
|
|
628
707
|
// ─── Poll Human Comments on PRs ─────────────────────────────────────────────
|
|
629
708
|
|
|
630
709
|
async function pollPrHumanComments(config) {
|
|
710
|
+
// Load dispatch state once per poll cycle (cached for ~2s by queries.getDispatch).
|
|
711
|
+
// Used by `_isAgentSelfReviewDeclinedComment` to verify dispatch ids referenced in
|
|
712
|
+
// candidate noop comments belong to the PR author.
|
|
713
|
+
const queries = require('./queries');
|
|
714
|
+
const dispatch = (() => {
|
|
715
|
+
try { return queries.getDispatch(); }
|
|
716
|
+
catch { return { pending: [], active: [], completed: [] }; }
|
|
717
|
+
})();
|
|
631
718
|
const totalUpdated = await forEachActiveGhPr(config, async (project, pr, prNum, slug) => {
|
|
632
719
|
// Get issue comments (general PR comments)
|
|
633
720
|
const comments = await ghApi(`/issues/${prNum}/comments`, slug);
|
|
@@ -653,7 +740,12 @@ async function pollPrHumanComments(config) {
|
|
|
653
740
|
for (const c of allComments) {
|
|
654
741
|
const date = c.created_at || c.updated_at || '';
|
|
655
742
|
const dateMs = date ? new Date(date).getTime() : 0;
|
|
656
|
-
|
|
743
|
+
// W-mp2h696g000a7bc0 — agent self-review-declined no-op comments are posted via
|
|
744
|
+
// `gh pr comment` and authenticate as the shared PAT user, so author identity alone
|
|
745
|
+
// can't distinguish them from real human feedback. The narrow allowlist check uses
|
|
746
|
+
// the comment body + dispatch history to identify the documented noop pattern.
|
|
747
|
+
const isNonActionable = _isNonActionableComment(c, config)
|
|
748
|
+
|| _isAgentSelfReviewDeclinedComment(c, { pr, dispatch });
|
|
657
749
|
if (dateMs) allCommentDates.push(date);
|
|
658
750
|
if (isNonActionable) continue;
|
|
659
751
|
const entry = {
|
|
@@ -975,5 +1067,6 @@ module.exports = {
|
|
|
975
1067
|
_hasMinionsReviewVerdict, // exported for testing
|
|
976
1068
|
_isAgentComment, // exported for testing
|
|
977
1069
|
_isNonActionableComment, // exported for testing
|
|
1070
|
+
_isAgentSelfReviewDeclinedComment, // exported for testing
|
|
978
1071
|
_isPreviewStatusComment, // exported for testing
|
|
979
1072
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1902",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|