@lazyingart/agintiflow 0.20.317 → 0.20.318

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.317",
3
+ "version": "0.20.318",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
@@ -6183,6 +6183,84 @@ try {
6183
6183
  sandboxMode: "host",
6184
6184
  }
6185
6185
  );
6186
+ const liveScopedOutsideStatusTestLogCommand = {
6187
+ toolName: "run_command",
6188
+ ok: true,
6189
+ exitCode: 0,
6190
+ args: {
6191
+ command:
6192
+ `cd ${JSON.stringify(workspace)} && ` +
6193
+ `TASK=${scopedRelativeTaskRoot} && ` +
6194
+ `{ date -u +"RUN_AT=%FT%TZ"; PYTHONPATH=src python -m pytest tests/test_wechat_document_reader.py -q; } ` +
6195
+ `> "$TASK/document-reader-test.log" 2>&1; rc=$?; echo "PYTEST_EXIT=$rc"; ` +
6196
+ `cat "$TASK/document-reader-test.log"`,
6197
+ },
6198
+ projectMutationPaths: [],
6199
+ stdout: "PYTEST_EXIT=0\n....... [100%]\n7 passed in 0.04s\n",
6200
+ stderr: "",
6201
+ };
6202
+ recordProjectVerificationOutcome(
6203
+ scopedTaskState,
6204
+ liveScopedOutsideStatusTestLogCommand,
6205
+ {
6206
+ commandCwd: workspace,
6207
+ taskProfile: "qa",
6208
+ allowShellTool: true,
6209
+ sandboxMode: "host",
6210
+ }
6211
+ );
6212
+ const conditionalTestCommand =
6213
+ `cd ${JSON.stringify(workspace)} && ` +
6214
+ `if PYTHONPATH=src python -c "import pytest" 2>/dev/null; then ` +
6215
+ `PYTHONPATH=src python -m pytest tests/test_wechat_document_reader.py -q; else ` +
6216
+ `PYTHONPATH=src python -m unittest tests.test_wechat_document_reader -v; fi`;
6217
+ const conditionalTestState = { meta: { goalContract: { revision: 1 } } };
6218
+ const conditionalTestResult = {
6219
+ toolName: "run_command",
6220
+ ok: true,
6221
+ exitCode: 0,
6222
+ args: { command: conditionalTestCommand },
6223
+ projectMutationPaths: [],
6224
+ stdout: "....... [100%]\n7 passed in 0.04s\n",
6225
+ stderr: "",
6226
+ commandPolicy: classifyCommand(conditionalTestCommand),
6227
+ };
6228
+ recordProjectVerificationOutcome(
6229
+ conditionalTestState,
6230
+ conditionalTestResult,
6231
+ {
6232
+ commandCwd: workspace,
6233
+ taskProfile: "qa",
6234
+ allowShellTool: true,
6235
+ sandboxMode: "host",
6236
+ }
6237
+ );
6238
+ const mutatingConditionalTestCommand = conditionalTestCommand.replace(
6239
+ "tests.test_wechat_document_reader -v; fi",
6240
+ "tests.test_wechat_document_reader -v; touch src/agent-runner.js; fi"
6241
+ );
6242
+ const mutatingConditionalTestState = { meta: { goalContract: { revision: 1 } } };
6243
+ const mutatingConditionalTestResult = {
6244
+ toolName: "run_command",
6245
+ ok: true,
6246
+ exitCode: 0,
6247
+ args: { command: mutatingConditionalTestCommand },
6248
+ projectMutationPaths: [],
6249
+ stdout: "7 passed in 0.04s\n",
6250
+ stderr: "",
6251
+ commandPolicy: classifyCommand(mutatingConditionalTestCommand),
6252
+ };
6253
+ recordProjectVerificationOutcome(
6254
+ mutatingConditionalTestState,
6255
+ mutatingConditionalTestResult,
6256
+ {
6257
+ commandCwd: workspace,
6258
+ taskProfile: "qa",
6259
+ allowShellTool: true,
6260
+ allowDestructive: true,
6261
+ sandboxMode: "host",
6262
+ }
6263
+ );
6186
6264
  const unsafeScopedLogState = {
6187
6265
  goal: scopedTaskGoal,
6188
6266
  commandCwd: workspace,
@@ -6333,6 +6411,14 @@ try {
6333
6411
  liveScopedTeeTestLogCommand.scopedTaskArtifactWrite === true &&
6334
6412
  liveScopedTeeTestLogCommand.projectTest?.passed === true &&
6335
6413
  liveScopedTeeTestLogCommand.projectTest?.explicitExitStatus === 0 &&
6414
+ liveScopedOutsideStatusTestLogCommand.scopedTaskArtifactWrite === true &&
6415
+ liveScopedOutsideStatusTestLogCommand.projectTest?.passed === true &&
6416
+ liveScopedOutsideStatusTestLogCommand.projectTest?.explicitExitStatus === 0 &&
6417
+ conditionalTestState.meta.projectVerification?.mutationRevision === 0 &&
6418
+ conditionalTestResult.boundedReadOnlyTestWrapper === true &&
6419
+ conditionalTestResult.projectTest?.passed === true &&
6420
+ mutatingConditionalTestState.meta.projectVerification?.mutationRevision === 1 &&
6421
+ mutatingConditionalTestResult.projectTest === undefined &&
6336
6422
  unsafeScopedLogState.meta.projectVerification?.mutationRevision === 1 &&
6337
6423
  outsideScopedGroupTestLogCommand.scopedTaskArtifactWrite !== true &&
6338
6424
  sourceWriteAfterScopedLogState.meta.projectVerification?.mutationRevision === 1 &&
@@ -6358,6 +6444,23 @@ try {
6358
6444
  scopedTaskArtifactWrite: liveScopedTeeTestLogCommand.scopedTaskArtifactWrite,
6359
6445
  projectTest: liveScopedTeeTestLogCommand.projectTest,
6360
6446
  },
6447
+ liveScopedOutsideStatusTestLogCommand: {
6448
+ scopedTaskArtifactWrite:
6449
+ liveScopedOutsideStatusTestLogCommand.scopedTaskArtifactWrite,
6450
+ projectTest: liveScopedOutsideStatusTestLogCommand.projectTest,
6451
+ },
6452
+ conditionalTestResult: {
6453
+ mutationRevision:
6454
+ conditionalTestState.meta.projectVerification?.mutationRevision,
6455
+ boundedReadOnlyTestWrapper:
6456
+ conditionalTestResult.boundedReadOnlyTestWrapper,
6457
+ projectTest: conditionalTestResult.projectTest,
6458
+ },
6459
+ mutatingConditionalTestResult: {
6460
+ mutationRevision:
6461
+ mutatingConditionalTestState.meta.projectVerification?.mutationRevision,
6462
+ projectTest: mutatingConditionalTestResult.projectTest,
6463
+ },
6361
6464
  outsideScopedGroupTestLogCommand: {
6362
6465
  mutationRevision:
6363
6466
  unsafeScopedLogState.meta.projectVerification?.mutationRevision,
@@ -5034,6 +5034,183 @@ function shellBraceGroups(value = "") {
5034
5034
  return groups;
5035
5035
  }
5036
5036
 
5037
+ function unquotedShellControlDelimiter(value = "", keyword = "", startIndex = 0) {
5038
+ const text = String(value || "");
5039
+ const expected = String(keyword || "").toLowerCase();
5040
+ let quote = "";
5041
+ let escaped = false;
5042
+ for (let index = Math.max(0, Number(startIndex || 0)); index < text.length; index += 1) {
5043
+ const char = text[index];
5044
+ if (quote === "'") {
5045
+ if (char === "'") quote = "";
5046
+ continue;
5047
+ }
5048
+ if (escaped) {
5049
+ escaped = false;
5050
+ continue;
5051
+ }
5052
+ if (char === "\\") {
5053
+ escaped = true;
5054
+ continue;
5055
+ }
5056
+ if (quote === '"') {
5057
+ if (char === '"') quote = "";
5058
+ continue;
5059
+ }
5060
+ if (char === "'" || char === '"') {
5061
+ quote = char;
5062
+ continue;
5063
+ }
5064
+ if (char !== ";") continue;
5065
+ const suffix = text.slice(index + 1).match(/^\s*([A-Za-z]+)\b/u);
5066
+ if (String(suffix?.[1] || "").toLowerCase() === expected) return index;
5067
+ }
5068
+ return -1;
5069
+ }
5070
+
5071
+ function boundedTestAvailabilityProbe(value = "") {
5072
+ let command = normalizeProjectCommand(value);
5073
+ if (!command || /(?:`|\$\()/u.test(command)) return false;
5074
+ command = command
5075
+ .replace(/(?:^|\s)(?:\d*>>?|&>>?)\s*(?:"\/dev\/null"|'\/dev\/null'|\/dev\/null)/gu, " ")
5076
+ .trim()
5077
+ .replace(/^(?:[A-Za-z_]\w*=(?:"[^"\n]*"|'[^'\n]*'|[^\s;&|]+)\s*)+/u, "")
5078
+ .trim();
5079
+ const tokens = tokenizeShellWords(command);
5080
+ if (
5081
+ ["command", "which"].includes(String(tokens[0] || "")) &&
5082
+ (tokens[0] !== "command" || tokens[1] === "-v")
5083
+ ) {
5084
+ const target = tokens[0] === "command" ? tokens[2] : tokens[1];
5085
+ return Boolean(target && /^[A-Za-z0-9_.+-]+$/u.test(target) && tokens.length <= 3);
5086
+ }
5087
+ const executable = String(tokens[0] || "").split(/[\\/]/u).at(-1) || "";
5088
+ const codeIndex = tokens.indexOf("-c");
5089
+ if (!/^python(?:\d+(?:\.\d+)*)?$/iu.test(executable) || codeIndex < 1) return false;
5090
+ const code = String(tokens[codeIndex + 1] || "").trim();
5091
+ if (!code || codeIndex + 2 !== tokens.length) return false;
5092
+ return /^(?:import\s+[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*|from\s+[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*\s+import\s+[A-Za-z_]\w*)$/u.test(
5093
+ code
5094
+ );
5095
+ }
5096
+
5097
+ function boundedTestCommandClassification(value = "") {
5098
+ const command = normalizeProjectCommand(value);
5099
+ const executableCommand = command
5100
+ .replace(/^(?:[A-Za-z_]\w*=(?:"[^"\n]*"|'[^'\n]*'|[^\s;&|]+)\s*)+/u, "")
5101
+ .trim();
5102
+ return {
5103
+ command,
5104
+ executableCommand,
5105
+ policy: classifyCommand(executableCommand),
5106
+ };
5107
+ }
5108
+
5109
+ function parseBoundedConditionalTestWrapper(value = "") {
5110
+ let normalized = normalizeProjectCommand(value);
5111
+ if (!normalized) return null;
5112
+ normalized = normalized.replace(
5113
+ /^cd\s+(?:"[^"\n]+"|'[^'\n]+'|[^\s;&|]+)\s*&&\s*/u,
5114
+ ""
5115
+ );
5116
+ if (!/^if\s+/u.test(normalized)) return null;
5117
+ const thenAt = unquotedShellControlDelimiter(normalized, "then", 3);
5118
+ if (thenAt < 0) return null;
5119
+ const thenMatch = normalized.slice(thenAt + 1).match(/^\s*then\b/u);
5120
+ if (!thenMatch) return null;
5121
+ const thenBodyStart = thenAt + 1 + thenMatch[0].length;
5122
+ const elseAt = unquotedShellControlDelimiter(normalized, "else", thenBodyStart);
5123
+ if (elseAt < 0) return null;
5124
+ const elseMatch = normalized.slice(elseAt + 1).match(/^\s*else\b/u);
5125
+ if (!elseMatch) return null;
5126
+ const elseBodyStart = elseAt + 1 + elseMatch[0].length;
5127
+ const fiAt = unquotedShellControlDelimiter(normalized, "fi", elseBodyStart);
5128
+ if (fiAt < 0 || !/^\s*fi\s*$/u.test(normalized.slice(fiAt + 1))) return null;
5129
+
5130
+ const condition = normalized.slice(3, thenAt).trim();
5131
+ const thenCommand = normalizeProjectCommand(
5132
+ normalized.slice(thenBodyStart, elseAt).trim()
5133
+ );
5134
+ const elseCommand = normalizeProjectCommand(
5135
+ normalized.slice(elseBodyStart, fiAt).trim()
5136
+ );
5137
+ if (!boundedTestAvailabilityProbe(condition)) return null;
5138
+ const branches = [thenCommand, elseCommand];
5139
+ const classifications = branches.map((candidate) =>
5140
+ boundedTestCommandClassification(candidate)
5141
+ );
5142
+ const policies = classifications.map((candidate) => candidate.policy);
5143
+ if (
5144
+ branches.some((candidate) => {
5145
+ const sequence = parseTopLevelShellSequence(candidate);
5146
+ return (
5147
+ !candidate ||
5148
+ sequence.openQuote ||
5149
+ sequence.trailingEscape ||
5150
+ sequence.trailingSeparator ||
5151
+ sequence.commands.length !== 1
5152
+ );
5153
+ }) ||
5154
+ policies.some((policy) => policy.substantiveTest !== true)
5155
+ ) {
5156
+ return null;
5157
+ }
5158
+ return {
5159
+ command: thenCommand,
5160
+ alternateCommand: elseCommand,
5161
+ wrappedCommand: normalizeProjectCommand(value),
5162
+ statusSource: "process",
5163
+ semanticallyReadOnly: policies.every(
5164
+ (policy, index) =>
5165
+ !commandCanMutateProjectContent(
5166
+ classifications[index].executableCommand,
5167
+ policy
5168
+ )
5169
+ ),
5170
+ };
5171
+ }
5172
+
5173
+ function capturedStatusEcho(value = "", statusVariable = "") {
5174
+ const label =
5175
+ "(?:(?:[A-Z][A-Z0-9_]{0,31}_)?(?:EXIT|STATUS|RESULT)(?:_CODE)?)";
5176
+ const variable = String(statusVariable || "");
5177
+ const statusOperand = variable
5178
+ ? `\\$(?:\\{${variable.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}\\}|${variable.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")})`
5179
+ : "\\$\\?";
5180
+ return String(value || "").trim().match(
5181
+ new RegExp(
5182
+ `^echo\\s+(?:-n\\s+)?["']?(?<label>${label})\\s*[:=]\\s*${statusOperand}["']?$`,
5183
+ "iu"
5184
+ )
5185
+ );
5186
+ }
5187
+
5188
+ function boundedReadOnlyTestPrefixCommand(value = "") {
5189
+ const command = normalizeProjectCommand(value);
5190
+ if (!command) return false;
5191
+ const tokens = tokenizeShellWords(command);
5192
+ if (
5193
+ ["date", "echo", "printf"].includes(String(tokens[0] || "")) &&
5194
+ !/(?:`|\$\()/u.test(command)
5195
+ ) {
5196
+ return true;
5197
+ }
5198
+ if (
5199
+ ["cat", "grep", "head", "stat", "tail", "wc"].includes(
5200
+ String(tokens[0] || "")
5201
+ ) &&
5202
+ !/(?:`|\$\(|[|<>])/u.test(command)
5203
+ ) {
5204
+ return true;
5205
+ }
5206
+ const policy = classifyCommand(command);
5207
+ return (
5208
+ policy.writesWorkspace !== true &&
5209
+ policy.mayMutateProject !== true &&
5210
+ policy.substantiveTest !== true
5211
+ );
5212
+ }
5213
+
5037
5214
  function parseCapturedTestExitStatusWrapper(value = "") {
5038
5215
  const normalized = normalizeProjectCommand(value);
5039
5216
  if (!normalized) return null;
@@ -5044,7 +5221,7 @@ function parseCapturedTestExitStatusWrapper(value = "") {
5044
5221
  sequence.openQuote ||
5045
5222
  sequence.trailingEscape ||
5046
5223
  (sequence.trailingSeparator && sequence.trailingSeparator !== ";") ||
5047
- sequence.commands.length < 3
5224
+ sequence.commands.length < 1
5048
5225
  ) {
5049
5226
  continue;
5050
5227
  }
@@ -5071,7 +5248,7 @@ function parseCapturedTestExitStatusWrapper(value = "") {
5071
5248
  continue;
5072
5249
  }
5073
5250
  const testCommand = normalizeProjectCommand(sequence.commands[index - 1]);
5074
- if (classifyCommand(testCommand).substantiveTest !== true) continue;
5251
+ if (boundedTestCommandClassification(testCommand).policy.substantiveTest !== true) continue;
5075
5252
  return {
5076
5253
  command: testCommand,
5077
5254
  wrappedCommand: normalized,
@@ -5080,10 +5257,110 @@ function parseCapturedTestExitStatusWrapper(value = "") {
5080
5257
  statusVariable,
5081
5258
  };
5082
5259
  }
5260
+
5261
+ for (let index = 0; index < sequence.commands.length - 1; index += 1) {
5262
+ const testCommand = normalizeProjectCommand(sequence.commands[index]);
5263
+ const directEcho = capturedStatusEcho(sequence.commands[index + 1]);
5264
+ if (
5265
+ !directEcho ||
5266
+ boundedTestCommandClassification(testCommand).policy.substantiveTest !== true
5267
+ ) {
5268
+ continue;
5269
+ }
5270
+ if (
5271
+ sequence.commands.slice(0, index).some(
5272
+ (candidate) => !boundedReadOnlyTestPrefixCommand(candidate)
5273
+ ) ||
5274
+ sequence.commands.length !== index + 2
5275
+ ) {
5276
+ continue;
5277
+ }
5278
+ const outsideGroup = `${normalized.slice(0, group.start)} ${normalized.slice(group.end + 1)}`;
5279
+ if (
5280
+ /\b(?:echo|printf)\b[^;&|\n]{0,240}\b(?:EXIT|STATUS|RESULT)(?:_CODE)?\s*[:=]/iu.test(
5281
+ outsideGroup
5282
+ )
5283
+ ) {
5284
+ continue;
5285
+ }
5286
+ return {
5287
+ command: testCommand,
5288
+ wrappedCommand: normalized,
5289
+ label: String(directEcho.groups?.label || "").toUpperCase(),
5290
+ statusSource: "captured-group",
5291
+ };
5292
+ }
5293
+
5294
+ const bodyCommands = sequence.commands.map((candidate) =>
5295
+ normalizeProjectCommand(candidate)
5296
+ );
5297
+ const testCommand = bodyCommands.at(-1) || "";
5298
+ if (
5299
+ boundedTestCommandClassification(testCommand).policy.substantiveTest !== true ||
5300
+ bodyCommands.slice(0, -1).some(
5301
+ (candidate) => !boundedReadOnlyTestPrefixCommand(candidate)
5302
+ )
5303
+ ) {
5304
+ continue;
5305
+ }
5306
+ const suffix = normalized
5307
+ .slice(group.end + 1)
5308
+ .replace(
5309
+ /^\s*(?:(?:\d*>>?|&>>?)\s*(?:"[^"\n]+"|'[^'\n]+'|&\d+|[^\s;&|]+)\s*)+/u,
5310
+ ""
5311
+ )
5312
+ .replace(/^\s*;\s*/u, "");
5313
+ const suffixSequence = parseTopLevelShellSequence(suffix);
5314
+ if (
5315
+ !suffix ||
5316
+ suffixSequence.openQuote ||
5317
+ suffixSequence.trailingEscape ||
5318
+ suffixSequence.trailingSeparator ||
5319
+ suffixSequence.commands.length < 2
5320
+ ) {
5321
+ continue;
5322
+ }
5323
+ const statusAssignment = String(suffixSequence.commands[0] || "")
5324
+ .trim()
5325
+ .match(/^([A-Za-z_]\w*)\s*=\s*\$\?$/u);
5326
+ if (!statusAssignment) continue;
5327
+ const statusEcho = capturedStatusEcho(
5328
+ suffixSequence.commands[1],
5329
+ statusAssignment[1]
5330
+ );
5331
+ if (!statusEcho) continue;
5332
+ if (
5333
+ suffixSequence.commands.slice(2).some((candidate) => {
5334
+ if (
5335
+ /\b(?:echo|printf)\b[^;&|\n]{0,240}\b(?:[A-Z][A-Z0-9_]{0,31}_)?(?:EXIT|STATUS|RESULT)(?:_CODE)?\s*[:=]/iu.test(
5336
+ candidate
5337
+ )
5338
+ ) {
5339
+ return true;
5340
+ }
5341
+ return !boundedReadOnlyTestPrefixCommand(candidate);
5342
+ })
5343
+ ) {
5344
+ continue;
5345
+ }
5346
+ return {
5347
+ command: testCommand,
5348
+ wrappedCommand: normalized,
5349
+ label: String(statusEcho.groups?.label || "").toUpperCase(),
5350
+ statusSource: "captured-group",
5351
+ statusVariable: statusAssignment[1],
5352
+ };
5083
5353
  }
5084
5354
  return null;
5085
5355
  }
5086
5356
 
5357
+ function projectTestCommandWrapper(command = "") {
5358
+ return (
5359
+ parseCapturedTestExitStatusWrapper(command) ||
5360
+ parseBoundedConditionalTestWrapper(command)
5361
+ );
5362
+ }
5363
+
5087
5364
  function projectExitStatusWrapper(command = "") {
5088
5365
  return (
5089
5366
  parseNonMutatingExitStatusWrapper(command) ||
@@ -5161,6 +5438,7 @@ function isRetainedExactVerificationCommand(command = "", config = {}) {
5161
5438
  export function isSubstantiveTestCommand(command = "", config = {}) {
5162
5439
  if (isRetainedExactVerificationCommand(command, config)) return true;
5163
5440
  const normalized = normalizeProjectCommand(normalizeCommandForPolicy(command, config));
5441
+ if (projectTestCommandWrapper(normalized)) return true;
5164
5442
  const text = projectExitStatusWrapper(normalized)?.command || normalized;
5165
5443
  if (!text) return false;
5166
5444
  const classification = classifyCommand(text);
@@ -5278,9 +5556,16 @@ function explicitExitProbeStatus(command = "", result = {}) {
5278
5556
 
5279
5557
  let status = parseExplicitExitStatus(result.stdout || "");
5280
5558
  if (status === null && exitProbe.statusSource === "captured-group") {
5559
+ const escapedLabel = String(exitProbe.label || "")
5560
+ .replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
5281
5561
  const matches = [
5282
5562
  ...String(result.stdout || "").matchAll(
5283
- /(?:^|\n)\s*(?:EXIT|STATUS|RESULT)(?:_CODE)?\s*[:=]\s*(-?\d+)\s*(?=\n|$)/giu
5563
+ escapedLabel
5564
+ ? new RegExp(
5565
+ `(?:^|\\n)\\s*${escapedLabel}\\s*[:=]\\s*(-?\\d+)\\s*(?=\\n|$)`,
5566
+ "giu"
5567
+ )
5568
+ : /(?:^|\n)\s*(?:EXIT|STATUS|RESULT)(?:_CODE)?\s*[:=]\s*(-?\d+)\s*(?=\n|$)/giu
5284
5569
  ),
5285
5570
  ];
5286
5571
  if (matches.length) status = Number(matches.at(-1)[1]);
@@ -6853,6 +7138,7 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6853
7138
 
6854
7139
  if (toolName === "run_command") {
6855
7140
  const command = normalizeProjectCommand(toolResult.args?.command || "");
7141
+ const testCommandWrapper = projectTestCommandWrapper(command);
6856
7142
  const retainedExactVerification = isRetainedExactVerificationCommand(
6857
7143
  command,
6858
7144
  config
@@ -6966,6 +7252,7 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6966
7252
  command &&
6967
7253
  toolResult.blocked !== true &&
6968
7254
  !retainedExactVerification &&
7255
+ testCommandWrapper?.semanticallyReadOnly !== true &&
6969
7256
  !disposableGeneratedCleanup &&
6970
7257
  !disposableGeneratedVerificationSideEffects &&
6971
7258
  !scopedTaskArtifactWrite &&
@@ -6990,6 +7277,9 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6990
7277
  if (readOnlyArtifactValidation) {
6991
7278
  toolResult.readOnlyArtifactValidation = true;
6992
7279
  }
7280
+ if (testCommandWrapper?.semanticallyReadOnly === true) {
7281
+ toolResult.boundedReadOnlyTestWrapper = true;
7282
+ }
6993
7283
  let requiredBatch = currentRequiredCommandBatch(verification, requiredCommands);
6994
7284
  const activeExecutionContract = state.meta?.activeExecutionContract;
6995
7285
  const activeTurnCommands =
@@ -13043,6 +13333,7 @@ function commandWritesOnlyEvidenceMatching(command = "", pathMatches = () => fal
13043
13333
  .trim();
13044
13334
  if (!withoutAssignments) return true;
13045
13335
  const commandTokens = tokenizeShellWords(withoutAssignments);
13336
+ if (boundedReadOnlyTestPrefixCommand(withoutAssignments)) return true;
13046
13337
  const teeTargets = evidenceTargetsFromTee(commandTokens);
13047
13338
  if (teeTargets.length) {
13048
13339
  const safe = teeTargets.every(pathMatches);