@link-assistant/hive-mind 1.67.1 → 1.68.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.68.0
4
+
5
+ ### Minor Changes
6
+
7
+ - cbc7033: Switch the test runner to folder-based discovery and deprecate `start-screen` in favour of `--isolated screen` (issue #1758).
8
+ - `scripts/run-tests.mjs` now discovers every `*.mjs` / `*.test.mjs` / `*.test.js` file under `tests/` automatically. The hard-coded `LEGACY_DEFAULT_TESTS` allow-list is gone, so new test files no longer need a runner update to be picked up.
9
+ - New markers complement the existing `@hive-mind-test-suite <name>` marker:
10
+ - `@hive-mind-integration` — skip the file in the default suite; opt in via `--suite integration` or `HIVE_MIND_RUN_INTEGRATION=1`.
11
+ - `@hive-mind-test-skip` — exclude helper / fixture modules from every suite.
12
+ - `tests/integration-guard.mjs` exposes `skipUnlessIntegration(import.meta.url)` for token- or network-heavy tests.
13
+ - `src/start-screen.mjs` and `src/telegram-command-execution.lib.mjs::executeStartScreen` print a one-shot deprecation banner to stderr (suppressible with `HIVE_MIND_SUPPRESS_DEPRECATIONS=1`) recommending `--isolated screen`, which is already the default for `hive`/`solve` invocations through the Telegram bot.
14
+ - Adds regression tests `tests/test-issue-1758-runner-discovery.mjs`, `tests/test-issue-1758-start-screen-deprecation.mjs`, and `tests/test-issue-1758-integration-guard.mjs`.
15
+ - Documents the analysis under `docs/case-studies/issue-1758/`.
16
+
17
+ ## 1.67.2
18
+
19
+ ### Patch Changes
20
+
21
+ - 240231e: Verify the pull request still links to the issue after every work session inside `--watch`, `--auto-restart-until-mergeable`, and `--finalize`, so that an iteration that turns out to be the last one cannot leave the PR un-linked when the AI rewrote the description without a closing keyword.
22
+
3
23
  ## 1.67.1
4
24
 
5
25
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.67.1",
3
+ "version": "1.68.0",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -23,12 +23,21 @@ const { wrapDollarWithGhRetry } = await import('./github-rate-limit.lib.mjs');
23
23
  const $ = wrapDollarWithGhRetry(__rawDollar$);
24
24
  // Import shared library functions
25
25
  const lib = await import('./lib.mjs');
26
- const { log } = lib;
26
+ const { log, cleanErrorMessage } = lib;
27
27
 
28
28
  // Import shared restart utilities
29
29
  const restartShared = await import('./solve.restart-shared.lib.mjs');
30
30
  const { executeToolIteration } = restartShared;
31
31
 
32
+ // Issue #1763: Re-verify PR ↔ issue link after every finalize iteration so a
33
+ // PR body that the AI rewrote without a closing keyword does not survive past
34
+ // the last requirements-check.
35
+ const resultsLib = await import('./solve.results.lib.mjs');
36
+ const { ensurePullRequestIssueLink } = resultsLib;
37
+
38
+ const sentryLib = await import('./sentry.lib.mjs');
39
+ const { reportError } = sentryLib;
40
+
32
41
  /**
33
42
  * Runs finalize requirements-check iterations after the main solve.
34
43
  *
@@ -116,6 +125,34 @@ export const runAutoEnsureRequirements = async ({ issueUrl, owner, repo, issueNu
116
125
  if (ensureResult.pricingInfo) pricingInfo = ensureResult.pricingInfo;
117
126
  }
118
127
 
128
+ // Issue #1763: Re-verify the PR body contains a closing keyword for the
129
+ // issue after every finalize iteration. The AI agent can rewrite the PR
130
+ // description mid-session and any iteration may end up being the last
131
+ // one, so this check cannot be deferred to the top-level verifyResults
132
+ // path.
133
+ if (prNumber && issueNumber && owner && repo) {
134
+ try {
135
+ await log('🔗 Verifying PR issue link after finalize iteration...');
136
+ await ensurePullRequestIssueLink({
137
+ prNumber,
138
+ issueNumber,
139
+ owner,
140
+ repo,
141
+ argv,
142
+ });
143
+ } catch (issueLinkError) {
144
+ reportError(issueLinkError, {
145
+ context: 'ensure_pr_issue_link_finalize_iteration',
146
+ prNumber,
147
+ owner,
148
+ repo,
149
+ ensureIteration,
150
+ operation: 'ensure_pr_issue_link',
151
+ });
152
+ await log(`⚠️ PR issue link check error: ${cleanErrorMessage(issueLinkError)}`, { level: 'warning' });
153
+ }
154
+ }
155
+
119
156
  await log(`✅ FINALIZE iteration ${ensureIteration}/${finalizeCount} complete`);
120
157
  await log('');
121
158
  }
@@ -60,8 +60,10 @@ const toolComments = await import('./tool-comments.lib.mjs');
60
60
  const { READY_TO_MERGE_MARKER, AUTO_RESTART_MARKER, AUTO_MERGED_MARKER, postTrackedComment } = toolComments;
61
61
 
62
62
  // Issue #1728: Per-iteration working session summary attachment helper
63
+ // Issue #1763: Per-iteration PR ↔ issue link verification (so a clobbered
64
+ // PR body is restored before the next stop condition fires).
63
65
  const resultsLib = await import('./solve.results.lib.mjs');
64
- const { maybeAttachWorkingSessionSummary } = resultsLib;
66
+ const { maybeAttachWorkingSessionSummary, ensurePullRequestIssueLink } = resultsLib;
65
67
 
66
68
  // Issue #1574: Interruptible sleep so CTRL+C is never blocked by a lingering timer
67
69
  const { interruptibleSleep } = await import('./interruptible-sleep.lib.mjs');
@@ -942,6 +944,35 @@ No further AI sessions will be started automatically for this run. Please review
942
944
  }
943
945
  }
944
946
 
947
+ // Issue #1763: Re-verify the PR body contains a closing keyword for
948
+ // the issue after every auto-restart-until-mergeable iteration. The
949
+ // AI agent can rewrite the PR description mid-session and any
950
+ // iteration may end up being the last one (mergeable, max-iters,
951
+ // billing limit, etc.), so this check cannot be deferred to the
952
+ // top-level verifyResults path.
953
+ if (prNumber && issueNumber && owner && repo) {
954
+ try {
955
+ await log(formatAligned('🔗', 'Verifying PR issue link after iteration...', '', 2));
956
+ await ensurePullRequestIssueLink({
957
+ prNumber,
958
+ issueNumber,
959
+ owner,
960
+ repo,
961
+ argv,
962
+ });
963
+ } catch (issueLinkError) {
964
+ reportError(issueLinkError, {
965
+ context: 'ensure_pr_issue_link_auto_restart_iteration',
966
+ prNumber,
967
+ owner,
968
+ repo,
969
+ iteration,
970
+ operation: 'ensure_pr_issue_link',
971
+ });
972
+ await log(formatAligned('', `⚠️ PR issue link check error: ${cleanErrorMessage(issueLinkError)}`, '', 2));
973
+ }
974
+ }
975
+
945
976
  await log('');
946
977
  await log(formatAligned('✅', `${argv.tool.toUpperCase()} execution completed:`, 'Checking if PR is now mergeable...'));
947
978
  }
@@ -47,8 +47,11 @@ const toolComments = await import('./tool-comments.lib.mjs');
47
47
  const { AUTO_RESTART_MARKER, postTrackedComment } = toolComments;
48
48
 
49
49
  // Issue #1728: Per-iteration working session summary attachment helper
50
+ // Issue #1763: Per-iteration PR ↔ issue link verification (in case the AI
51
+ // agent overwrites the PR body without a closing keyword and the iteration
52
+ // ends up being the last one).
50
53
  const resultsLib = await import('./solve.results.lib.mjs');
51
- const { maybeAttachWorkingSessionSummary } = resultsLib;
54
+ const { maybeAttachWorkingSessionSummary, ensurePullRequestIssueLink } = resultsLib;
52
55
 
53
56
  /**
54
57
  * Monitor for feedback in a loop and trigger restart when detected
@@ -514,6 +517,34 @@ export const watchForFeedback = async params => {
514
517
  }
515
518
  }
516
519
 
520
+ // Issue #1763: Re-verify the PR body contains a closing keyword for
521
+ // the issue after every iteration. The AI agent can rewrite the PR
522
+ // description mid-session and any iteration may turn out to be the
523
+ // last one (interrupt, hit iteration cap, billing limit, etc.), so
524
+ // we cannot rely on a single end-of-run check.
525
+ if (prNumber && issueNumber && owner && repo) {
526
+ try {
527
+ await log(formatAligned('🔗', 'Verifying PR issue link after iteration...', '', 2));
528
+ await ensurePullRequestIssueLink({
529
+ prNumber,
530
+ issueNumber,
531
+ owner,
532
+ repo,
533
+ argv,
534
+ });
535
+ } catch (issueLinkError) {
536
+ reportError(issueLinkError, {
537
+ context: 'ensure_pr_issue_link_watch_iteration',
538
+ prNumber,
539
+ owner,
540
+ repo,
541
+ autoRestartCount,
542
+ operation: 'ensure_pr_issue_link',
543
+ });
544
+ await log(formatAligned('', `⚠️ PR issue link check error: ${cleanErrorMessage(issueLinkError)}`, '', 2));
545
+ }
546
+ }
547
+
517
548
  await log('');
518
549
  if (isTemporaryWatch) {
519
550
  await log(formatAligned('✅', `${argv.tool.toUpperCase()} execution completed:`, 'Checking for remaining changes...'));
@@ -19,6 +19,24 @@ const printUsage = (log = console.error) => {
19
19
  }
20
20
  };
21
21
 
22
+ /**
23
+ * Print a single-line deprecation notice to stderr the first time it is
24
+ * called per process. Suppressed when `HIVE_MIND_SUPPRESS_DEPRECATIONS=1`.
25
+ *
26
+ * Tracked via a module-scope flag (`deprecationWarned`) so a long-running
27
+ * process emits the banner only once even if `main()` is invoked multiple
28
+ * times in tests.
29
+ *
30
+ * @see https://github.com/link-assistant/hive-mind/issues/1758
31
+ */
32
+ let deprecationWarned = false;
33
+ const printDeprecationBanner = () => {
34
+ if (deprecationWarned) return;
35
+ if (process.env.HIVE_MIND_SUPPRESS_DEPRECATIONS === '1') return;
36
+ deprecationWarned = true;
37
+ console.error('⚠️ start-screen is deprecated; prefer `--isolated screen` (the default in newer hive/solve CLIs). Set HIVE_MIND_SUPPRESS_DEPRECATIONS=1 to silence this warning.');
38
+ };
39
+
22
40
  const createStartScreenYargsConfig = yargsInstance =>
23
41
  yargsInstance
24
42
  .usage(START_SCREEN_USAGE[0])
@@ -288,6 +306,8 @@ async function createOrEnterScreen(sessionName, command, args, autoTerminate = f
288
306
  async function main() {
289
307
  const args = process.argv.slice(2);
290
308
 
309
+ printDeprecationBanner();
310
+
291
311
  if (args.includes('--help') || args.includes('-h')) {
292
312
  printUsage(console.log);
293
313
  process.exit(0);
@@ -4,6 +4,14 @@ import { exec as execCallback } from 'child_process';
4
4
 
5
5
  const exec = promisify(execCallback);
6
6
 
7
+ let deprecationWarned = false;
8
+ function warnStartScreenDeprecated() {
9
+ if (deprecationWarned) return;
10
+ if (process.env.HIVE_MIND_SUPPRESS_DEPRECATIONS === '1') return;
11
+ deprecationWarned = true;
12
+ console.warn('⚠️ executeStartScreen is deprecated; prefer the `--isolated screen` workflow exposed by hive/solve directly. Set HIVE_MIND_SUPPRESS_DEPRECATIONS=1 to silence this warning.');
13
+ }
14
+
7
15
  async function findStartScreenCommand() {
8
16
  try {
9
17
  const { stdout } = await exec('which start-screen');
@@ -68,6 +76,8 @@ function executeWithCommand(startScreenCmd, command, args, verbose = false) {
68
76
  export async function executeStartScreen(command, args, options = {}) {
69
77
  const { verbose = false } = options;
70
78
 
79
+ warnStartScreenDeprecated();
80
+
71
81
  try {
72
82
  const whichPath = await findStartScreenCommand();
73
83