@lazyingart/agintiflow 0.20.291 → 0.20.296

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.
@@ -431,3 +431,121 @@ punctuation search literal, added regression coverage, passed four unit tests,
431
431
  and committed a clean tree at `b956470`. The independent migration-safety
432
432
  contract also passed. The complete npm suite passes, and the runtime fix is
433
433
  released in AgInTiFlow `0.20.291`.
434
+
435
+ ### Explicit test evidence and security-review completion
436
+
437
+ `security-labshare-050` asked a fresh DeepSeek Pro session to harden a small
438
+ standard-library laboratory service from an imperfect, outcome-level prompt.
439
+ The agent removed a default credential, bounded artifact and dataset paths,
440
+ kept export execution shell-free, redacted audit credentials, protected the
441
+ public status response, added focused tests, and committed a clean repair. The
442
+ hidden acceptance contract then exposed a remaining audit-log injection path:
443
+ newlines in an actor or artifact field could forge additional physical records.
444
+
445
+ During the retained-session repair, AgInTi fixed the production source but
446
+ committed it before adding the explicitly requested regression test. This was a
447
+ runtime contract defect, not a model-quality failure. The mutation parser did
448
+ not recognize contextual requests such as "add a regression test," and the SCS
449
+ evidence contract did not convert "run the tests" into a fresh test obligation.
450
+ Consequently, source and Git evidence could satisfy the phase too early.
451
+
452
+ AgInTiFlow now recognizes explicit English and Chinese test-file mutations,
453
+ requires fresh test evidence for explicit run/rerun-test requests, and keeps
454
+ task-owned commit completion closed until that evidence exists. The regression
455
+ suite reproduces the stale persisted contract and proves no commit is offered
456
+ after source mutation but before a fresh passing test. It avoids broad keyword
457
+ matching, so phrases such as "create a canvas preview for this smoke test" do
458
+ not invent a test-file mutation.
459
+
460
+ The same release accepts genuinely observational Git evidence when no
461
+ consequential Git action is required, permits only the exact bounded Python
462
+ cache-cleanup forms used by project hygiene, and treats masked values such as
463
+ `token=***` as safe status evidence without weakening detection of real
464
+ credential assignments. The security-review skill now covers control-character
465
+ log injection and requires a standalone note to distinguish deployment
466
+ boundary, threat model, controls, residual risks, non-goals, and verification.
467
+
468
+ The retained session completed `SECURITY.md` after a valid secret-content
469
+ block, passed the exact hidden contract and all 13 visible tests, and left a
470
+ clean target repository at `ef3c099`. Focused regressions and the complete npm
471
+ suite pass. These runtime and skill fixes are released in AgInTiFlow
472
+ `0.20.292`.
473
+
474
+ ### Retained storage live authority poisoning
475
+
476
+ `retained-storage-live-poison-047` exercised the native integration storage
477
+ authority after independent audit addenda, using a dictated but realistic
478
+ operator prompt. The defect was an AgInTiFlow core integration-storage
479
+ authority gap: live retained handles and named bindings were mostly checked,
480
+ but the public limitation record omitted the generic `resolveBeneath:false`
481
+ fact, and protected file success paths could return after cleanup awaits
482
+ without one final permanent-poison check.
483
+
484
+ The storage authority now reports `procfsRequired:true`,
485
+ `resolveBeneath:false`, and `noXdev:false` truthfully across retained
486
+ directory/file/lock limitation records. Live retained owner, mode, fstat, and
487
+ named-binding divergence remains `INTEGRATION_STORAGE_POISONED`, not an
488
+ availability failure, and protected read/write/sync paths check permanent
489
+ poison again immediately before returning success.
490
+
491
+ External verification used the module-mocked retained-storage smoke for live
492
+ fstat, owner, mode, named-binding, concurrent poison, close/admission, cleanup,
493
+ and residual-FD adversaries. Retained durable-common, retained file-lock,
494
+ production-mount, integration authorities, runtime authority,
495
+ session-persistence, syntax, and whitespace checks passed on the working tree.
496
+
497
+ ### Authoritative read-only routine first
498
+
499
+ `authoritative-readonly-routine-001` reproduced the LabCanvas status failure
500
+ from sessions `web-agent-labcanvas-cb6fe7cd-3464-42f7-99e4-b24c376e0115` and
501
+ `web-agent-labcanvas-6d0b04bc-79e8-4fd8-87b4-4d8e651dcd72`. The first session
502
+ had no selected routine and spent 25 model requests and 23 tools on broad
503
+ workspace/private-store exploration. The second session received a disclosed
504
+ canonical read-only routine, but AgInTiFlow still kept
505
+ `requiredProjectCommands` empty, then continued through 32 model requests and
506
+ 32 tools after the status snapshot.
507
+
508
+ The defect was an AgInTiFlow core execution-contract/SCS handoff gap, with a
509
+ separate upstream routine-disclosure improvement already handled by
510
+ AgenticApp. AgInTiFlow now promotes an authoritative read-only routine's first
511
+ safe command into the exact required command batch, records an observed
512
+ nonzero status snapshot as read-only evidence instead of a failed verifier,
513
+ retains forbidden raw/private evidence scopes, and closes the following turn to
514
+ `finish` once normal SCS evidence is sufficient.
515
+
516
+ The focused regression uses a weak status prompt and a project-neutral
517
+ `sample-status` routine. It proves the first model turn sees only the exact
518
+ status command plus `finish`, a JSON status command exiting 1 still satisfies
519
+ the read-only evidence contract, the second turn is finish-only, and no
520
+ private/raw exploratory command is dispatched. `npm run smoke:progressive-tools`,
521
+ `npm run smoke:scs-evidence`, `npm run smoke:truthful-completion`, and
522
+ `npm run check` pass on the working tree.
523
+
524
+ Fresh compact retest `web-agent-labcanvas-2ebe5aef-83c8-4134-abcd-bec630a4ecb9`
525
+ reduced the flow to 6 model requests and 5 tool starts and used the canonical
526
+ compact status command, but both valid `finish` calls were rejected because the
527
+ human-facing status said `Still retrying: echomind_daily_pdf` and named a
528
+ `next attempt`. That was still an AgInTiFlow core completion-semantic defect:
529
+ the validator treated an observed external retry state as the agent promising
530
+ unfinished work. The completion predicate now rejects pending work only when it
531
+ is tied to the current task/report/validation/change or an agent-promised
532
+ future action. Read-only status answers may truthfully report external
533
+ pending/retrying/next-attempt state after sufficient evidence exists.
534
+
535
+ Installed retest `web-agent-labcanvas-5e6eb6b6-fb8d-419d-a549-aa4098252064`
536
+ then proved the next core gap. AgInTiFlow ran the same canonical compact
537
+ health command exactly once and avoided raw/private exploration, but the
538
+ finish-only verified-completion turns kept returning empty content and no tool
539
+ calls with `finishReason=length` at the inherited 768-token output cap. After
540
+ the bounded empty-response repair was exhausted, the runtime emitted the
541
+ generic verified fallback `Evidence: command` instead of summarizing the
542
+ authoritative JSON status snapshot.
543
+
544
+ That defect is still AgInTiFlow core completion-loop behavior, not AgenticApp
545
+ or a WeChat-specific routine. Verified-completion turns now have their own
546
+ final-answer output floor and explicit finish-only instruction. If a provider
547
+ still returns empty final responses after verified evidence exists, the
548
+ fallback derives a concise public answer from the successful bounded command
549
+ output. JSON status snapshots summarize visible delivered/retrying schedule
550
+ states, ingress reachability, queue counts, and top-level health while keeping
551
+ the original SCS checks for genuinely unfinished agent work.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.291",
3
+ "version": "0.20.296",
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",
@@ -1680,6 +1680,45 @@ try {
1680
1680
  mixedValidationCleanupAdvice.autoRecover === true,
1681
1681
  "unrequested cleanup should be skipped without pausing substantive work"
1682
1682
  );
1683
+ const pythonCacheCleanupArgs = {
1684
+ command:
1685
+ "find . -type d -name __pycache__ -prune -exec rm -rf {} + ; find . -type f -name '*.pyc' -delete; git status --short",
1686
+ };
1687
+ assert(
1688
+ isUnrequestedCleanupCommand(
1689
+ "run_command",
1690
+ pythonCacheCleanupArgs,
1691
+ { goal: "Repair the service, run its tests, and commit the intentional work." },
1692
+ {}
1693
+ ),
1694
+ "post-acceptance Python cache deletion was not recognized as optional housekeeping"
1695
+ );
1696
+ const pythonCacheCleanupAdvice = buildPermissionAdvice({
1697
+ toolName: "run_command",
1698
+ args: pythonCacheCleanupArgs,
1699
+ guard: {
1700
+ category: "destructive",
1701
+ reason: "Destructive shell commands require Allow destructive actions.",
1702
+ },
1703
+ config: {
1704
+ ...dockerWorkspacePolicy,
1705
+ goal: "Repair the service, run its tests, and commit the intentional work.",
1706
+ },
1707
+ state: { sessionId: "coding-python-cache-cleanup-smoke" },
1708
+ });
1709
+ assert(
1710
+ pythonCacheCleanupAdvice.autoRecover === true,
1711
+ "ignored Python cache cleanup should be skipped without pausing a completed task"
1712
+ );
1713
+ assert(
1714
+ !isUnrequestedCleanupCommand(
1715
+ "run_command",
1716
+ { command: "find . -type f -delete" },
1717
+ { goal: "Repair the service and commit the result." },
1718
+ {}
1719
+ ),
1720
+ "broad find deletion was incorrectly classified as optional Python cache cleanup"
1721
+ );
1683
1722
  assert(
1684
1723
  !isUnrequestedCleanupCommand(
1685
1724
  "run_command",
@@ -9251,7 +9251,7 @@ try {
9251
9251
  const verifiedCompletionText = JSON.stringify(verifiedCompletionRequest?.messages || []);
9252
9252
  assert(
9253
9253
  verifiedCompletionRequest?.mode === "verified-completion" &&
9254
- verifiedCompletionRequest?.maxOutputTokens === 768,
9254
+ verifiedCompletionRequest?.maxOutputTokens === 2048,
9255
9255
  "fresh passing evidence did not narrow the final response turn"
9256
9256
  );
9257
9257
  assert(
@@ -13546,6 +13546,91 @@ Do not prefix, suffix, wrap, redirect, pipe, or combine that validator command.`
13546
13546
  "a partial multi-artifact task entered task-owned Git completion before its deliverables existed"
13547
13547
  );
13548
13548
 
13549
+ const securityWorkspace = path.join(tempRoot, "security-test-before-commit");
13550
+ await fs.mkdir(securityWorkspace, { recursive: true });
13551
+ await fs.writeFile(path.join(securityWorkspace, "labshare.py"), "def record_access():\n pass\n");
13552
+ const securityGoal = [
13553
+ "Independent acceptance failed: audit fields permit newline log injection.",
13554
+ "Add a focused regression test, fix the root cause, run the full visible tests,",
13555
+ "review the diff, and commit only this corrective work.",
13556
+ ].join(" ");
13557
+ const securityState = {
13558
+ goal: securityGoal,
13559
+ commandCwd: securityWorkspace,
13560
+ plan: "Patch the source and regression test, run the full suite, then commit the verified work.",
13561
+ messages: [
13562
+ {
13563
+ role: "tool",
13564
+ content: JSON.stringify({
13565
+ ok: true,
13566
+ toolName: "apply_patch",
13567
+ path: "labshare.py",
13568
+ goalRevision: 1,
13569
+ projectMutationRevision: 1,
13570
+ }),
13571
+ },
13572
+ {
13573
+ role: "tool",
13574
+ content: JSON.stringify({
13575
+ ok: true,
13576
+ toolName: "run_command",
13577
+ exitCode: 0,
13578
+ args: { command: "git status --short" },
13579
+ stdout: " M labshare.py\n",
13580
+ goalRevision: 1,
13581
+ projectMutationRevision: 1,
13582
+ }),
13583
+ },
13584
+ ],
13585
+ meta: {
13586
+ taskProfile: "security",
13587
+ goalContract: {
13588
+ revision: 1,
13589
+ currentRequest: securityGoal,
13590
+ taskGoal: securityGoal,
13591
+ activeGoal: securityGoal,
13592
+ activeGoalRevision: 1,
13593
+ history: [{ revision: 1, refreshExecutionContract: true }],
13594
+ lifecycle: [{ at: new Date(Date.now() - 2000).toISOString() }],
13595
+ },
13596
+ // Reproduce the pre-fix persisted contract that recognized a mutation
13597
+ // but failed to classify the requested regression test as file work.
13598
+ activeExecutionContract: {
13599
+ revision: 1,
13600
+ startedMutationRevision: 0,
13601
+ materialMutationRevision: 1,
13602
+ requiresWorkspaceMutation: true,
13603
+ requiresFileMutation: false,
13604
+ },
13605
+ projectVerification: {
13606
+ mutationRevision: 1,
13607
+ mutationHistory: [
13608
+ {
13609
+ revision: 1,
13610
+ at: new Date().toISOString(),
13611
+ toolName: "apply_patch",
13612
+ paths: ["labshare.py"],
13613
+ goalRevision: 1,
13614
+ },
13615
+ ],
13616
+ },
13617
+ },
13618
+ };
13619
+ const securityCompletionContract = completionTaskContract(
13620
+ { goal: securityGoal, taskProfile: "security", commandCwd: securityWorkspace },
13621
+ securityState
13622
+ );
13623
+ const preTestSecurityRuntime = nextStepRuntimeConfig(
13624
+ { goal: securityGoal, taskProfile: "security", commandCwd: securityWorkspace },
13625
+ securityState
13626
+ );
13627
+ assert(
13628
+ securityCompletionContract.requiresFileMutation === true &&
13629
+ securityCompletionContract.requiredEvidence.some((item) => item.category === "test") &&
13630
+ preTestSecurityRuntime.taskOwnedCommitPending !== true,
13631
+ "a regression-test repair entered task-owned Git completion before fresh test evidence"
13632
+ );
13633
+
13549
13634
  await fs.rm(tempRoot, { recursive: true, force: true });
13550
13635
  console.log("smoke-dynamic-step-budget ok");
13551
13636
  } catch (error) {
@@ -538,6 +538,7 @@ async function main() {
538
538
  assert.equal(authority.attestation.limitations.preEnablePrimitive, true);
539
539
  assert.equal(authority.attestation.limitations.procfsRequired, true);
540
540
  assert.equal(authority.attestation.limitations.openat2ResolveBeneath, false);
541
+ assert.equal(authority.attestation.limitations.resolveBeneath, false);
541
542
  assert.equal(authority.attestation.limitations.noXdev, false);
542
543
 
543
544
  const identity = await authority.identity();
@@ -930,6 +930,22 @@ const refreshedSecurityContract = deriveScsTaskContract({
930
930
  goal: "Fix the security issue and run the regression tests.",
931
931
  taskProfile: "security",
932
932
  });
933
+ const correctiveSecurityContract = deriveScsTaskContract({
934
+ goal: [
935
+ "Independent acceptance failed: audit fields permit newline log injection.",
936
+ "Reopen the exact repair, add a focused regression test, fix the root cause,",
937
+ "run the full visible tests, review the diff, and commit only this corrective work.",
938
+ ].join(" "),
939
+ taskProfile: "security",
940
+ });
941
+ assert(
942
+ correctiveSecurityContract.requiresWorkspaceMutation === true &&
943
+ correctiveSecurityContract.requiresFileMutation === true &&
944
+ correctiveSecurityContract.requiredEvidence.some((item) => item.category === "file") &&
945
+ correctiveSecurityContract.requiredEvidence.some((item) => item.category === "test") &&
946
+ correctiveSecurityContract.requiredGitActions.includes("commit"),
947
+ "a requested regression-test repair could enter Git completion without fresh file and test evidence"
948
+ );
933
949
  const staleContractFinish = await reviewScsFinish(
934
950
  { mock: true },
935
951
  { provider: "mock", model: "mock-agent", taskProfile: "security" },
@@ -964,6 +980,11 @@ const staleContractFinish = await reviewScsFinish(
964
980
  exitCode: 0,
965
981
  args: { command: "python -m unittest discover -s tests -v" },
966
982
  stdout: "Ran 10 tests\nOK",
983
+ projectTest: {
984
+ passed: true,
985
+ command: "python -m unittest discover -s tests -v",
986
+ mutationRevision: 0,
987
+ },
967
988
  }),
968
989
  },
969
990
  ],
@@ -1056,6 +1077,20 @@ assert(
1056
1077
  evaluateScsEvidence(commitContract, gitStatusLedger).missingGitActions.includes("commit"),
1057
1078
  "the completion deficit did not identify the missing commit"
1058
1079
  );
1080
+ const readOnlyGitContract = {
1081
+ requiresExternalEvidence: true,
1082
+ requiredEvidence: [
1083
+ { id: "command", category: "command", description: "read-only verification" },
1084
+ { id: "git", category: "git", description: "existing commit verification" },
1085
+ ],
1086
+ requiredToolCalls: [],
1087
+ requiredGitActions: [],
1088
+ requiredProjectCommands: [],
1089
+ };
1090
+ assert(
1091
+ evaluateScsEvidence(readOnlyGitContract, gitStatusLedger).ok,
1092
+ "read-only Git evidence was rejected when the contract required no consequential Git action"
1093
+ );
1059
1094
  const committedEvaluation = evaluateScsEvidence(commitContract, gitCommitLedger);
1060
1095
  assert(
1061
1096
  committedEvaluation.missingGitActions.length === 0 &&
@@ -1231,7 +1266,14 @@ const checkedCodeEval = evaluateScsEvidence(
1231
1266
  messages: [
1232
1267
  {
1233
1268
  role: "tool",
1234
- content: JSON.stringify({ toolName: "run_command", ok: true, exitCode: 0, args: { command: "npm test" }, stdout: "ok" }),
1269
+ content: JSON.stringify({
1270
+ toolName: "run_command",
1271
+ ok: true,
1272
+ exitCode: 0,
1273
+ args: { command: "npm test" },
1274
+ stdout: "ok",
1275
+ projectTest: { passed: true, command: "npm test", mutationRevision: 0 },
1276
+ }),
1235
1277
  },
1236
1278
  ],
1237
1279
  },
@@ -5501,6 +5501,55 @@ assertStrict.equal(
5501
5501
  undefined,
5502
5502
  "an explicit task artifact root still activated generic repository grounding"
5503
5503
  );
5504
+ const scopedPathWriteDescriptor = {
5505
+ type: "function",
5506
+ function: {
5507
+ name: "write_file",
5508
+ description: "Write one workspace file.",
5509
+ parameters: {
5510
+ type: "object",
5511
+ properties: {
5512
+ path: { type: "string" },
5513
+ content: { type: "string" },
5514
+ },
5515
+ required: ["path", "content"],
5516
+ additionalProperties: false,
5517
+ },
5518
+ },
5519
+ };
5520
+ const scopedPathSurface = [
5521
+ ...allTools.filter((item) => item.function.name !== "write_file"),
5522
+ scopedPathWriteDescriptor,
5523
+ ];
5524
+ const scopedPathTools = selectProgressiveTools(scopedPathSurface, {
5525
+ config: {
5526
+ provider: "deepseek",
5527
+ progressiveTools: true,
5528
+ scopedArtifactTask: true,
5529
+ scopedArtifactRoot: scopedRuntime.scopedArtifactRoot,
5530
+ commandCwd: "/workspace",
5531
+ },
5532
+ goal: scopedRuntimePrompt,
5533
+ profile: "auto",
5534
+ messages: [{ role: "user", content: scopedRuntimePrompt }],
5535
+ });
5536
+ const scopedWritePathPattern = scopedPathTools.find(
5537
+ (item) => item.function.name === "write_file"
5538
+ )?.function?.parameters?.properties?.path?.pattern;
5539
+ assert(scopedWritePathPattern, "scoped artifact write tool omitted its path contract");
5540
+ const scopedWritePathRegex = new RegExp(scopedWritePathPattern);
5541
+ assert(
5542
+ scopedWritePathRegex.test("output/wechat_worker/task/report.md"),
5543
+ "scoped artifact write contract rejected its workspace-relative path"
5544
+ );
5545
+ assert(
5546
+ scopedWritePathRegex.test("/workspace/output/wechat_worker/task/report.md"),
5547
+ "scoped artifact write contract rejected the host-supplied exact absolute path"
5548
+ );
5549
+ assert(
5550
+ !scopedWritePathRegex.test("/workspace/output/wechat_worker/other/report.md"),
5551
+ "scoped artifact write contract accepted a sibling task path"
5552
+ );
5504
5553
 
5505
5554
  const longScopedTaskRoot = path.join(repoRoot, "output/wechat_worker/long-scoped-task");
5506
5555
  const longScopedScopeLine = `AGINTI_EVIDENCE_SCOPE_JSON: ${JSON.stringify({
@@ -7288,6 +7337,160 @@ async function runToolContractCase({
7288
7337
  }
7289
7338
  }
7290
7339
 
7340
+ function weakAuthoritativeRoutineGoal(command) {
7341
+ return [
7342
+ "AGINTI_EVIDENCE_SCOPE_JSON: {\"mode\":\"task\",\"request\":\"tell me phone and schedule status, read-only, dont send or change anything\"}",
7343
+ "Matched established routines",
7344
+ `- \`sample-status\` ready=true; commands=[${JSON.stringify(command)}, "node scripts/secondary-status.js --json"]; outputs=["status"]; guidance=For a read-only phone, message-intake, queue, or schedule question, run the canonical \`${command}\` command first. Treat the current status snapshot as authoritative. Do not inspect raw private stores, and do not send or mutate anything.`,
7345
+ "Operating contract",
7346
+ "- Forbidden evidence: raw/private ledgers are out of scope for this read-only answer.",
7347
+ ].join("\n");
7348
+ }
7349
+
7350
+ const authoritativeRoutineCommand = "node scripts/status.js status --json";
7351
+ const authoritativeRoutineGoal = weakAuthoritativeRoutineGoal(
7352
+ authoritativeRoutineCommand
7353
+ );
7354
+ const authoritativeRoutineContract = completionTaskContract(
7355
+ {
7356
+ goal: authoritativeRoutineGoal,
7357
+ taskProfile: "auto",
7358
+ },
7359
+ {
7360
+ goal: authoritativeRoutineGoal,
7361
+ meta: {
7362
+ taskProfile: "auto",
7363
+ goalContract: {
7364
+ revision: 1,
7365
+ currentRequest: authoritativeRoutineGoal,
7366
+ },
7367
+ },
7368
+ }
7369
+ );
7370
+ assertStrict.deepEqual(
7371
+ authoritativeRoutineContract.requiredProjectCommands,
7372
+ [authoritativeRoutineCommand],
7373
+ "authoritative read-only routine was not promoted to an exact required command"
7374
+ );
7375
+ assertStrict.deepEqual(
7376
+ authoritativeRoutineContract.authoritativeRoutine?.forbiddenEvidenceScopes,
7377
+ ["private", "raw"],
7378
+ "authoritative read-only routine did not retain forbidden evidence scopes"
7379
+ );
7380
+
7381
+ const hostedRequiredRoutineTools = selectProgressiveTools(allTools, {
7382
+ config: {
7383
+ provider: "deepseek",
7384
+ toolSurfacePolicy: "full",
7385
+ requiredProjectCommandPending: true,
7386
+ requiredProjectCommand: authoritativeRoutineCommand,
7387
+ },
7388
+ goal: authoritativeRoutineGoal,
7389
+ profile: "auto",
7390
+ });
7391
+ sameNames(
7392
+ hostedRequiredRoutineTools,
7393
+ ["run_command", "finish"],
7394
+ "hosted/full policy did not constrain an authoritative routine first command"
7395
+ );
7396
+ assertStrict.deepEqual(
7397
+ enumFor(hostedRequiredRoutineTools, "run_command", "command"),
7398
+ [authoritativeRoutineCommand],
7399
+ "authoritative routine command was not exposed as an exact enum"
7400
+ );
7401
+
7402
+ let authoritativeRoutineStep = 0;
7403
+ const authoritativeRoutineRun = await runToolContractCase({
7404
+ id: "authoritative-readonly-routine",
7405
+ provider: "deepseek",
7406
+ profile: "auto",
7407
+ goal: authoritativeRoutineGoal,
7408
+ allowShellTool: true,
7409
+ maxSteps: 4,
7410
+ toolCalls: [
7411
+ contractCall("unused-authoritative", "finish", { result: "unused" }),
7412
+ ],
7413
+ expectSuccess: true,
7414
+ setupWorkspace: async (workspace) => {
7415
+ await fs.mkdir(path.join(workspace, "scripts"), { recursive: true });
7416
+ await fs.writeFile(
7417
+ path.join(workspace, "scripts", "status.js"),
7418
+ [
7419
+ "console.log(JSON.stringify({ phone: 'login-required', schedule: 'current', authoritative: true }));",
7420
+ "process.exit(1);",
7421
+ "",
7422
+ ].join("\n"),
7423
+ "utf8"
7424
+ );
7425
+ },
7426
+ responseFactory: ({ payload }) => {
7427
+ authoritativeRoutineStep += 1;
7428
+ const offered = Array.isArray(payload.tools) ? names(payload.tools) : [];
7429
+ if (authoritativeRoutineStep === 1) {
7430
+ assertStrict.deepEqual(
7431
+ offered,
7432
+ ["run_command", "finish"],
7433
+ "first authoritative routine turn exposed exploratory tools"
7434
+ );
7435
+ assertStrict.deepEqual(
7436
+ enumFor(payload.tools, "run_command", "command"),
7437
+ [authoritativeRoutineCommand],
7438
+ "first authoritative routine turn did not constrain the exact command"
7439
+ );
7440
+ return assistantWithToolCalls([
7441
+ contractCall("run-authoritative-status", "run_command", {
7442
+ command: authoritativeRoutineCommand,
7443
+ }),
7444
+ ]);
7445
+ }
7446
+ assertStrict.deepEqual(
7447
+ offered,
7448
+ ["finish"],
7449
+ "authoritative routine observation did not shrink the next turn to finish-only"
7450
+ );
7451
+ return assistantWithToolCalls([
7452
+ contractCall("finish-authoritative-status", "finish", {
7453
+ result: "The authoritative status snapshot says phone login is required and the schedule snapshot is current.",
7454
+ }),
7455
+ ]);
7456
+ },
7457
+ });
7458
+ assertStrict.equal(
7459
+ authoritativeRoutineRun.requests.length,
7460
+ 2,
7461
+ "authoritative routine flow used more than two model requests"
7462
+ );
7463
+ assertStrict.equal(
7464
+ authoritativeRoutineRun.events.filter(
7465
+ (event) =>
7466
+ event.type === "tool.started" &&
7467
+ event.data?.toolName !== "finish"
7468
+ ).length,
7469
+ 1,
7470
+ "authoritative routine flow dispatched more than the canonical command"
7471
+ );
7472
+ assert(
7473
+ authoritativeRoutineRun.events.some(
7474
+ (event) =>
7475
+ event.type === "tool.completed" &&
7476
+ event.data?.toolName === "run_command" &&
7477
+ event.data?.ok === false &&
7478
+ event.data?.authoritativeReadOnlyRoutine === true &&
7479
+ event.data?.authoritativeRoutineObserved === true
7480
+ ),
7481
+ "nonzero authoritative routine snapshot was not retained as observed evidence"
7482
+ );
7483
+ assert(
7484
+ !authoritativeRoutineRun.events.some(
7485
+ (event) =>
7486
+ event.type === "tool.started" &&
7487
+ /(?:^|[ /])(?:\\.private|private|raw|sqlite|jsonl)(?:$|[ /.-])/i.test(
7488
+ String(event.data?.args?.command || "")
7489
+ )
7490
+ ),
7491
+ "authoritative routine flow still dispatched private/raw exploratory commands"
7492
+ );
7493
+
7291
7494
  const hiddenDryRun = await runToolContractCase({
7292
7495
  id: "native-hidden-dry-run",
7293
7496
  goal: "Create hidden-dry-run.txt containing unsafe if this tool dispatches.",
@@ -7647,6 +7850,7 @@ console.log(
7647
7850
  "finish-contract",
7648
7851
  "per-turn-contract-preserved",
7649
7852
  "schema-required-type-enum-extra",
7853
+ "authoritative-readonly-routine-first",
7650
7854
  "native-hidden-dry-run-zero-dispatch",
7651
7855
  "text-fallback-unoffered-zero-dispatch",
7652
7856
  "strict-single-call-batch",
@@ -637,11 +637,23 @@ assert.equal(
637
637
  "an incidental safety-clause 'cannot' was misclassified as a current external blocker"
638
638
  );
639
639
  assert.equal(finishResultClaimsIncompleteWork("Completed and verified the requested report."), false);
640
+ assert.equal(
641
+ finishResultClaimsIncompleteWork(
642
+ "Read-only status: Still retrying: external_pdf (quality_retry_pending; next attempt at 10:14). Nothing was sent or changed."
643
+ ),
644
+ false,
645
+ "an external retry status was misclassified as unfinished agent work"
646
+ );
640
647
  assert.equal(
641
648
  finishResultClaimsIncompleteWork("The task is paused. A corrected implementation will be written next."),
642
649
  true,
643
650
  "future work was accepted as a completed result"
644
651
  );
652
+ assert.equal(
653
+ finishResultClaimsIncompleteWork("The requested report is pending and I will complete it next."),
654
+ true,
655
+ "agent-owned pending report work was accepted as complete"
656
+ );
645
657
 
646
658
  const outputFilenameContract = deriveScsTaskContract({
647
659
  goal: "Save the complete cited report as `CLAIM_LEVEL_CITATION_RESEARCH.md` and include `negative evidence` in the report.",
@@ -132,6 +132,12 @@ for (const unrelated of [
132
132
  }
133
133
  assert(selectedIds("debug Docker deployment logs and port config").includes("devops-deployment"), "devops prompt did not select devops-deployment");
134
134
  assert(selectedIds("review auth security and secrets handling").includes("security-review"), "security prompt did not select security-review");
135
+ const securitySkill = skills.find((skill) => skill.id === "security-review");
136
+ assert(
137
+ securitySkill?.body.includes("carriage-return, newline") &&
138
+ securitySkill?.body.includes("residual risks and non-goals"),
139
+ "security skill omitted audit-record injection or structured security-note guidance"
140
+ );
135
141
  assert(selectedIds("make a PowerPoint pitch deck").includes("presentation-slides"), "slides prompt did not select presentation-slides");
136
142
  const presentationSkill = skills.find((skill) => skill.id === "presentation-slides");
137
143
  assert(presentationSkill?.body.includes("Render every slide"), "presentation skill does not require every slide to be rendered");