@nanobpm/nano-workforce 0.200.4 → 0.200.5

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.
@@ -10,9 +10,12 @@ import {
10
10
  DELIVERY_NODE_KINDS,
11
11
  type DeliveryGraphError,
12
12
  type DeliveryGraphErrorCode,
13
+ drainClauseScanSteps,
13
14
  redactConnectorValue,
14
15
  validateDeliveryGraph,
15
16
  } from "./deliveryGraph.ts";
17
+ import { readFileSync } from "node:fs";
18
+ import { join } from "node:path";
16
19
 
17
20
  /** The single error in the result, asserting there is exactly one. */
18
21
  function only(errors: DeliveryGraphError[]): DeliveryGraphError {
@@ -1350,3 +1353,1750 @@ test("#548 a converge connector with MULTIPLE incoming `pr` facts is rejected as
1350
1353
  const err = hasCode(validateDeliveryGraph(g), "unbound-pr");
1351
1354
  assert(err.message.includes("disambiguate"), err.message);
1352
1355
  });
1356
+
1357
+ // Issue #858: the deterministic compile/lint-time guard for the partial-scope-close defect class.
1358
+ // The field case (delivery graph `5e36636255ab`, node `i12`) paired a brief scoped to ONE of an
1359
+ // issue's three acceptance criteria with "…open a PR that closes it", so the agent wrote `Closes #N`
1360
+ // and the remainder was silently dropped. `validateDeliveryGraph` must now reject an `agent.prompt`
1361
+ // that closes an issue WITHOUT an explicit full-scope acknowledgement marker.
1362
+ test("#858 an agent prompt that closes an issue with NO full-scope marker is rejected (partial-scope-close)", () => {
1363
+ const g = {
1364
+ name: "partial close",
1365
+ nodes: [
1366
+ {
1367
+ id: "i12",
1368
+ kind: "agent",
1369
+ agent: {
1370
+ jobType: "senior:feature",
1371
+ // The exact field-case shape: scoped to one criterion, told to close the parent.
1372
+ prompt: "Implement criterion 1 of nanobpm/nano-supervisor#12 and open a PR that closes #12.",
1373
+ },
1374
+ },
1375
+ ],
1376
+ edges: [],
1377
+ };
1378
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1379
+ assertEquals(err.path, "nodes[0].agent.prompt");
1380
+ });
1381
+
1382
+ test("#858 the closing-keyword families (closes/fixes/resolves, owner/repo#N, issue URL, and the pronoun form `closes it`) are all detected", () => {
1383
+ const closers = [
1384
+ "open a PR that closes #12",
1385
+ "this fixes nanobpm/nano-supervisor#12",
1386
+ "Resolves https://github.com/nanobpm/nano-supervisor/issues/12",
1387
+ "resolve #12 when done",
1388
+ // The LITERAL field case (delivery graph `5e36636255ab`, node `i12`): the issue is referenced by
1389
+ // name (`nano-supervisor#12`) and the closing verb uses the pronoun `it`, NOT a bare `#N`.
1390
+ "Implement nanobpm/nano-supervisor#12 and open a PR that closes it",
1391
+ "Implement #12 and close the issue",
1392
+ ];
1393
+ for (const prompt of closers) {
1394
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1395
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1396
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1397
+ }
1398
+ });
1399
+
1400
+ test("#858 round-16: the duplicate closing-language prefilter is gone (one canonical closing grammar)", () => {
1401
+ // Finding (review 5407092970): a second `CLOSING_ACTION_PATTERN` prefilter duplicated the whole
1402
+ // active+passive closing grammar of `CLOSING_TARGET_PATTERN`, violating the one-canonical rule — any
1403
+ // syntax added only to the authoritative pattern was unreachable through the prefilter and failed
1404
+ // OPEN. It was removed so `isPartialScopeClose` derives "is there an active close?" from the single
1405
+ // `closingTargets` grammar. Guard against the drift surface being reintroduced.
1406
+ const src = readFileSync(join(import.meta.dirname, "deliveryGraph.ts"), "utf8");
1407
+ assert(
1408
+ !/const\s+CLOSING_ACTION_PATTERN\b/.test(src) && !/CLOSING_ACTION_PATTERN\s*\.\s*test\b/.test(src),
1409
+ "deliveryGraph.ts must not reintroduce a duplicate CLOSING_ACTION_PATTERN prefilter — the single " +
1410
+ "authoritative closing grammar is CLOSING_TARGET_PATTERN (via closingTargets)",
1411
+ );
1412
+ // And the behaviour it used to gate must still hold through the one grammar: a passive, issue-first
1413
+ // close with no full-scope marker (a form only CLOSING_TARGET_PATTERN parses) is still rejected.
1414
+ const g = {
1415
+ nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt: "Implement criterion 1 of #12; ensure #12 is closed by the PR." } }],
1416
+ edges: [],
1417
+ };
1418
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1419
+ assertEquals(err.path, "nodes[0].agent.prompt");
1420
+ });
1421
+
1422
+ test("#858 a closing keyword WITH an explicit full-scope marker validates (the legitimate final closer)", () => {
1423
+ const ok = [
1424
+ "Implement nanobpm/nano-supervisor#12 — this brief delivers #12's full stated scope (every acceptance criterion) — and open a PR that closes #12.",
1425
+ "Own the whole issue: deliver every acceptance criterion of #12, then close #12.",
1426
+ "This slice covers the full scope of #12; open a PR with Closes #12.",
1427
+ ];
1428
+ for (const prompt of ok) {
1429
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1430
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1431
+ }
1432
+ });
1433
+
1434
+ // The marker list must also recognise the NATURAL issue-anchored whole-scope paraphrases a planner
1435
+ // writes when it genuinely scopes a slice to the whole issue — not only the exact phrases the
1436
+ // contract happens to use verbatim. A legitimately full-scope closer phrased "the whole issue" /
1437
+ // "all of #N" / "the entire issue" must NOT be rejected (the under-inclusive-marker false positive).
1438
+ test("#858 a closing keyword WITH a natural issue-anchored whole-scope paraphrase validates", () => {
1439
+ const ok = [
1440
+ "Deliver the whole issue #12 and close it.",
1441
+ "Implement all of #12 and close it.",
1442
+ "Own the entire issue #12, then close it.",
1443
+ "This slice covers the complete issue #12; open a PR with Closes #12.",
1444
+ "Implement all of the issue #12 and resolve it.",
1445
+ "Deliver the whole of #12 and close it.",
1446
+ ];
1447
+ for (const prompt of ok) {
1448
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1449
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1450
+ }
1451
+ });
1452
+
1453
+ // The converse guard: a bare adverb of completeness (`fully` / `completely` / `end-to-end` /
1454
+ // `in full` / `in its entirety`) is NOT a full-scope marker, because it can modify a PARTIAL
1455
+ // deliverable ("implement one criterion fully"). Only an issue-anchored whole-scope phrase licenses
1456
+ // a closing keyword — a partial brief dressed in an adverb must still be rejected.
1457
+ test("#858 a bare completeness adverb on a partial brief is STILL rejected (not a full-scope marker)", () => {
1458
+ const partials = [
1459
+ "Implement one criterion of #12 fully, then close #12.",
1460
+ "Do the parser slice of #12 completely; close #12.",
1461
+ "Implement part of #12 end-to-end and close it.",
1462
+ "This slice covers the login part of #12 in full; close it.",
1463
+ "Implement the backend portion of #12 in its entirety; close it.",
1464
+ ];
1465
+ for (const prompt of partials) {
1466
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1467
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1468
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1469
+ }
1470
+ });
1471
+
1472
+ test("#858 a non-closing reference (Part of / Refs) is NOT flagged, and a prompt with no issue ref is NOT flagged", () => {
1473
+ const ok = [
1474
+ "Implement criterion 1 of #12 and reference it as Part of #12.", // non-closing ref
1475
+ "Refs #12 — work on the first slice.",
1476
+ "un-draft + merge #B", // the WELL_FORMED prompt: `#B` is not a numeric issue ref
1477
+ "close the door behind you", // prose "close" with no issue ref
1478
+ "fix the bug in the parser", // closing verb but no issue ref
1479
+ "resolve conflicts in #12 merge", // closing verb not applied to the issue (intervening word)
1480
+ "closes the loop on #12 feedback", // closing verb not applied to the issue
1481
+ ];
1482
+ for (const prompt of ok) {
1483
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1484
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1485
+ }
1486
+ });
1487
+
1488
+ test("#858 a non-agent node (human) carrying a closing keyword is NOT flagged — the guard is agent-scoped", () => {
1489
+ const g = {
1490
+ nodes: [{ id: "h", kind: "human", human: { prompt: "verify then close #12" } }],
1491
+ edges: [],
1492
+ };
1493
+ assertEquals(validateDeliveryGraph(g), []);
1494
+ });
1495
+
1496
+ // Issue #858 (round-3 review): the full-scope acknowledgement must be TIED to the issue the brief
1497
+ // closes, not accepted as a global substring anywhere in the prompt. Two bypass classes slip a
1498
+ // partial-scope-close past a global-substring check and MUST be rejected:
1499
+ // (1) cross-issue — the marker acknowledges a DIFFERENT issue's scope than the one being closed
1500
+ // (e.g. acknowledge #11's full scope, but close #12);
1501
+ // (2) attributed-to-others — the whole-scope phrase assigns the scope to SIBLINGS/others, not to
1502
+ // this brief (e.g. "the full scope of #12 is handled by siblings; … close #12").
1503
+ test("#858 a full-scope marker for a DIFFERENT issue than the one closed is rejected (cross-issue bypass)", () => {
1504
+ const bypasses = [
1505
+ // Marker anchored to #11, but the brief closes #12.
1506
+ "Acknowledge #11's full stated scope; implement criterion 1 of #12 and close #12.",
1507
+ "This covers the whole issue #11. Implement part of #12 and open a PR with Closes #12.",
1508
+ "Deliver every acceptance criterion of #11, then fix #12.",
1509
+ // SAME clause mixes a whole-scope marker for #11 with a partial reference to the closed #12 —
1510
+ // proximity anchoring must credit #11 (adjacent to the marker), not #12.
1511
+ "Covers the whole issue #11 and the parser part of #12. Closes #12.",
1512
+ ];
1513
+ for (const prompt of bypasses) {
1514
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1515
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1516
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1517
+ }
1518
+ });
1519
+
1520
+ test("#858 a whole-scope phrase ATTRIBUTED to siblings/others does not licence a close (attribution bypass)", () => {
1521
+ const bypasses = [
1522
+ "The full scope of #12 is handled by siblings; implement criterion 1 and close #12.",
1523
+ "Every acceptance criterion of #12 is delivered by the other slices; do the parser part and close #12.",
1524
+ "The whole issue #12 is covered by sibling slices; implement criterion 2 and open a PR with Closes #12.",
1525
+ // Generic (un-numbered) marker attributed to others, single issue — still must not licence it.
1526
+ "The full scope is owned by another slice; implement part of #12 and close it.",
1527
+ ];
1528
+ for (const prompt of bypasses) {
1529
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1530
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1531
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1532
+ }
1533
+ });
1534
+
1535
+ // Issue #858 (round-3 adversarial review): a whole-scope marker immediately followed by a
1536
+ // PART-QUALIFIER scopes the acknowledgement DOWN to a part, so it must NOT licence a close. The
1537
+ // marker-credit loop matched a bare substring, so "the whole issue's parser slice" / "all of #12's
1538
+ // backend" / "every acceptance criterion's auth half" all credited the marker even though each
1539
+ // describes a PARTIAL deliverable — the exact defect class the guard exists to catch. The occurrence
1540
+ // is disqualified when the text right after the marker is `'s <part>` / `of <part>` (a possessive or
1541
+ // partitive that narrows the whole to one slice).
1542
+ test("#858 a whole-scope marker immediately qualified DOWN to a part is rejected (possessive/partitive bypass)", () => {
1543
+ const bypasses = [
1544
+ "Deliver the whole issue's parser slice. Closes #12.",
1545
+ "Implement all of #12's backend. Closes #12.",
1546
+ "Covers every acceptance criterion's auth half. Closes #12.",
1547
+ "Implement the complete issue's backend only. Closes #12.",
1548
+ "Deliver the whole issue's first half. Closes #12.",
1549
+ // Sibling-marker overlap: a prefix marker ("own the whole") ends before the connective "issue",
1550
+ // so the qualifier is not adjacent to THAT marker — the walk must see past the connective.
1551
+ "Own the whole issue #12's parser. Close it.",
1552
+ "Owns the whole issue's backend. Close #12.",
1553
+ ];
1554
+ for (const prompt of bypasses) {
1555
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1556
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1557
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1558
+ }
1559
+ });
1560
+
1561
+ // The converse: the part-qualifier disqualifier must not over-fire on a legitimate whole-scope
1562
+ // acknowledgement that merely has a part-word LATER in the clause (not immediately after the marker),
1563
+ // or where the marker is at a clause boundary. A genuine full-scope closer must still validate.
1564
+ test("#858 a whole-scope marker NOT immediately qualified down still validates", () => {
1565
+ const ok = [
1566
+ // Part-word appears later in the clause, NOT immediately after the marker.
1567
+ "Deliver the whole issue #12, including the parser slice, and close it.",
1568
+ // Marker at a clause boundary (next char is a delimiter / end).
1569
+ "Own the whole issue #12. Then close it.",
1570
+ "Implement all of #12 and close it.",
1571
+ ];
1572
+ for (const prompt of ok) {
1573
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1574
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1575
+ }
1576
+ });
1577
+
1578
+ // The converse: a marker genuinely tied to the closed issue (anchored to it, or the sole issue the
1579
+ // prompt references) and asserting THIS brief owns it must still validate — the target-association
1580
+ // tightening must not regress the legitimate single-issue and issue-anchored closers.
1581
+ test("#858 a full-scope marker tied to the closed issue still validates after the target-association tightening", () => {
1582
+ const ok = [
1583
+ // Anchored to the SAME issue that is closed.
1584
+ "Acknowledge #11 is out of scope; this brief delivers all of #12 and opens a PR with Closes #12.",
1585
+ // Generic marker, but the prompt references exactly one issue (#12) — unambiguous.
1586
+ "This slice covers the full stated scope; implement every acceptance criterion of #12 and close #12.",
1587
+ "Own the whole issue #12 — deliver every checkbox — then close #12.",
1588
+ ];
1589
+ for (const prompt of ok) {
1590
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1591
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1592
+ }
1593
+ });
1594
+
1595
+ // Issue #858 (round-4 review): issue identity must preserve the REPOSITORY, not collapse to the bare
1596
+ // number. The accepted syntax includes `owner/repo#N` and issue URLs, and the validator supports
1597
+ // cross-repository graphs, so an acknowledgement anchored to `owner/alpha#12` must NOT licence closing
1598
+ // a DIFFERENT issue `owner/beta#12` — both used to become `12`. Each explicit-repo closing target
1599
+ // requires its own repo-matched acknowledgement; a bare `#N` (the implicit node/run repo) is a third,
1600
+ // distinct identity.
1601
+ test("#858 a full-scope marker for one repo's issue does not licence closing a different repo's same-numbered issue (cross-repo collision)", () => {
1602
+ const bypasses = [
1603
+ // Acknowledge owner/alpha#12's full scope, but ALSO close owner/beta#12 (partial) — the collapse-to-12
1604
+ // bug credited beta from alpha's acknowledgement.
1605
+ "Deliver owner/alpha#12's full stated scope and close owner/alpha#12. Implement only criterion 1 of owner/beta#12 and close owner/beta#12.",
1606
+ // Marker anchored (after) to owner/alpha, close targets owner/beta.
1607
+ "Deliver the full scope of owner/alpha#12 and close owner/beta#12.",
1608
+ // A bare #12 acknowledgement must not licence closing an explicitly-qualified owner/alpha#12.
1609
+ "Deliver #12's full stated scope; implement part of owner/alpha#12 and close owner/alpha#12.",
1610
+ ];
1611
+ for (const prompt of bypasses) {
1612
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1613
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1614
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1615
+ }
1616
+ });
1617
+
1618
+ // The converse: a repo-qualified marker genuinely tied to the SAME repo-qualified issue it closes must
1619
+ // still validate — the repository-identity tightening must not regress a legitimate `owner/repo#N`
1620
+ // closer, nor a bare `#N` closer (unchanged).
1621
+ test("#858 a repo-qualified full-scope marker tied to the SAME repo-qualified issue it closes still validates", () => {
1622
+ const ok = [
1623
+ "Deliver owner/alpha#12's full stated scope and close owner/alpha#12.",
1624
+ "This slice delivers nanobpm/nano-supervisor#99's full stated scope; Closes nanobpm/nano-supervisor#99.",
1625
+ "Own the whole issue of owner/alpha#7 — every acceptance criterion — then close owner/alpha#7.",
1626
+ ];
1627
+ for (const prompt of ok) {
1628
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1629
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1630
+ }
1631
+ });
1632
+
1633
+ // Issue #858 (round-4 review): a whole-scope phrase whose CLAUSE explicitly NEGATES or disclaims it
1634
+ // ("this slice does NOT deliver the full scope of #12", "we won't cover every acceptance criterion")
1635
+ // asserts the opposite of ownership, yet the bare `full scope` substring is still present. Such an
1636
+ // occurrence must NOT licence a close — the negated clause reopens the exact partial-close bypass.
1637
+ test("#858 a NEGATED/disclaimed full-scope acknowledgement does not licence a close (negation bypass)", () => {
1638
+ const bypasses = [
1639
+ "This slice does not deliver the full scope of #12; implement criterion 1 and close #12.",
1640
+ "We won't cover every acceptance criterion of #12 — just the parser. Close #12.",
1641
+ "This does not own the whole issue #12; close it.",
1642
+ "Implement part of #12; this is not the full scope of #12. Closes #12.",
1643
+ "Deliver everything other than the full scope of #12, then close #12.",
1644
+ // The "but"-as-exception idiom — a direct sibling of "other than"/"apart from": "all but X" /
1645
+ // "everything but X" mean "everything EXCEPT X", so the marker is disclaimed even though no
1646
+ // explicit negation token is present (adversarial review, issue #858 round 4).
1647
+ "Deliver all but the full scope of #12; close #12.",
1648
+ "This slice delivers everything but the full scope of #12. Closes #12.",
1649
+ "Implement all but one acceptance criterion of #12 — the full scope of #12 is NOT delivered here. Close #12.",
1650
+ ];
1651
+ for (const prompt of bypasses) {
1652
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1653
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1654
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1655
+ }
1656
+ });
1657
+
1658
+ // Issue #858 (round-14 review, thread deliveryGraph.ts:1392): the `SCOPE_NEGATED_PREFIX` memo was
1659
+ // keyed by `clauseStart` even though the tested text is the marker's BEFORE-MARKER prefix
1660
+ // (`slice(clauseStart, i)`), which grows with each marker's offset `i`. For TWO markers in one
1661
+ // (delimiter-free) clause, the FIRST (shortest-prefix) marker decided the SECOND: an affirmative lead
1662
+ // marker cached `false`, then a trailing marker whose OWN prefix contains an exception negator
1663
+ // (`excluding …`) was wrongly credited — reopening the exact partial-close bypass. The prefix test is
1664
+ // monotonic in `i`, so a negator sitting between the two markers must disqualify ONLY the later one.
1665
+ test("#858 a later marker in the same clause disclaimed by a PREFIX negator after an earlier affirmative marker is rejected (per-marker prefix negation, not one cached boolean)", () => {
1666
+ const bypasses = [
1667
+ // The review's own example: #11 affirmative, then `excluding the parser` disclaims #12 before close.
1668
+ "Deliver the full scope of #11 and excluding the parser implement the full scope of #12 and close #12.",
1669
+ // Other exception prefixes between the two markers, same clause, no delimiter.
1670
+ "Deliver the full scope of #11 and other than the parser the full scope of #12 is here so close #12.",
1671
+ "Own the whole issue #11 and without the migration path the full scope of #12 then close #12.",
1672
+ "Cover the whole issue #11 and aside from the docs deliver every acceptance criterion of #12 and close #12.",
1673
+ ];
1674
+ for (const prompt of bypasses) {
1675
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1676
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1677
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1678
+ }
1679
+ });
1680
+
1681
+ // The converse of the per-marker prefix fix: an affirmative lead marker must NOT be dragged down by a
1682
+ // negator that sits AFTER it but still BEFORE a later marker — the lead marker's own (shorter) prefix is
1683
+ // clean, so a brief that fully acknowledges the issue it closes must still validate even when a LATER,
1684
+ // unrelated marker in the same clause is prefixed by an exception word.
1685
+ test("#858 an earlier affirmative marker is NOT disqualified by a prefix negator that only precedes a LATER marker (per-marker prefix must not back-propagate)", () => {
1686
+ const ok = [
1687
+ // Closes #11 (fully acknowledged, clean prefix); #12 is merely referenced with `excluding` and never closed.
1688
+ "Deliver the full scope of #11 and excluding the parser the #12 follow-up is deferred so close #11.",
1689
+ "Own the whole issue #11 and aside from the stretch goals of #12 ship it then close #11.",
1690
+ ];
1691
+ for (const prompt of ok) {
1692
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1693
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1694
+ }
1695
+ });
1696
+
1697
+ // The converse: the negation disqualifier must not over-fire on an AFFIRMATIVE whole-scope closer that
1698
+ // merely contains an innocuous "no …" phrase ("with no gaps", "leaving nothing deferred") — those are
1699
+ // not negations of the delivery. A genuine full-scope closer must still validate.
1700
+ test("#858 an affirmative whole-scope closer with an innocuous 'no' phrase still validates (negation must not over-fire)", () => {
1701
+ const ok = [
1702
+ "Deliver the whole issue #12 with no gaps; close it.",
1703
+ "Own the whole issue #12, leaving nothing deferred, and close #12.",
1704
+ "This slice covers the full stated scope of #12 — no part is out of scope — then Closes #12.",
1705
+ // An affirmative "but" is NOT an exception disclaimer: "…, but split across two commits" and
1706
+ // "all but identical" do not disclaim the whole-scope marker in the same clause.
1707
+ "Deliver the full scope of #12, but split the work across two commits. Closes #12.",
1708
+ "Own the whole issue #12 — the plan is all but identical to the reference slice — then close #12.",
1709
+ ];
1710
+ for (const prompt of ok) {
1711
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1712
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1713
+ }
1714
+ });
1715
+
1716
+
1717
+ // Issue #858 (round-15 review, deliveryGraph.ts:318): the closing grammar ordered the optional
1718
+ // `issues?` noun BEFORE the quantifier, so a natural multi-target directive worded quantifier-first —
1719
+ // `close both issues #12 and #13` — matched neither the `CLOSING_ACTION_PATTERN` prefilter nor
1720
+ // `CLOSING_TARGET_PATTERN`, so a partial-scope prompt phrased that way received NO `partial-scope-close`
1721
+ // error (fail-open). The quantifier/determiner must be admitted on EITHER side of the `issues?` noun,
1722
+ // and EVERY coordinated target must still be acknowledged.
1723
+ test("#858 a quantifier-before-noun close (`close both issues #12 and #13`) is detected and each target needs its own acknowledgement", () => {
1724
+ const bypasses = [
1725
+ "Implement criterion 1 of #12 and close both issues #12 and #13.",
1726
+ "Deliver part of #12; close all issues #12 and #13.",
1727
+ "Fix each issue #12 and #13 after implementing one criterion.",
1728
+ // Only one of the two coordinated targets is acknowledged — the other still fails.
1729
+ "Deliver the full scope of #12; close both issues #12 and #13.",
1730
+ ];
1731
+ for (const prompt of bypasses) {
1732
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1733
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1734
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1735
+ }
1736
+ });
1737
+
1738
+ // The converse: a quantifier-before-noun close whose EVERY coordinated target is fully acknowledged must
1739
+ // still validate — the widened grammar must not regress a legitimate multi-issue closer.
1740
+ test("#858 a quantifier-before-noun close whose every target is acknowledged still validates", () => {
1741
+ const ok = [
1742
+ "Deliver the full scope of #12 and the full scope of #13; close both issues #12 and #13.",
1743
+ "Own the whole issue #7 and the whole issue #8 — every acceptance criterion — then close all issues #7 and #8.",
1744
+ ];
1745
+ for (const prompt of ok) {
1746
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1747
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1748
+ }
1749
+ });
1750
+
1751
+ // Issue #858 (round-15 review, deliveryGraph.ts:1202): a TRAILING scope exclusion that sits AFTER the
1752
+ // marker's issue anchor and past the clause comma (`Deliver the full scope of #12, excluding the parser;
1753
+ // close #12.`) narrows the whole scope down to "the whole MINUS a part", yet the comma ended the marker's
1754
+ // clause before the exclusion and the before-marker `SCOPE_NEGATED_PREFIX` only examined text BEFORE the
1755
+ // marker — so the explicitly-partial brief validated (fail-open). A trailing `except`/`excluding`/
1756
+ // `other than`/`apart from`/`aside from`/`but not`/`minus` phrase after the anchor must disqualify the
1757
+ // marker just like a before-marker exception prefix does.
1758
+ test("#858 a TRAILING exclusion after the issue anchor narrows a full-scope marker and is rejected (post-marker exclusion bypass)", () => {
1759
+ const bypasses = [
1760
+ "Deliver the full scope of #12, excluding the parser; close #12.",
1761
+ "Deliver the full scope of #12, except the migration path; close #12.",
1762
+ "Cover every acceptance criterion of #12, other than the docs. Close #12.",
1763
+ "Own the whole issue #12, apart from the parser, and close it.",
1764
+ "Deliver the full scope of #12, aside from the UI; close #12.",
1765
+ "Deliver the full scope of #12, but not the parser; close #12.",
1766
+ "Deliver the full scope of #12, omitting the parser; close #12.",
1767
+ "Deliver the full scope of #12, excluded the parser; close #12.",
1768
+ // Exclusion directly adjacent (no comma) still disqualifies.
1769
+ "Deliver the full scope of #12 excluding the parser; close #12.",
1770
+ // Sole-issue fallback: marker has no adjacent anchor but the prompt references exactly one issue.
1771
+ "Deliver the full scope, excluding the parser; close #12.",
1772
+ "Deliver the full scope of owner/alpha#12, excluding the parser; close owner/alpha#12.",
1773
+ ];
1774
+ for (const prompt of bypasses) {
1775
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1776
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1777
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1778
+ }
1779
+ });
1780
+
1781
+ // The converse: the trailing-exclusion disqualifier must not over-fire. An EXCLUSION OF NOTHING
1782
+ // (`excluding nothing`/`except none`) still delivers the whole scope; a bare affirmative `but`, an
1783
+ // `including` continuation, a trailing `without <noun>` constraint, and an `and`-coordinated unrelated
1784
+ // action are all affirmative closers that must still validate.
1785
+ test("#858 an exclusion-of-nothing or an affirmative trailing phrase does not disqualify a full-scope closer", () => {
1786
+ const ok = [
1787
+ "Deliver the full scope of #12, excluding nothing; close #12.",
1788
+ "Cover every acceptance criterion of #12, excluding none of them; close #12.",
1789
+ "Deliver the full scope of #12, including the parser slice, and close it.",
1790
+ "Deliver the full scope of #12, but split the work across two commits. Closes #12.",
1791
+ "Own the whole issue #12, leaving nothing deferred, and close #12.",
1792
+ "Deliver the full scope of #12 without regressions; close #12.",
1793
+ "Deliver the full scope of #12, and separately update the changelog; close #12.",
1794
+ ];
1795
+ for (const prompt of ok) {
1796
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1797
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1798
+ }
1799
+ });
1800
+
1801
+
1802
+ // Issue #858 (round-5 review, thread deliveryGraph.ts:453): a PART-QUALIFIER can narrow a whole-scope
1803
+ // marker from EITHER side. The after-side was already caught (`the whole issue's parser`), but a PREFIX
1804
+ // partitive ("half of every acceptance criterion of #12") slipped through — `every acceptance criterion`
1805
+ // was credited to #12 while the preceding `half of` was never examined. Qualification must be detected
1806
+ // on BOTH sides of the marker, so a prefix partitive disqualifies the occurrence just like a suffix one.
1807
+ test("#858 a whole-scope marker narrowed by a PREFIX partitive is rejected (both-sides qualifier bypass)", () => {
1808
+ const bypasses = [
1809
+ "Implement half of every acceptance criterion of #12, then close #12.",
1810
+ "Deliver part of the whole issue #12 and close it.",
1811
+ "Cover some of every acceptance criterion of #12; Closes #12.",
1812
+ "Deliver a subset of every acceptance criterion of #12. Closes #12.",
1813
+ "Do a portion of the whole issue #12 and close #12.",
1814
+ "Ship a fraction of the full scope of #12 and close #12.",
1815
+ ];
1816
+ for (const prompt of bypasses) {
1817
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1818
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1819
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1820
+ }
1821
+ });
1822
+
1823
+ // The converse: a whole-quantifier prefix ("all of" / "the whole of") is NOT a part-qualifier, and a
1824
+ // marker merely preceded by an unrelated "… of …" phrase in the same clause must still validate.
1825
+ test("#858 a whole-scope marker with a whole/benign prefix still validates (prefix-partitive must not over-fire)", () => {
1826
+ const ok = [
1827
+ "Deliver the full scope of #12 and close #12.",
1828
+ "Implement all of #12 and close it.",
1829
+ "As part of the milestone, deliver the full scope of #12 and close #12.",
1830
+ "On behalf of the team, deliver every acceptance criterion of #12; Closes #12.",
1831
+ ];
1832
+ for (const prompt of ok) {
1833
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1834
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1835
+ }
1836
+ });
1837
+
1838
+ // Issue #858 (round-5 review, thread deliveryGraph.ts:369): an EXCEPTION/REDIRECTION preposition
1839
+ // (`without`, `rather than`, `other than`, `instead of`, …) negates the full-scope phrase only when it
1840
+ // GOVERNS it — i.e. it sits BEFORE the marker. A TRAILING occurrence governs some OTHER phrase, so an
1841
+ // affirmative closer with an unrelated trailing constraint ("the full scope of #12 WITHOUT regressions",
1842
+ // "… RATHER THAN a piecemeal split") must NOT be read as scope negation and must still validate.
1843
+ test("#858 an affirmative whole-scope closer with a TRAILING exception constraint still validates (negation must not over-fire)", () => {
1844
+ const ok = [
1845
+ "Deliver the full scope of #12 without regressions; close #12.",
1846
+ "Deliver the full scope of #12 rather than a piecemeal split. Closes #12.",
1847
+ "Own the whole issue #12 instead of a thin vertical slice; close #12.",
1848
+ "Deliver every acceptance criterion of #12 with no feature left aside from the stretch goals noted elsewhere; close #12.",
1849
+ ];
1850
+ for (const prompt of ok) {
1851
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1852
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1853
+ }
1854
+ });
1855
+
1856
+ // The converse of the above: a LEADING exception preposition that genuinely governs (negates) the
1857
+ // marker must STILL disqualify — the trailing-is-benign fix must not reopen the leading-negation bypass.
1858
+ test("#858 a LEADING exception preposition that governs the marker still disqualifies the close", () => {
1859
+ const bypasses = [
1860
+ "Deliver this slice without covering the full scope of #12; close #12.",
1861
+ "Implement #12's parser rather than the full scope of #12. Closes #12.",
1862
+ "Do the auth part instead of the whole issue #12 and close #12.",
1863
+ "Ship everything other than the full scope of #12, then close #12.",
1864
+ ];
1865
+ for (const prompt of bypasses) {
1866
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1867
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1868
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1869
+ }
1870
+ });
1871
+
1872
+ // Issue #858 (round-12 adversarial review, app/deliveryGraph.ts clauseBeforeMarker): the round-12
1873
+ // complexity fix capped the before-marker text `SCOPE_NEGATED_PREFIX` is tested against to the last
1874
+ // `PREFIX_WINDOW` (96) chars. That is sound ONLY for a `$`-anchored SUFFIX pattern
1875
+ // (`PART_QUALIFIER_BEFORE_MARKER`), but `SCOPE_NEGATED_PREFIX` is an UNANCHORED "contains" test — so a
1876
+ // scope-narrowing negator sitting MORE than 96 chars before the marker (inside one long delimiter-free
1877
+ // clause) fell outside the window and was silently dropped, failing the guard OPEN. The negator must be
1878
+ // tested against the WHOLE before-clause (it is a per-clause "contains", so it is memoised per clause
1879
+ // start to stay linear); only the genuinely `$`-anchored part-qualifier may use the bounded window.
1880
+ test("#858 a leading exception preposition >96 chars before the marker still disqualifies the close (prefix-window fail-open regression)", () => {
1881
+ // One long DELIMITER-FREE clause (no `.`/`;`/`:`) so the negator and the marker share a clause, with
1882
+ // the negator pushed >96 chars before the marker by filler. At roundEntryHead (whole before-clause)
1883
+ // this was flagged; the round-12 window let it through.
1884
+ const filler = "word ".repeat(20); // pushes the negator ~130 chars before the marker
1885
+ const bypasses = [
1886
+ `excluding the parser only ${filler}the full scope of #12 so close #12`,
1887
+ `apart from the auth slice ${filler}deliver the whole issue #12 and close #12`,
1888
+ `other than the parser ${filler}own the entire issue #12 then close #12`,
1889
+ ];
1890
+ for (const prompt of bypasses) {
1891
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1892
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1893
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1894
+ }
1895
+ // …but a long clause with NO negator and an affirmative full-scope acknowledgement must still VALIDATE
1896
+ // (testing the whole before-clause must not over-fire and break a legitimate closer).
1897
+ const genuine = `deliver ${filler}the full scope of #12 then close #12`;
1898
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt: genuine } }], edges: [] };
1899
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${genuine}`);
1900
+ });
1901
+
1902
+ // Issue #858 (round-5 review, "Allow current-slice ownership in passive attribution",
1903
+ // deliveryGraph.ts:354): a whole-scope phrase attributed to THIS slice ("delivered by this slice",
1904
+ // "owned by the current slice") is an AFFIRMATIVE ownership assertion and must licence a close. Only
1905
+ // attribution to SIBLINGS/OTHERS disqualifies. The old `<verb> by` alternative fired regardless of who
1906
+ // followed `by`, wrongly rejecting first-person/current-slice passive phrasing.
1907
+ test("#858 a whole-scope phrase attributed to THIS slice (passive self-ownership) still validates", () => {
1908
+ const ok = [
1909
+ "The full scope of #12 is delivered by this slice; close #12.",
1910
+ "Every acceptance criterion of #12 is covered by the current slice; Closes #12.",
1911
+ "The whole issue #12 is owned by me here; close it.",
1912
+ "The full stated scope of #12 is handled by this brief, so close #12.",
1913
+ ];
1914
+ for (const prompt of ok) {
1915
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1916
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1917
+ }
1918
+ });
1919
+
1920
+ // The converse: attribution to SIBLINGS/OTHERS (or a non-self team) must STILL disqualify — the
1921
+ // self-ownership fix must not reopen the "handled by others" bypass.
1922
+ test("#858 a whole-scope phrase attributed to OTHERS (incl. a non-self team) still disqualifies the close", () => {
1923
+ const bypasses = [
1924
+ "The full scope of #12 is handled by siblings; implement criterion 1 and close #12.",
1925
+ "Every acceptance criterion of #12 is delivered by the other slices; do the parser part and close #12.",
1926
+ "The full scope of #12 is delivered by the backend team; implement the UI and close #12.",
1927
+ "The whole issue #12 is owned by another slice; implement part and close it.",
1928
+ ];
1929
+ for (const prompt of bypasses) {
1930
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1931
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1932
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1933
+ }
1934
+ });
1935
+
1936
+ // Issue #858 (round-5 review, "Support issue URLs in scope-anchor detection", deliveryGraph.ts:490):
1937
+ // `issueRefsIn`/`closingTargets` parse issue URLs, but the whole-scope ANCHOR only recognised `#N` /
1938
+ // `owner/repo#N`. So when a second issue reference made the sole-issue fallback unavailable, a
1939
+ // URL-anchored full-scope acknowledgement was never credited and a legitimate closer was rejected.
1940
+ test("#858 a full-scope marker anchored to an issue URL licences closing that URL's issue (multi-ref, no sole fallback)", () => {
1941
+ const ok = [
1942
+ "Deliver the full scope of https://github.com/acme/a/issues/12; note acme/b#7 is out of scope; close https://github.com/acme/a/issues/12.",
1943
+ "This brief covers the full scope of https://github.com/acme/a/issues/12 (acme/b#7 is handled elsewhere); Closes https://github.com/acme/a/issues/12.",
1944
+ // URL anchored AFTER the marker, with a second issue reference defeating the sole-issue fallback.
1945
+ "acme/b#7 is out of scope. The full scope of https://github.com/acme/a/issues/12 is delivered here; close https://github.com/acme/a/issues/12.",
1946
+ // URL anchored BEFORE the marker (possessive-style proximity): https://…/issues/12's full scope.
1947
+ "Deliver https://github.com/acme/a/issues/12's full scope; acme/b#7 is out of scope; close https://github.com/acme/a/issues/12.",
1948
+ ];
1949
+ for (const prompt of ok) {
1950
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1951
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
1952
+ }
1953
+ });
1954
+
1955
+ // The converse: a URL-anchored acknowledgement for ONE issue must NOT licence closing a DIFFERENT
1956
+ // issue (URL repo identity is preserved exactly like `owner/repo#N`).
1957
+ test("#858 a full-scope marker anchored to one issue URL does not licence closing a different issue", () => {
1958
+ const bypasses = [
1959
+ "Deliver the full scope of https://github.com/acme/a/issues/12; implement part of acme/b#7 and close acme/b#7.",
1960
+ "Deliver the full scope of https://github.com/acme/a/issues/12 and close https://github.com/acme/b/issues/12.",
1961
+ ];
1962
+ for (const prompt of bypasses) {
1963
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1964
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1965
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1966
+ }
1967
+ });
1968
+
1969
+ // Issue #858 (round-5 adversarial review): the prefix-partitive guard required a literal `of` after the
1970
+ // partitive, so the most common English partitive — `half the X` (NO `of`) — bypassed it: `half of the
1971
+ // full scope` was caught but `half the full scope` was credited. The `of` is optional in English
1972
+ // ("half the …", "part the …", "half my …"), so the qualifier must narrow the marker with or without it.
1973
+ test("#858 a whole-scope marker narrowed by a prefix partitive with NO `of` is rejected (half-the bypass)", () => {
1974
+ const bypasses = [
1975
+ "Deliver half the full scope of #12; close #12.",
1976
+ "Deliver half the whole issue #12; close #12.",
1977
+ "Implement part the whole issue #12 and close it.",
1978
+ "Deliver half my full scope of #12; close #12.",
1979
+ "Cover half its full scope of #12. Closes #12.",
1980
+ ];
1981
+ for (const prompt of bypasses) {
1982
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
1983
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
1984
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
1985
+ }
1986
+ });
1987
+
1988
+ // The converse: making the partitive `of` optional must not over-fire on a plain whole-scope marker, a
1989
+ // whole-quantifier prefix, or a part-word used non-partitively earlier in the clause.
1990
+ test("#858 a whole-scope marker with no partitive prefix still validates (optional-`of` must not over-fire)", () => {
1991
+ const ok = [
1992
+ "Deliver the full scope of #12 and close #12.",
1993
+ "Implement all of #12 and close it.",
1994
+ "As part of the milestone, deliver the full scope of #12 and close #12.",
1995
+ // A part-word used NON-partitively (not governing the marker) must not disqualify.
1996
+ "Deliver the full scope of #12 from the halfway house; close #12.",
1997
+ ];
1998
+ for (const prompt of ok) {
1999
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2000
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2001
+ }
2002
+ });
2003
+
2004
+ // Issue #858 (round-5 adversarial review): the self-exclusion lookahead added `our`/`us`/`my` to the
2005
+ // self list, which opened an attribution-to-others hole — `delivered by OUR siblings` / `handled by OUR
2006
+ // peers` were credited because the `<verb> by` alternative's lookahead failed on the leading `our` and
2007
+ // the sibling/peer alternative didn't allow an intervening possessive. A possessive before a
2008
+ // sibling/peer/other noun is still attribution to OTHERS, so it must disqualify.
2009
+ test("#858 a whole-scope phrase attributed to OUR/THEIR siblings or peers does not licence a close (possessive attribution bypass)", () => {
2010
+ const bypasses = [
2011
+ "The full scope of #12 is delivered by our siblings; implement criterion 1 and close #12.",
2012
+ "The full scope of #12 is handled by our peers; implement criterion 1 and close #12.",
2013
+ "The full scope of #12 is delivered by our sibling slices; implement criterion 1 and close #12.",
2014
+ "The full scope of #12 is delivered by our other slices; implement criterion 1 and close #12.",
2015
+ "The full scope of #12 is delivered by their siblings; implement criterion 1 and close #12.",
2016
+ ];
2017
+ for (const prompt of bypasses) {
2018
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2019
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2020
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2021
+ }
2022
+ });
2023
+
2024
+ // The converse: a possessive before a SELF noun ("our team", "our slice") is still self-ownership and
2025
+ // must NOT be disqualified — the possessive allowance is scoped to the sibling/peer/other nouns only.
2026
+ test("#858 a whole-scope phrase attributed to a SELF possessive (our team/slice) still validates", () => {
2027
+ const ok = [
2028
+ "The full scope of #12 is delivered by our team; close #12.",
2029
+ "The full scope of #12 is delivered by our slice; close #12.",
2030
+ "The full scope of #12 is delivered by this slice; close #12.",
2031
+ "The full scope of #12 is delivered by us; close #12.",
2032
+ ];
2033
+ for (const prompt of ok) {
2034
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2035
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2036
+ }
2037
+ });
2038
+
2039
+ // Issue #858 (round-5 adversarial review): splitting `without` into a before-marker-only prefix check
2040
+ // was a fail-open regression for a TRAILING delivery-negating `without` — `Deliver the full scope of #12
2041
+ // WITHOUT COVERING the edge cases; close #12.` genuinely disclaims completeness yet was credited. The
2042
+ // benign carve-out is `without <noun>` ("without regressions"); a trailing `without <delivery gerund>`
2043
+ // (covering/delivering/finishing/implementing the scope) is still a disclaimer and must disqualify.
2044
+ test("#858 a TRAILING `without <delivery gerund>` still disqualifies the close (trailing-negation regression)", () => {
2045
+ const bypasses = [
2046
+ "Deliver the full scope of #12 without covering the edge cases; close #12.",
2047
+ "Deliver the full scope of #12 without delivering the auth work; close #12.",
2048
+ "Deliver the full scope of #12 without finishing the parser; close #12.",
2049
+ "Deliver the full scope of #12 without implementing criterion 3; close #12.",
2050
+ "Deliver the full scope of #12 without completing the migration; close #12.",
2051
+ "Deliver the full scope of #12 without shipping the docs; close #12.",
2052
+ "Deliver the full scope of #12 without addressing the feedback; close #12.",
2053
+ // An adverb between `without` and the gerund still negates.
2054
+ "Deliver the full scope of #12 without fully covering the edge cases; close #12.",
2055
+ ];
2056
+ for (const prompt of bypasses) {
2057
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2058
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2059
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2060
+ }
2061
+ });
2062
+
2063
+ // The converse: a trailing `without <noun>` (the benign carve-out the round-5 split was FOR) must still
2064
+ // validate — only a trailing `without <delivery gerund>` negates, not every trailing `without`.
2065
+ test("#858 a TRAILING `without <noun>` (benign constraint) still validates (gerund-only negation must not over-fire)", () => {
2066
+ const ok = [
2067
+ "Deliver the full scope of #12 without regressions; close #12.",
2068
+ "Deliver the full scope of #12 without tests; close #12.",
2069
+ "Deliver the full scope of #12 without any regressions; close #12.",
2070
+ "Deliver the full scope of #12 without breaking changes; close #12.",
2071
+ "Deliver the full scope of #12 rather than a piecemeal split. Closes #12.",
2072
+ // A benign NOUN that merely looks like a delivery gerund ("a covering letter", "the building
2073
+ // blocks", "meeting notes") is an unrelated trailing constraint, NOT a scope disclaimer — the
2074
+ // article/determiner (or the noun sense) keeps it affirmative.
2075
+ "Deliver the full scope of #12 without a covering letter; close #12.",
2076
+ "Deliver the full scope of #12 without the building blocks; close #12.",
2077
+ "Deliver the full scope of #12 without meeting notes; close #12.",
2078
+ "Deliver the full scope of #12 without a finishing touch; close #12.",
2079
+ ];
2080
+ for (const prompt of ok) {
2081
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2082
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2083
+ }
2084
+ });
2085
+
2086
+ // Issue #858 (round-6 review, thread deliveryGraph.ts:294): the closing-action/target grammar only
2087
+ // accepted a closing verb directly adjacent to `#N`, a URL, or a pronoun — it MISSED the common
2088
+ // explicit `issue #N` / `GitHub issue #N` noun phrase (`…open a PR that closes issue #12`). That still
2089
+ // tells a partial node to close #12, so it must be detected (and its target extracted as #12).
2090
+ test("#858 a partial brief that closes via an `issue #N` noun phrase is rejected (issue-#N closing form)", () => {
2091
+ const bypasses = [
2092
+ "Implement criterion 1 of #12 and open a PR that closes issue #12.",
2093
+ "Implement the parser part of #12; fixes GitHub issue #12.",
2094
+ "Do the auth slice of #12 and resolve issue #12.",
2095
+ // Plural `issues #N` noun phrase.
2096
+ "Implement one criterion of #12 and close issues #12.",
2097
+ // Repo-qualified noun-phrase target: acknowledge nothing, close owner/repo#12.
2098
+ "Implement part of owner/repo#12 and close issue owner/repo#12.",
2099
+ ];
2100
+ for (const prompt of bypasses) {
2101
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2102
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2103
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2104
+ }
2105
+ });
2106
+
2107
+ // The converse: a FULL-scope closer phrased with the `issue #N` noun phrase must still validate — the
2108
+ // new grammar must recognise the target so the marker's acknowledgement is correctly credited to it.
2109
+ test("#858 a full-scope closer using the `issue #N` noun phrase still validates (issue-#N form must not over-fire)", () => {
2110
+ const ok = [
2111
+ "Deliver the full scope of #12 and close issue #12.",
2112
+ "Own the whole issue #12, then open a PR that closes GitHub issue #12.",
2113
+ "This slice covers the full scope of owner/repo#12; resolve issue owner/repo#12.",
2114
+ ];
2115
+ for (const prompt of ok) {
2116
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2117
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2118
+ }
2119
+ });
2120
+
2121
+ // Issue #858 (round-6 review, thread deliveryGraph.ts:606): the core-negation check tested the marker's
2122
+ // whole comma-bounded clause, so a negator governing an UNRELATED constraint coordinated onto the clause
2123
+ // by `and` (`…full scope of #12 AND do not introduce regressions`) wrongly disqualified an affirmative
2124
+ // full-scope closer. The check is now scoped to the marker's `and`-coordinated delivery assertion, so
2125
+ // such a closer validates — while a negator in the marker's own segment still disqualifies.
2126
+ test("#858 an affirmative closer whose `and`-coordinated constraint is negated still validates (unrelated-negation must not over-fire)", () => {
2127
+ const ok = [
2128
+ "Deliver the full scope of #12 and do not introduce regressions; close #12.",
2129
+ "Deliver the full scope of #12 and never break the build; close #12.",
2130
+ "Own the whole issue #12 and do not add new dependencies, then close #12.",
2131
+ "Deliver every acceptance criterion of #12 and ensure it cannot be bypassed; close #12.",
2132
+ // The WITHOUT_DELIVERY disqualifier is scoped the same way: a trailing delivery gerund in an
2133
+ // `and`-coordinated sibling constraint governs that constraint, not the marker.
2134
+ "Deliver the full scope of #12 and refactor without breaking the build; close #12.",
2135
+ ];
2136
+ for (const prompt of ok) {
2137
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2138
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2139
+ }
2140
+ });
2141
+
2142
+ // The converse of the above: a negator that GOVERNS the marker (in its own `and`-segment, or with no
2143
+ // coordinator between it and the marker) must STILL disqualify — the assertion-scoping fix must not
2144
+ // reopen the negation bypass. Likewise a `but without <gerund>` trailing disclaimer stays in-segment.
2145
+ test("#858 a negator governing the marker still disqualifies after assertion-scoping (no reopened bypass)", () => {
2146
+ const bypasses = [
2147
+ "This slice does not deliver the full scope of #12 and close #12.",
2148
+ "Do the parser and never cover the full scope of #12; close #12.",
2149
+ "Deliver the full scope of #12 but without covering the auth; close #12.",
2150
+ "Implement one criterion and do not own the whole issue #12; close it.",
2151
+ ];
2152
+ for (const prompt of bypasses) {
2153
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2154
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2155
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2156
+ }
2157
+ });
2158
+
2159
+ // Issue #858 (round-6 adversarial review, app/deliveryGraph.ts:666): the ATTRIBUTION disqualifier was
2160
+ // still tested against the marker's WHOLE comma-bounded clause while its two negation siblings were
2161
+ // scoped to the marker's `and`-coordinated delivery assertion — the SAME false-positive class round-6
2162
+ // fixed for them. A legit full-scope closer carrying an UNRELATED `and`-coordinated attribution
2163
+ // (`…full scope of #12 AND the regression suite is handled by another team`) was falsely flagged. The
2164
+ // attribution check is now scoped to the delivery assertion too, so such a closer validates — while an
2165
+ // attribution in the marker's OWN segment still disqualifies.
2166
+ test("#858 an affirmative closer whose `and`-coordinated attribution is unrelated still validates (attribution scoped to the assertion)", () => {
2167
+ const ok = [
2168
+ "Deliver the full scope of #12 and the regression suite is handled by another team; close #12.",
2169
+ "Own the whole issue #12 and the docs are owned by a sibling slice, then close #12.",
2170
+ "Deliver every acceptance criterion of #12 and the migration is covered by our peers; close #12.",
2171
+ ];
2172
+ for (const prompt of ok) {
2173
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2174
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2175
+ }
2176
+ });
2177
+
2178
+ // The converse: an attribution that GOVERNS the marker (in its own `and`-segment, or with no
2179
+ // coordinator between it and the marker) must STILL disqualify — assertion-scoping the attribution
2180
+ // check must not reopen the attribution bypass.
2181
+ test("#858 an attribution governing the marker still disqualifies after assertion-scoping (no reopened bypass)", () => {
2182
+ const bypasses = [
2183
+ "The full scope of #12 is handled by siblings and close #12.",
2184
+ "The full scope of #12 is delivered by another team and close #12.",
2185
+ ];
2186
+ for (const prompt of bypasses) {
2187
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2188
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2189
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2190
+ }
2191
+ });
2192
+
2193
+ // Issue #858 (round-6 adversarial review, app/deliveryGraph.ts:671): scoping the core-negation check
2194
+ // to the marker's `and`-segment opened a fail-open regression — a GENUINE disclaimer that sits AFTER
2195
+ // the marker in an `and`-segment (`…full scope of #12 AND it is not fully delivered`) was shed from
2196
+ // the segment and no longer disqualified, where the pre-round whole-clause test caught it. An
2197
+ // after-marker negation that REFERENCES DELIVERY (a delivery verb / `it` referring back to the scope)
2198
+ // still disclaims the marker and must disqualify — while an after-marker negation of an UNRELATED
2199
+ // constraint (`…AND do not introduce regressions`) stays affirmative.
2200
+ test("#858 an after-marker negation that references delivery still disqualifies (fail-open regression closed)", () => {
2201
+ const bypasses = [
2202
+ "Deliver the full scope of #12 and it is not fully delivered; close #12.",
2203
+ "Deliver the full scope of #12 and we cannot deliver the edge cases; close #12.",
2204
+ "Own the whole issue #12 and it is never fully covered, then close #12.",
2205
+ ];
2206
+ for (const prompt of bypasses) {
2207
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2208
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2209
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2210
+ }
2211
+ });
2212
+
2213
+ // Issue #858 (round-7 review, app/deliveryGraph.ts:298/342 — "Exclude negated closing actions"): the
2214
+ // closing-action detector matched a closing verb even when it was DIRECTLY NEGATED, so a safe
2215
+ // partial-slice brief that explicitly forbids the close (`Implement criterion 1 of #12. Do not close
2216
+ // #12; use Part of #12.`) — which follows the partial-slice contract — was wrongly rejected as
2217
+ // `partial-scope-close`, blocking a legitimate graph at compile/dispatch time. A directly-negated close
2218
+ // (`do not close`, `don't close`, `never close`) is the OPPOSITE of the defect (a part-scope node told
2219
+ // TO close), so it must not count as a closing action.
2220
+ test("#858 a directly-negated close is not a partial-scope-close (safe partial-slice brief validates)", () => {
2221
+ const ok = [
2222
+ "Implement criterion 1 of nanobpm/nano-supervisor#12. Do not close #12; use Part of #12.",
2223
+ "Implement criterion 1 of #12. Don't close #12.",
2224
+ "Work on one slice of #12; never close #12 — that is the epic's job.",
2225
+ "Implement part of #12. Do not resolve #12. Do not fix #12.",
2226
+ "Scope: one criterion of #12. Do not simply close it; leave the parent open.",
2227
+ ];
2228
+ for (const prompt of ok) {
2229
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2230
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2231
+ }
2232
+ });
2233
+
2234
+ // The converse: negating ONE close must not blind the guard to a DIFFERENT active close, and a
2235
+ // double-negative idiom (`do not forget to close`) is still an active close instruction. Both must stay
2236
+ // flagged — the negation refinement must not reopen the partial-scope-close bypass.
2237
+ test("#858 a negated close must not mask a sibling active close (no reopened bypass)", () => {
2238
+ const bypasses = [
2239
+ // #34 is actively closed with no full-scope marker; the negated #12 must not suppress detection.
2240
+ "Implement criterion 1 of #12. Do not close #12, but close #34.",
2241
+ // "do not forget to close" INSTRUCTS the close — a double negative, still a partial-scope-close.
2242
+ "Implement criterion 1 of #12. Do not forget to close #12.",
2243
+ ];
2244
+ for (const prompt of bypasses) {
2245
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2246
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2247
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2248
+ }
2249
+ });
2250
+
2251
+ // Issue #858 (round-7 review, app/deliveryGraph.ts:378 — "Distinguish implementation methods from
2252
+ // alternate ownership"): the broad `<completion verb> by <non-self>` attribution branch mistook an
2253
+ // implementation METHOD (a gerund describing HOW the scope is delivered) for attribution to another
2254
+ // owner, because the method word is not on the self allowlist. `The full scope of #12 is implemented by
2255
+ // updating the parser; close #12.` was wrongly rejected. A `by <gerund>` means-clause is the slice's
2256
+ // own method, not another actor, so it must stay a valid full-scope acknowledgement.
2257
+ test("#858 a `by <implementation method>` gerund is the slice's own method, not attribution (validates)", () => {
2258
+ const ok = [
2259
+ "The full scope of #12 is implemented by updating the parser; close #12.",
2260
+ "Deliver the full scope of #12, satisfied by adding the missing migration; close #12.",
2261
+ "The whole issue #12 is delivered by carefully refactoring the module, then close #12.",
2262
+ "Own the full scope of #12, completed by wiring up the remaining handlers; close #12.",
2263
+ ];
2264
+ for (const prompt of ok) {
2265
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2266
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2267
+ }
2268
+ });
2269
+
2270
+ // The converse: attribution to another ACTOR (not a gerund method) must STILL disqualify — the
2271
+ // gerund-method carve-out must not reopen the attribution bypass. A determiner before an `-ing` word
2272
+ // (`by the training team`) makes it an actor noun phrase, not a method, so it still disqualifies.
2273
+ test("#858 attribution to another actor still disqualifies after the gerund-method carve-out (no reopened bypass)", () => {
2274
+ const bypasses = [
2275
+ "The full scope of #12 is handled by siblings; close #12.",
2276
+ "The full scope of #12 is delivered by the other slices; close #12.",
2277
+ "The full scope of #12 is implemented by the platform team; close #12.",
2278
+ "The full scope of #12 is delivered by the training team; close #12.",
2279
+ ];
2280
+ for (const prompt of bypasses) {
2281
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2282
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2283
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2284
+ }
2285
+ });
2286
+
2287
+ // Issue #858 (round-8 adversarial review, app/deliveryGraph.ts:411 — "gerund-method lookahead fails
2288
+ // open on a bare `-ing` noun actor"): the round-7 lookahead `(?!(?:\w+ly\s+)?\w+ing\b)` excluded ANY
2289
+ // bare `-ing` word after `by`, so attribution to a real actor whose name ends in `-ing` (`owned by
2290
+ // engineering`, `handled by marketing`, `covered by staffing`) no longer disqualified — a fail-OPEN
2291
+ // regression vs. round-6 (no lookahead). The carve-out must exclude only a gerund that GOVERNS an
2292
+ // object (a means-clause like `by updating the parser`), not a bare terminal `-ing` noun.
2293
+ test("#858 a bare `-ing` noun actor still disqualifies after the gerund-method carve-out (no fail-open regression)", () => {
2294
+ const bypasses = [
2295
+ "The full scope of #12 is owned by engineering; close #12.",
2296
+ "The full scope of #12 is handled by marketing; close #12.",
2297
+ "The full scope of #12 is covered by staffing; close #12.",
2298
+ "The full scope of #12 is delivered by engineering; close #12.",
2299
+ "The full scope of #12 is owned by engineering and product; close #12.",
2300
+ "The full scope of #12 is handled by marketing or sales; close #12.",
2301
+ "The full scope of #12 is provided by consulting; close #12.",
2302
+ "The full scope of #12 is owned by engineering that reports to product; close #12.",
2303
+ ];
2304
+ for (const prompt of bypasses) {
2305
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2306
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2307
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2308
+ }
2309
+ });
2310
+
2311
+ // …while a genuine means-clause — a gerund GOVERNING an object — is still the slice's own method and
2312
+ // must validate (the round-7 carve-out's intent, preserved).
2313
+ test("#858 a gerund governing an object is still a method, not attribution (validates)", () => {
2314
+ const ok = [
2315
+ "The full scope of #12 is implemented by updating the parser; close #12.",
2316
+ "The full scope of #12 is satisfied by doing it; close #12.",
2317
+ "The full scope of #12 is addressed by filing tickets; close #12.",
2318
+ "The full scope of #12 is met by quietly shipping it; close #12.",
2319
+ ];
2320
+ for (const prompt of ok) {
2321
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2322
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2323
+ }
2324
+ });
2325
+
2326
+ // Issue #858 (round-8 adversarial review, app/deliveryGraph.ts:361 — "NEGATED_CLOSE_PREFIX misses the
2327
+ // `not to close` infinitive"): the negator→verb gap admitted only adverbs, so the `to` of the
2328
+ // infinitive (`Remember not to close #12.`, `Be sure not to close #12.`) was not admitted and a brief
2329
+ // that forbids the close this way was still rejected. The gap now admits an optional `to`.
2330
+ test("#858 a `not to close` infinitive is a negated close, not a partial-scope-close (validates)", () => {
2331
+ const ok = [
2332
+ "Implement criterion 1 of #12. Remember not to close #12; use Part of #12.",
2333
+ "Implement one slice of #12. Be sure not to close #12.",
2334
+ "Implement criterion 1 of #12. Do not simply blindly close it.",
2335
+ ];
2336
+ for (const prompt of ok) {
2337
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2338
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2339
+ }
2340
+ });
2341
+
2342
+ // …and admitting `to` must not reopen the double-negative bypass: `do not forget to close` still
2343
+ // INSTRUCTS the close (a verb sits between the negator and `to`), so it stays flagged.
2344
+ test("#858 admitting `to` keeps `do not forget to close` an active close (no reopened bypass)", () => {
2345
+ const bypasses = [
2346
+ "Implement criterion 1 of #12. Do not forget to close #12.",
2347
+ "Implement criterion 1 of #12. Do not hesitate to close #12.",
2348
+ ];
2349
+ for (const prompt of bypasses) {
2350
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2351
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2352
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2353
+ }
2354
+ });
2355
+
2356
+ // Issue #858 (round-8 review, "Support optional colon after closing keywords", deliveryGraph.ts:301 and
2357
+ // the mirrored target regex / scope-classify.md): GitHub also closes on the colon form (`Closes: #12`),
2358
+ // but the prefilter and target regex required the issue target immediately after whitespace, so a
2359
+ // partial brief using the colon form produced NO `partial-scope-close` error even though GitHub would
2360
+ // close the issue. Both patterns now accept the optional colon.
2361
+ test("#858 the colon closing form (`Closes: #12`) is detected as a partial-scope-close", () => {
2362
+ const bypasses = [
2363
+ "Implement criterion 1 of #12. Closes: #12.",
2364
+ "Implement the parser slice of #12. Fixes: #12.",
2365
+ "Do the UI part of nanobpm/nano-supervisor#12. Resolves: nanobpm/nano-supervisor#12.",
2366
+ "Implement one criterion of #12. Closes:#12.",
2367
+ ];
2368
+ for (const prompt of bypasses) {
2369
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2370
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2371
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2372
+ }
2373
+ });
2374
+
2375
+ // …and the colon form carrying a genuine full-scope acknowledgement still validates (the colon change
2376
+ // must widen detection, not over-fire on a legitimate full-scope closer).
2377
+ test("#858 the colon closing form with a full-scope acknowledgement validates", () => {
2378
+ const ok = [
2379
+ "Deliver the full scope of #12. Closes: #12.",
2380
+ "This slice owns the whole issue #12. Fixes: #12.",
2381
+ ];
2382
+ for (const prompt of ok) {
2383
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2384
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2385
+ }
2386
+ });
2387
+
2388
+ // Issue #858 (round-8 review, app/deliveryGraph.ts:366 — "Distinguish additive not-just constructions
2389
+ // from negation"): the negator gap admitted `just`/`only`, so an ADDITIVE `not just/only … ; also …`
2390
+ // correlative (`Do not just close #12; also add a release note` — the close STILL happens) was treated as
2391
+ // a negated close and the partial brief validated. The additive correlative now re-activates the close.
2392
+ // (Round-8 adversarial review narrowed the trigger: the additive signal is the `also`/`as well`/`too`
2393
+ // continuation, never a bare `but`, so these cases all carry an explicit additive word.)
2394
+ test("#858 an additive `not just/only … ; also/but …` close is a partial-scope-close (not a negation)", () => {
2395
+ const bypasses = [
2396
+ "Implement criterion 1 of #12. Do not just close #12; also add a release note.",
2397
+ "Implement one slice of #12. Do not only close #12, but also update the docs.",
2398
+ "Do the parser part of #12. Don't just close #12; also open a follow-up.",
2399
+ "Implement part of #12. Do not only close #12 but also notify the team.",
2400
+ ];
2401
+ for (const prompt of bypasses) {
2402
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2403
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2404
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2405
+ }
2406
+ });
2407
+
2408
+ // …but a genuine negated close with a merely/simply/just manner-adverb and NO additive continuation
2409
+ // stays a safe partial-slice brief (the additive carve-out must not reopen the negated-close false
2410
+ // positive the round-7 fix closed).
2411
+ test("#858 a `not simply/just/merely close` with no additive continuation stays a safe negated close", () => {
2412
+ const ok = [
2413
+ "Scope: one criterion of #12. Do not simply close it; leave the parent open.",
2414
+ "Implement criterion 1 of #12. Do not just close #12.",
2415
+ "Implement one slice of #12. Do not merely close #12 — leave it open.",
2416
+ ];
2417
+ for (const prompt of ok) {
2418
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2419
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2420
+ }
2421
+ });
2422
+
2423
+ // Issue #858 (round-8 review, app/deliveryGraph.ts:427 — "Detect active-voice sibling ownership
2424
+ // attribution"): the attribution check only detected the PASSIVE `delivered by siblings`. An active-voice
2425
+ // `Siblings deliver the full scope of #12; … close #12.` credited the marker even though siblings — not
2426
+ // this node — own the scope, restoring the attribution bypass. Active-voice sibling/other ownership now
2427
+ // disqualifies the marker too.
2428
+ test("#858 active-voice sibling/other ownership disqualifies the full-scope marker (no attribution bypass)", () => {
2429
+ const bypasses = [
2430
+ "Siblings deliver the full scope of #12; implement criterion 1 and close #12.",
2431
+ "The other slices own every acceptance criterion of #12; do the parser part and close #12.",
2432
+ "Peers handle the full scope of #12; implement the UI and close #12.",
2433
+ "Another slice covers the whole issue #12; implement part and close it.",
2434
+ ];
2435
+ for (const prompt of bypasses) {
2436
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2437
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2438
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2439
+ }
2440
+ });
2441
+
2442
+ // …while active-voice ownership by THIS slice is an affirmative assertion and must still validate (the
2443
+ // active-voice check must fire only on OTHERS, never reopen a self-ownership false positive).
2444
+ test("#858 active-voice self ownership still validates (active-voice check fires only on others)", () => {
2445
+ const ok = [
2446
+ "This slice delivers the full scope of #12; close #12.",
2447
+ "This brief covers every acceptance criterion of #12, so close #12.",
2448
+ "I own the whole issue #12; close it.",
2449
+ ];
2450
+ for (const prompt of ok) {
2451
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2452
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2453
+ }
2454
+ });
2455
+
2456
+ // Issue #858 (round-8 adversarial review): the round-8 additive trigger admitted a BARE `but` as an
2457
+ // additive continuation. A genuine CONTRASTIVE `but` after a negated close (`Do not just close it; but
2458
+ // leave the parent open`) is a SAFE negated close — the `but` introduces a contrast, not an added action
2459
+ // — yet it was re-activated and flagged, a FALSE-POSITIVE REGRESSION vs. round-7. The additive signal is
2460
+ // the correlative `also`, never a bare `but`, so a bare `but` must NOT reinstate the close.
2461
+ test("#858 a contrastive `but` after a negated close is NOT an additive continuation (no false positive)", () => {
2462
+ const ok = [
2463
+ "Scope: one criterion of #12. Do not just close it; but leave the parent open.",
2464
+ "Do not only close #12, but never reopen it.",
2465
+ "Implement one slice of #12. Do not just close #12; but do add a note.",
2466
+ ];
2467
+ for (const prompt of ok) {
2468
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2469
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2470
+ }
2471
+ });
2472
+
2473
+ // …while the correlative `not only X but ALSO Y` still instructs the close — the additive signal is the
2474
+ // `also`, so requiring it keeps the cited bypass flagged without the bare-`but` false positive.
2475
+ test("#858 the correlative `not only … but also …` close still flags (additive via `also`)", () => {
2476
+ const bypasses = [
2477
+ "Implement one slice of #12. Do not only close #12, but also update the docs.",
2478
+ "Implement criterion 1 of #12. Do not just close #12; but also add a release note.",
2479
+ ];
2480
+ for (const prompt of bypasses) {
2481
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2482
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2483
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2484
+ }
2485
+ });
2486
+
2487
+ // Issue #858 (round-8 adversarial review): the correlative re-activation fired only on `just`/`only`, so
2488
+ // the SAME additive construction with another manner adverb (`merely`/`simply`/`basically`) + an `also`
2489
+ // continuation stayed a false negation and the partial close validated. The additive signal is the `also`
2490
+ // continuation, not the specific adverb, so the correlative class now spans the manner-adverb family.
2491
+ test("#858 an additive `not merely/simply/basically … ; also …` close is a partial-scope-close", () => {
2492
+ const bypasses = [
2493
+ "Implement criterion 1 of #12. Do not merely close #12; also add a note.",
2494
+ "Implement one slice of #12. Do not simply close #12; also update the docs.",
2495
+ "Do the parser part of #12. Do not basically close #12; also open a follow-up.",
2496
+ ];
2497
+ for (const prompt of bypasses) {
2498
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2499
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2500
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2501
+ }
2502
+ });
2503
+
2504
+ // …while the synonymous additive continuation `as well` (in place of `also`) still instructs the close —
2505
+ // `Do not just close #12; add a note as well.` performs the close AND adds a note. (A bare `too` is NOT
2506
+ // treated as additive: it is also the intensifier `too risky/early`, which would over-fire on a safe
2507
+ // negated close — a fail-closed tradeoff that keeps `too` a safe negation.)
2508
+ test("#858 an additive `not just/only … ; … as well` close is a partial-scope-close", () => {
2509
+ const bypasses = [
2510
+ "Implement criterion 1 of #12. Do not just close #12; add a note as well.",
2511
+ "Implement one slice of #12. Do not only close #12; update the docs as well.",
2512
+ ];
2513
+ for (const prompt of bypasses) {
2514
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2515
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2516
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2517
+ }
2518
+ });
2519
+
2520
+ // …and a bare `too` is the INTENSIFIER (`too risky`/`too early`) as often as the additive (`add a note
2521
+ // too`), so it is deliberately NOT an additive trigger — a safe negated close followed by a `too <adj>`
2522
+ // constraint stays a safe negated close (the additive widening must not over-fire on the intensifier).
2523
+ test("#858 a `too <adjective>` intensifier after a negated close is NOT additive (no false positive)", () => {
2524
+ const ok = [
2525
+ "Implement criterion 1 of #12. Do not just close #12; it is too risky.",
2526
+ "Implement one slice of #12. Do not only close #12; it is too early to ship.",
2527
+ ];
2528
+ for (const prompt of ok) {
2529
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2530
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2531
+ }
2532
+ });
2533
+
2534
+ // Issue #858 (round-8 adversarial review): the active-voice attribution subject allowlist omitted the
2535
+ // common actor nouns, so an active-voice `The team / another team / the upstream slice delivers the full
2536
+ // scope of #12` bypassed — asymmetric with the passive `handled by the team`, which already disqualifies.
2537
+ test("#858 active-voice team/upstream-slice ownership disqualifies the full-scope marker", () => {
2538
+ const bypasses = [
2539
+ "The team delivers the full scope of #12; implement criterion 1 and close #12.",
2540
+ "Another team owns the whole issue #12; implement criterion 1 and close #12.",
2541
+ "The upstream slice delivers the full scope of #12; implement criterion 1 and close #12.",
2542
+ "Other teams handle every acceptance criterion of #12; do the parser part and close #12.",
2543
+ ];
2544
+ for (const prompt of bypasses) {
2545
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2546
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2547
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2548
+ }
2549
+ });
2550
+
2551
+ // Issue #858 (round-8 adversarial review): the colon fix admitted only a single optional colon, so a
2552
+ // doubled colon (`Closes:: #12` / `Closes: : #12`) — which GitHub's trailing-colon trim still closes —
2553
+ // was not detected and the partial close validated.
2554
+ test("#858 the double-colon closing form (`Closes:: #12`) is detected as a partial-scope-close", () => {
2555
+ const bypasses = [
2556
+ "Implement criterion 1 of #12. Closes:: #12.",
2557
+ "Implement the parser slice of #12. Fixes: : #12.",
2558
+ ];
2559
+ for (const prompt of bypasses) {
2560
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2561
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2562
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2563
+ }
2564
+ });
2565
+
2566
+ // Issue #858 (round-9 review, app/deliveryGraph.ts:660 — "Preserve subjects when splitting compound
2567
+ // predicates"): `deliveryAssertionAround` splits the marker's clause at EVERY `and`, so a compound
2568
+ // predicate (`Siblings plan and deliver the full scope of #12`) drops the segment containing the
2569
+ // grammatical SUBJECT (`Siblings plan`) and leaves only `deliver the full scope…` in the assertion —
2570
+ // `SCOPE_ACTIVE_VOICE_OTHERS` then cannot see the `Siblings` actor and the attribution bypass returns.
2571
+ // A compound predicate shares ONE subject across both verbs, so the subject must be retained when the
2572
+ // post-`and` segment is a bare verb phrase (no new subject of its own).
2573
+ test("#858 a compound-predicate subject is retained for active-voice attribution (no bypass)", () => {
2574
+ const bypasses = [
2575
+ "Siblings plan and deliver the full scope of #12; implement criterion 1 and close #12.",
2576
+ "The other slices design and own the whole issue #12; implement part and close it.",
2577
+ "Peers build and handle the full scope of #12; implement the UI and close #12.",
2578
+ ];
2579
+ for (const prompt of bypasses) {
2580
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2581
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2582
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2583
+ }
2584
+ });
2585
+
2586
+ // …while an `and` that coordinates an INDEPENDENT constraint (its own subject + verb) still splits the
2587
+ // assertion, so a disqualifier governing that OTHER constraint does not disqualify the marker (the
2588
+ // round-6 scoping guarantee is preserved — only a shared-subject compound predicate retains the subject).
2589
+ test("#858 an independent `and`-coordinated constraint still splits the assertion (no over-fire)", () => {
2590
+ const ok = [
2591
+ "Deliver the full scope of #12 and the regression suite is handled by another team; close #12.",
2592
+ "Deliver the full scope of #12 and do not introduce regressions; close #12.",
2593
+ ];
2594
+ for (const prompt of ok) {
2595
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2596
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2597
+ }
2598
+ });
2599
+
2600
+ // Issue #858 (round-13 review, app/deliveryGraph.ts `compoundAtToken`): the per-coordinator threshold
2601
+ // scanned EVERY later token until some suffix reached a completion verb, so an INDEPENDENT clause whose
2602
+ // verb merely sits later (`… and this slice delivers the full scope of #12`) was misclassified as a
2603
+ // compound-predicate continuation. The `and` then did NOT split, so the marker's assertion retained the
2604
+ // prior segment's unrelated negation/attribution and a legitimate full-scope acknowledgement was
2605
+ // rejected. The compound test must ask only whether the coordinator's INTRODUCED text is verb-LED
2606
+ // (`leadVerbReach[te]`), not whether any later suffix reaches a verb.
2607
+ test("#858 an independent clause after `and` is not a compound predicate (verb-led lead test)", () => {
2608
+ const ok = [
2609
+ // Copilot's cited shape: an unrelated negation before `and`, then an independent self-assertion.
2610
+ "The team must not regress and this slice delivers the full scope of #12. Close #12.",
2611
+ "The team must not regress and the slice delivers the full scope of #12. Close #12.",
2612
+ // The same misclassification via an unrelated ATTRIBUTION retained across the unsplit `and`.
2613
+ "The parser is handled by siblings and this slice delivers the full scope of #12. Close #12.",
2614
+ // A multi-word subject pushes the verb even later; the lead test must still see a non-verb lead.
2615
+ "The team must not regress and the parser subsystem carefully delivers the full scope of #12. Close #12.",
2616
+ ];
2617
+ for (const prompt of ok) {
2618
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2619
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2620
+ }
2621
+ });
2622
+
2623
+ // …while a genuinely verb-led (or adverb-led) compound predicate still SHARES the prior subject, so an
2624
+ // others-subject / negation governing it still disqualifies the marker (the round-9 retention is kept).
2625
+ test("#858 a verb-led compound predicate still shares the subject (no fail-open)", () => {
2626
+ const bypasses = [
2627
+ "Siblings plan and deliver the full scope of #12; implement criterion 1 and close #12.",
2628
+ "The team must not regress and deliver the full scope of #12. Close #12.",
2629
+ "The team must not regress and quickly deliver the full scope of #12. Close #12.",
2630
+ ];
2631
+ for (const prompt of bypasses) {
2632
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2633
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2634
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2635
+ }
2636
+ });
2637
+
2638
+ // Issue #858 (round-13 review, app/deliveryGraph.ts `SCOPE_NEGATED_AFTER_MARKER_DELIVERY`): the
2639
+ // re-catch scanned the marker's WHOLE clause, so a negator that PRECEDES the marker (`The tests must not
2640
+ // regress and this slice delivers the full scope of #12`) matched `not … delivers` inside the four-word
2641
+ // window and rejected a legitimate acknowledgement — the negation governs the unrelated earlier
2642
+ // constraint, not the marker. The check must run against the marker-RELATIVE suffix (from the marker to
2643
+ // the clause end), not the whole clause.
2644
+ test("#858 an after-marker delivery negation must sit AFTER the marker (marker-relative suffix)", () => {
2645
+ const ok = [
2646
+ "The tests must not regress and this slice delivers the full scope of #12. Close #12.",
2647
+ "We never skip tests and this slice delivers the full scope of #12. Close #12.",
2648
+ ];
2649
+ for (const prompt of ok) {
2650
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2651
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2652
+ }
2653
+ });
2654
+
2655
+ // …while a negator that genuinely sits AFTER the marker and references delivery still disclaims it
2656
+ // (the round-6 fail-open the re-catch exists to close stays closed).
2657
+ test("#858 a genuine after-marker delivery negation still disqualifies (no fail-open)", () => {
2658
+ const bypasses = [
2659
+ "Deliver the full scope of #12 and it is not fully delivered. Close #12.",
2660
+ "Deliver the full scope of #12 and that scope is never delivered. Close #12.",
2661
+ ];
2662
+ for (const prompt of bypasses) {
2663
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2664
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2665
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2666
+ }
2667
+ });
2668
+
2669
+ // Issue #858 (round-9 review, app/deliveryGraph.ts:478 — "Treat our team as self-ownership in active
2670
+ // voice"): the others-subject pattern admitted an optional `our` before `team`, so `Our team delivers the
2671
+ // full scope of #12` was classified as EXTERNAL ownership and rejected — even though the passive
2672
+ // equivalent `delivered by our team` is explicitly treated as valid SELF-ownership. `our team` is the
2673
+ // current slice, so it must NOT disqualify; `the/another/other team` still do.
2674
+ test("#858 active-voice `our team` is self-ownership and validates (active check fires only on others)", () => {
2675
+ const ok = [
2676
+ "Our team delivers the full scope of #12; close #12.",
2677
+ "Our team owns the whole issue #12; close it.",
2678
+ ];
2679
+ for (const prompt of ok) {
2680
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2681
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2682
+ }
2683
+ });
2684
+
2685
+ // …while `the team` / `another team` / `other teams` (no self-possessive) remain OTHERS and still
2686
+ // disqualify — the self-carve-out is only for the `our`-possessive self-reference.
2687
+ test("#858 active-voice `the/another/other team` still disqualifies (not self)", () => {
2688
+ const bypasses = [
2689
+ "The team delivers the full scope of #12; implement criterion 1 and close #12.",
2690
+ "Another team delivers the full scope of #12; implement criterion 1 and close #12.",
2691
+ "Other teams deliver the full scope of #12; implement criterion 1 and close #12.",
2692
+ ];
2693
+ for (const prompt of bypasses) {
2694
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2695
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2696
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2697
+ }
2698
+ });
2699
+
2700
+ // Issue #858 (round-9 review, "Previously missed", app/deliveryGraph.ts:303 — "Recognize possessive
2701
+ // pronouns in closing targets"): the closing grammar accepted `it`/`the issue`/`that issue`/`this issue`
2702
+ // but not the POSSESSIVE `its issue`, so `…open a PR that closes its issue.` produced no
2703
+ // `partial-scope-close`. The possessive-pronoun form is now an (unresolved/pronoun) closing target in
2704
+ // both the prefilter and `CLOSING_TARGET_PATTERN`.
2705
+ test("#858 the possessive-pronoun close (`closes its issue`) is detected as a partial-scope-close", () => {
2706
+ const bypasses = [
2707
+ "Implement criterion 1 of #12 and open a PR that closes its issue.",
2708
+ "Implement the parser slice of #12; a follow-up PR resolves its issue.",
2709
+ ];
2710
+ for (const prompt of bypasses) {
2711
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2712
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2713
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2714
+ }
2715
+ });
2716
+
2717
+ // …while a full-scope marker still licenses the possessive-pronoun close (the new target is only a
2718
+ // detection widening — the legitimate final closer that owns the whole scope still validates).
2719
+ test("#858 a full-scope marker licenses the `closes its issue` form (no false positive)", () => {
2720
+ const ok = [
2721
+ "Deliver the full scope of #12 and open a PR that closes its issue.",
2722
+ "This slice covers every acceptance criterion of #12; the PR resolves its issue.",
2723
+ ];
2724
+ for (const prompt of ok) {
2725
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2726
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2727
+ }
2728
+ });
2729
+
2730
+ // Issue #858 (round-9 adversarial review, app/deliveryGraph.ts:715): the compound-predicate
2731
+ // subject-retention heuristic (`COMPOUND_PREDICATE_LEAD`) admits an ADVERB-LED continuation
2732
+ // (`(?:\w+ly\s+)?`), so `deliveryAssertionAround` correctly retains the subject for `Siblings plan and
2733
+ // carefully deliver the full scope of #12` — but the retained assertion then failed
2734
+ // `SCOPE_ACTIVE_VOICE_OTHERS`, whose verb had to sit within `(?:\w+\s+){0,2}?` of the subject and so
2735
+ // could not reach across the `and carefully` coordinator gap. The exact active-voice attribution bypass
2736
+ // the compound-predicate fix exists to close still worked the moment one adverb was inserted. The
2737
+ // subject-to-verb window now also spans a coordinator (`and`/`or`/`then`) plus adverbs.
2738
+ test("#858 an adverb-led compound predicate still attributes the scope to its others-subject", () => {
2739
+ const bypasses = [
2740
+ "Siblings plan and carefully deliver the full scope of #12; implement criterion 1 and close #12.",
2741
+ "The other slices design and quickly own the whole issue #12; implement part and close it.",
2742
+ "Peers build and then handle the full scope of #12; implement the UI and close #12.",
2743
+ ];
2744
+ for (const prompt of bypasses) {
2745
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2746
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2747
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2748
+ }
2749
+ });
2750
+
2751
+ // …while widening that window must NOT over-fire onto an affirmative SELF assertion that merely has a
2752
+ // coordinator+adverb before its verb, nor re-admit an independent `and`-coordinated constraint (the
2753
+ // round-6 scoping guarantee).
2754
+ test("#858 the widened subject-to-verb window does not over-fire on self or independent constraints", () => {
2755
+ const ok = [
2756
+ "This slice plans and carefully delivers the full scope of #12; close #12.",
2757
+ "We design and then implement every acceptance criterion of #12; close #12.",
2758
+ "Deliver the full scope of #12 and the regression suite is carefully maintained by another team; close #12.",
2759
+ ];
2760
+ for (const prompt of ok) {
2761
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2762
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2763
+ }
2764
+ });
2765
+
2766
+ // Issue #858 (round-9 adversarial review, app/deliveryGraph.ts:482): the `our team` self-carve-out
2767
+ // restructured the team alternative to REQUIRE a determiner (`(?:(?:the|their|another|other)\s+)teams?`),
2768
+ // so the previously-flagged BARE `Team delivers the full scope of #12` / `Teams deliver …` (no
2769
+ // determiner) regressed to validating — a fail-open regression vs. the round-entry code, whose optional
2770
+ // determiner `(?:(?:the|our|their)\s+)?` still covered the bare form. The determiner is optional again
2771
+ // but now excludes `our` specifically, so bare `team`/`teams` is others-attribution while `our team`
2772
+ // stays self-ownership.
2773
+ test("#858 active-voice BARE `team`/`teams` (no determiner) still disqualifies (not self)", () => {
2774
+ const bypasses = [
2775
+ "Team delivers the full scope of #12; implement criterion 1 and close #12.",
2776
+ "Teams deliver the full scope of #12; implement criterion 1 and close #12.",
2777
+ "Team owns the whole issue #12; implement part and close it.",
2778
+ ];
2779
+ for (const prompt of bypasses) {
2780
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2781
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2782
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2783
+ }
2784
+ });
2785
+
2786
+ // Issue #858 (round-10 review, app/deliveryGraph.ts:303 — "Handle issue-first and passive closing
2787
+ // directives"): the closing grammar recognised only VERB-BEFORE-OBJECT forms (`close #12`,
2788
+ // `closes issue #12`), so a partial brief that phrases the SAME instruction with the issue as the
2789
+ // SUBJECT of a passive/participial close — `Implement criterion 1 of #12 and ensure issue #12 is
2790
+ // closed by the PR` — produced no error and validated. That is still a direct instruction to close
2791
+ // the issue from a part-scope node, so the issue-first/passive form must be detected (and its target
2792
+ // extracted) exactly like the active form.
2793
+ test("#858 a partial brief that closes via an issue-first/passive directive is rejected (passive closing form)", () => {
2794
+ const bypasses = [
2795
+ // The cited case.
2796
+ "Implement criterion 1 of #12 and ensure issue #12 is closed by the PR.",
2797
+ // Bare-#N subject, auxiliaries, and the `get`-passive.
2798
+ "Implement criterion 1 of #12; #12 is closed by the PR.",
2799
+ "Implement criterion 1 of #12; #12 will be closed by the PR.",
2800
+ "Implement criterion 1 of #12; make sure #12 gets closed.",
2801
+ // Participial directive with no auxiliary.
2802
+ "Implement criterion 1 of #12; see #12 closed.",
2803
+ "Implement criterion 1 of #12 and mark #12 as resolved.",
2804
+ "Implement criterion 1 of #12; with #12 closed by the PR.",
2805
+ // Pronoun / `the issue` subjects and the other verb families.
2806
+ "Implement criterion 1 of #12; the issue is closed by the PR.",
2807
+ "Implement criterion 1 of #12; it is closed by the PR.",
2808
+ "Implement criterion 1 of #12; issue #12 is fixed by the PR.",
2809
+ "Implement criterion 1 of #12; GitHub issue #12 is resolved by the PR.",
2810
+ // Repo-qualified and URL subjects attribute the close to THAT issue.
2811
+ "Implement criterion 1 of owner/repo#12; issue owner/repo#12 is closed by the PR.",
2812
+ "Implement criterion 1 of #12; the issue at https://github.com/o/r/issues/12 is closed by the PR.",
2813
+ ];
2814
+ for (const prompt of bypasses) {
2815
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2816
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2817
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2818
+ }
2819
+ });
2820
+
2821
+ // The converse: the passive arm's subject is an issue REFERENCE, never a bare noun, so a benign
2822
+ // passive about a non-issue subject must NOT be read as a closing directive — and a negated passive
2823
+ // close (a safe partial-slice brief that forbids the close) must not over-fire either.
2824
+ test("#858 a benign or negated passive does not over-fire the issue-first close detection (validates)", () => {
2825
+ const ok = [
2826
+ // Benign passives about a non-issue subject.
2827
+ "Implement criterion 1 of #12; the door is closed by the latch.",
2828
+ "Implement criterion 1 of #12; the PR is closed by the merge queue.",
2829
+ "Implement criterion 1 of #12; the ticket status is closed by automation.",
2830
+ "Implement criterion 1 of #12; the milestone is closed by the bot.",
2831
+ "Implement criterion 1 of #12; the window is fixed by the frame.",
2832
+ "Implement criterion 1 of #12; the door gets closed.",
2833
+ "Work on #12; the loop is closed by the feedback.",
2834
+ // A directly-negated passive close is a SAFE partial-slice brief, not a partial-scope-close.
2835
+ "Implement criterion 1 of #12 and ensure issue #12 is not closed by the PR.",
2836
+ "Implement criterion 1 of #12; issue #12 will not be closed by the PR.",
2837
+ "Implement criterion 1 of #12; the issue is never closed by this PR.",
2838
+ "Implement criterion 1 of #12; #12 isn't closed by this PR.",
2839
+ "Implement criterion 1 of #12; ensure the issue is not closed yet.",
2840
+ ];
2841
+ for (const prompt of ok) {
2842
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2843
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2844
+ }
2845
+ });
2846
+
2847
+ // A negator in an UNRELATED earlier clause must not mask an ACTIVE passive close — the passive
2848
+ // negation check reads only the match's own auxiliary window, so a preceding `do not …` cannot
2849
+ // suppress detection of a real close.
2850
+ test("#858 an unrelated earlier negation does not mask an active passive close (no reopened bypass)", () => {
2851
+ const bypasses = [
2852
+ "Implement criterion 1 of #12; do not introduce regressions; issue #12 is closed by the PR.",
2853
+ ];
2854
+ for (const prompt of bypasses) {
2855
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2856
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2857
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2858
+ }
2859
+ });
2860
+
2861
+ // Issue #858 (round-10 adversarial review, app/deliveryGraph.ts:318/379 — the passive arm's auxiliary
2862
+ // window): the window capped at THREE words, so a periphrastic-future passive close of the node's own
2863
+ // issue — `#12 is going to be closed` / `#12 is expected to be closed` (`is/going/to/be` and
2864
+ // `is/expected/to/be` are FOUR window words) — slipped past BOTH mirrored patterns (prefilter +
2865
+ // extractor) and validated. That is the fail-OPEN direction (a real close missed); over-firing is this
2866
+ // guard's safe/fail-closed direction, so the window is widened to four words to catch the whole class.
2867
+ test("#858 a partial brief that closes via a periphrastic-future passive directive is rejected", () => {
2868
+ const bypasses = [
2869
+ "Implement criterion 1 of #12; #12 is going to be closed by the PR.",
2870
+ "Implement criterion 1 of #12; #12 is expected to be closed by the PR.",
2871
+ "Implement criterion 1 of #12 and ensure issue #12 is going to be closed by the PR.",
2872
+ "Implement criterion 1 of #12; the issue is expected to be closed by the PR.",
2873
+ "Implement criterion 1 of owner/repo#12; issue owner/repo#12 is going to be closed by the PR.",
2874
+ ];
2875
+ for (const prompt of bypasses) {
2876
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2877
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2878
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2879
+ }
2880
+ });
2881
+
2882
+ // The widened window must not swallow a negated periphrastic close (a SAFE partial-slice brief) nor a
2883
+ // benign passive about a non-issue subject.
2884
+ test("#858 a benign or negated periphrastic-future passive does not over-fire (validates)", () => {
2885
+ const ok = [
2886
+ "Implement criterion 1 of #12; the door is going to be closed by the janitor.",
2887
+ "Implement criterion 1 of #12; the PR is expected to be closed by the merge queue.",
2888
+ "Implement criterion 1 of #12; issue #12 is not going to be closed by the PR.",
2889
+ ];
2890
+ for (const prompt of ok) {
2891
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2892
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2893
+ }
2894
+ });
2895
+
2896
+ // A FULL-scope closer phrased with the passive/issue-first form must still validate — the new grammar
2897
+ // must recognise the target so the marker's acknowledgement is correctly credited to it.
2898
+ test("#858 a full-scope closer using the issue-first/passive form still validates (must not over-fire)", () => {
2899
+ const ok = [
2900
+ "Deliver the full scope of #12; issue #12 is closed by this PR.",
2901
+ "Deliver the full scope of #12; #12 is closed by this PR.",
2902
+ "Deliver the full scope of #12; #12 gets closed by this PR.",
2903
+ "Deliver the full scope of owner/repo#12; issue owner/repo#12 is closed by this PR.",
2904
+ ];
2905
+ for (const prompt of ok) {
2906
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2907
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2908
+ }
2909
+ });
2910
+
2911
+ // Issue #858 (round-11 review, app/deliveryGraph.ts:379 — the active closing arm): the extractor
2912
+ // captured only the FIRST target of a COORDINATED close, so `Deliver the full scope of #12; implement
2913
+ // only criterion 1 of #13; close #12 and #13.` validated — #13 entered `numbered` only via the
2914
+ // un-anchored sole-issue fallback, never as a checked target, and its under-delivery was never
2915
+ // guarded. Every issue a prompt directs the agent to close must be extracted and acknowledged.
2916
+ test("#858 a coordinated close extracts and validates EVERY target (close #12 and #13)", () => {
2917
+ const bypasses = [
2918
+ // The cited case: #13 is a part-scope node told to close, but only #12 was checked.
2919
+ "Deliver the full scope of #12; implement only criterion 1 of #13; close #12 and #13.",
2920
+ // The `both`/`and` and comma-list forms, and a repo-qualified extra target.
2921
+ "Deliver the full scope of #12; implement criterion 1 of #13; close both #12 and #13.",
2922
+ "Deliver the full scope of #12; do one slice of #13 and one of #14; close #12, #13, and #14.",
2923
+ "Deliver the full scope of #12; implement criterion 1 of owner/repo#13; close #12 and owner/repo#13.",
2924
+ "Deliver the full scope of #12; implement criterion 1 of #13; close #12 and #13 and #14.",
2925
+ ];
2926
+ for (const prompt of bypasses) {
2927
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2928
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2929
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2930
+ }
2931
+ });
2932
+
2933
+ // The converse: when EVERY coordinated target is genuinely acknowledged full-scope, the close is
2934
+ // licensed — extracting the extra targets must not over-fire on a legitimate full-scope close.
2935
+ test("#858 a coordinated close of fully-acknowledged issues still validates (must not over-fire)", () => {
2936
+ const ok = [
2937
+ "Deliver the full scope of #12 and the full scope of #13; close #12 and #13.",
2938
+ "Own the whole issue #12 and the whole issue #13; close both #12 and #13.",
2939
+ ];
2940
+ for (const prompt of ok) {
2941
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2942
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2943
+ }
2944
+ });
2945
+
2946
+ // Issue #858 (round-18 review, app/deliveryGraph.ts — the issue-first/PASSIVE closing arm): the
2947
+ // round-11 fix extracted every target of a coordinated ACTIVE close (`close #12 and #13`) from the
2948
+ // active arm's coordination tail, but the passive arm captured only its FIRST subject — so `Deliver
2949
+ // the full scope of #13; implement only criterion 1 of #12; ensure issues #12 and #13 are closed by
2950
+ // the PR.` validated: the match started at `#13`, checked only that acknowledged target, and never
2951
+ // treated `#12` as closed. The passive subject now carries the same coordination tail, re-scanned
2952
+ // for every extra numbered target so each passively-closed issue needs its own acknowledgement.
2953
+ test("#858 a coordinated passive close extracts and validates EVERY target (issues #12 and #13 are closed)", () => {
2954
+ const bypasses = [
2955
+ // The cited case: #12 is a part-scope node passively closed, but only #13 was checked.
2956
+ "Deliver the full scope of #13; implement only criterion 1 of #12; ensure issues #12 and #13 are closed by the PR.",
2957
+ // The `both`-quantified form, and a comma-list of three.
2958
+ "Deliver the full scope of #13; implement only criterion 1 of #12; ensure both issues #12 and #13 are closed by the PR.",
2959
+ "Deliver the full scope of #14; implement criterion 1 of #12 and one of #13; issues #12, #13, and #14 are closed by the PR.",
2960
+ // A repo-qualified extra target, and the other verb families.
2961
+ "Deliver the full scope of #13; implement criterion 1 of owner/repo#12; issues owner/repo#12 and #13 are closed by the PR.",
2962
+ "Deliver the full scope of #13; implement only criterion 1 of #12; issues #12 and #13 will be fixed by the PR.",
2963
+ "Deliver the full scope of #13; implement only criterion 1 of #12; issues #12 and #13 get resolved by the PR.",
2964
+ ];
2965
+ for (const prompt of bypasses) {
2966
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2967
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
2968
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
2969
+ }
2970
+ });
2971
+
2972
+ // The converse: when EVERY coordinated passive target is genuinely acknowledged full-scope, the
2973
+ // close is licensed — and a coordinated passive whose own auxiliary window is NEGATED stays a safe
2974
+ // partial-slice brief (the negation drop must still fire with the new tail present).
2975
+ test("#858 a coordinated passive close of fully-acknowledged issues still validates (must not over-fire)", () => {
2976
+ const ok = [
2977
+ "Deliver the full scope of #12 and the full scope of #13; ensure issues #12 and #13 are closed by the PR.",
2978
+ "Deliver the full scope of #12 and the full scope of #13; issues #12 and #13 will be closed by the PR.",
2979
+ // A negated coordinated passive is a SAFE partial-slice brief, not a partial-scope-close.
2980
+ "Implement criterion 1 of #12 and criterion 1 of #13; issues #12 and #13 are not closed by the PR.",
2981
+ ];
2982
+ for (const prompt of ok) {
2983
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
2984
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
2985
+ }
2986
+ });
2987
+
2988
+ // Issue #858 (round-11 suppressed advisory, app/deliveryGraph.ts:392 — `NEGATED_CLOSE_PREFIX`): the
2989
+ // negation gap admitted only ADVERBS and `to`, so the safe, contract-compliant meta-instruction
2990
+ // `Do not include Closes #12; use Part of #12` was treated as an ACTIVE close (`include` is not an
2991
+ // adverb) and the graph was wrongly rejected. A negated `use`/`write`/`include`/`add` of a closing
2992
+ // keyword is a SAFE partial-slice brief — explicitly telling the agent NOT to close — so it must
2993
+ // validate. A meaning-flipping verb (`do not forget to close`) still stays ACTIVE.
2994
+ test("#858 a negated use/write/include/add of a closing keyword is a safe partial-slice brief (validates)", () => {
2995
+ const ok = [
2996
+ "Implement criterion 1 of #12. Do not include Closes #12; use Part of #12.",
2997
+ "Implement criterion 1 of #12. Do not use Closes #12; use Part of #12.",
2998
+ "Implement criterion 1 of #12. Do not write Fixes #12 in the commit body.",
2999
+ "Implement criterion 1 of #12. Do not add a Closes #12 line; reference Part of #12.",
3000
+ "Implement criterion 1 of #12. Never include Resolves #12 in the PR description.",
3001
+ ];
3002
+ for (const prompt of ok) {
3003
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
3004
+ assertEquals(validateDeliveryGraph(g), [], `expected no errors for: ${prompt}`);
3005
+ }
3006
+ });
3007
+
3008
+ // Widening the negation gap to admit `use`/`write`/`include`/`add` must NOT reopen the bypass for a
3009
+ // meaning-flipping verb — `do not forget/hesitate to close #12` still INSTRUCTS the close.
3010
+ test("#858 a meaning-flipping verb still keeps the close active (no reopened bypass)", () => {
3011
+ const bypasses = [
3012
+ "Implement criterion 1 of #12. Do not forget to close #12.",
3013
+ "Implement criterion 1 of #12. Do not hesitate to close #12.",
3014
+ ];
3015
+ for (const prompt of bypasses) {
3016
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
3017
+ const err = hasCode(validateDeliveryGraph(g), "partial-scope-close");
3018
+ assertEquals(err.path, "nodes[0].agent.prompt", `expected rejection for: ${prompt}`);
3019
+ }
3020
+ });
3021
+
3022
+ // Issue #858 (round-11 + round-12 review, app/deliveryGraph.ts full-scope scan): every marker occurrence
3023
+ // re-scanned back/forward to its clause boundaries AND re-ran the coordinator/compound-predicate scan per
3024
+ // marker, so a long prompt repeating a marker — worst case a single DELIMITER-FREE clause interleaving
3025
+ // `full scope … and …` (where `scope` makes every `and` a compound predicate, so all markers share one
3026
+ // growing segment) — made validation quadratic and could monopolise the event loop across the 256 allowed
3027
+ // nodes. The scan is now a per-clause single forward pass (precomputed boundaries + coordinator
3028
+ // thresholds + a far/near split + monotone disqualifier memoisation). These tests assert CORRECTNESS on
3029
+ // the worst-case shape and assert LINEAR scaling via a deterministic operation count
3030
+ // (`drainClauseScanSteps`) — never a wall-clock threshold, which is nondeterministic under CI load
3031
+ // (issue #858 round-12 review).
3032
+ test("#858 a long prompt with repeated full-scope markers validates correctly (period-delimited)", () => {
3033
+ const filler = "Deliver the full scope of #12. ".repeat(400); // ~12k chars, one clause per repetition
3034
+ const g = {
3035
+ nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt: `${filler}Close #12.` } }],
3036
+ edges: [],
3037
+ };
3038
+ // Every occurrence is an affirmative full-scope acknowledgement of #12, so the close is licensed.
3039
+ assertEquals(validateDeliveryGraph(g), []);
3040
+ });
3041
+
3042
+ test("#858 the full-scope scan is linear on the worst-case delimiter-free clause (deterministic op count)", () => {
3043
+ // The worst-case clause shape the review cited: ONE delimiter-free clause repeating `full scope … and
3044
+ // …`, so every coordinator is a compound predicate and every marker shares one growing segment. The
3045
+ // operation counter measures coordinator-threshold sweep steps + near-window coordinator examinations —
3046
+ // the two former quadratic drivers. (The assertion-segment disqualifiers are memoised per segment-start
3047
+ // group, so they are not per-marker on this shape either.)
3048
+ const worstCase = (reps: number) =>
3049
+ `Deliver ${"full scope of #12 and ".repeat(reps)}the whole issue. Close #12.`;
3050
+ const stepsFor = (reps: number): number => {
3051
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt: worstCase(reps) } }], edges: [] };
3052
+ drainClauseScanSteps();
3053
+ // Every occurrence is an affirmative full-scope acknowledgement of #12, so the close is licensed.
3054
+ assertEquals(validateDeliveryGraph(g), []);
3055
+ return drainClauseScanSteps();
3056
+ };
3057
+ const small = stepsFor(100);
3058
+ const large = stepsFor(800); // 8× the markers/coordinators/clause-length
3059
+ // Linear scaling: 8× the input must cost ≤ 8× the operations (plus a small constant slack for the
3060
+ // fixed per-clause setup). A quadratic scan would cost ~64×.
3061
+ assert(small > 0, "expected the small input to do non-zero scan work");
3062
+ assert(
3063
+ large <= small * 8 + 64,
3064
+ `expected linear scaling, but ops(800)=${large} vs 8×ops(100)+slack=${small * 8 + 64} — the scan is superlinear`,
3065
+ );
3066
+ });
3067
+
3068
+ // Issue #858 (round-12 review, app/deliveryGraph.ts clauseBoundaries): treating every `:` (and `.`) as a
3069
+ // clause boundary split an issue URL at `https:` / `github.com`, so the attribution/negation checks saw
3070
+ // only the fragment before the URL and a sibling-attributed scope validated. Delimiters INSIDE a URL span
3071
+ // are not clause boundaries.
3072
+ test("#858 an issue URL is not split at its scheme colon / interior delimiters (attribution still seen)", () => {
3073
+ // The full scope of the URL-named issue is attributed to siblings — must NOT licence the close.
3074
+ const attributed =
3075
+ "The full scope of https://github.com/acme/a/issues/12 is handled by siblings; implement criterion 1 and close https://github.com/acme/a/issues/12.";
3076
+ const g1 = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt: attributed } }], edges: [] };
3077
+ assertEquals(hasCode(validateDeliveryGraph(g1), "partial-scope-close").code, "partial-scope-close");
3078
+
3079
+ // …but a genuine full-scope acknowledgement anchored to a URL, with no sibling attribution, must still
3080
+ // VALIDATE (the URL-span fix must not over-fire and break legitimate closers).
3081
+ const genuine =
3082
+ "Deliver the full scope of #12. See https://github.com/acme/a/issues/12 for the checklist. Close #12.";
3083
+ const g2 = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt: genuine } }], edges: [] };
3084
+ assertEquals(validateDeliveryGraph(g2), []);
3085
+ });
3086
+
3087
+ // Issue #858 (round-12 "previously missed" advisory, app/deliveryGraph.ts PART_QUALIFIER_AFTER_MARKER /
3088
+ // anchoredIssueKey): the possessive qualifier only accepted an ASCII apostrophe, so typographic text
3089
+ // (`issue’s`, U+2019) bypassed the part-narrowing guard. Accept both apostrophe forms (the negation
3090
+ // regexes already do).
3091
+ test("#858 a typographic-apostrophe possessive still narrows the scope to a part (rejected)", () => {
3092
+ const ascii = "Deliver the whole issue's parser slice. Closes #12.";
3093
+ const typographic = "Deliver the whole issue’s parser slice. Closes #12.";
3094
+ for (const prompt of [ascii, typographic]) {
3095
+ const g = { nodes: [{ id: "a", kind: "agent", agent: { jobType: "j", prompt } }], edges: [] };
3096
+ assertEquals(
3097
+ hasCode(validateDeliveryGraph(g), "partial-scope-close").code,
3098
+ "partial-scope-close",
3099
+ `expected rejection for: ${prompt}`,
3100
+ );
3101
+ }
3102
+ });