@attalabs/vinaya 0.8.0 → 0.8.2

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.
@@ -1220,7 +1220,8 @@ function mapForgeFacts(raw) {
1220
1220
  reviewDecision: mapReviewDecision(raw.pullRequest?.reviewDecision),
1221
1221
  stateReason: mapStateReason(raw.issue.stateReason),
1222
1222
  closedAt: raw.issue.closedAt ?? null,
1223
- mergedAt: raw.pullRequest?.mergedAt ?? null
1223
+ mergedAt: raw.pullRequest?.mergedAt ?? null,
1224
+ closedByActor: raw.closedByActor ?? null
1224
1225
  };
1225
1226
  }
1226
1227
  function mapStateReason(reason) {
@@ -1313,6 +1314,7 @@ function buildBatchQuery(tranche, tasks) {
1313
1314
  timelineItems(last: 1, itemTypes: [CLOSED_EVENT]) {
1314
1315
  nodes {
1315
1316
  ... on ClosedEvent {
1317
+ actor { login }
1316
1318
  closer {
1317
1319
  ... on PullRequest {
1318
1320
  number
@@ -1350,7 +1352,8 @@ function extractRawFromResponse(repository, alias) {
1350
1352
  const issue = repository[`${alias}_issue`];
1351
1353
  const ref = repository[`${alias}_ref`];
1352
1354
  const prs = repository[`${alias}_prs`];
1353
- const rawCloser = issue?.timelineItems?.nodes?.[0]?.closer ?? null;
1355
+ const timelineNode = issue?.timelineItems?.nodes?.[0];
1356
+ const rawCloser = timelineNode?.closer ?? null;
1354
1357
  const closingPr = rawCloser && typeof rawCloser.number === "number" && typeof rawCloser.url === "string" ? rawCloser : null;
1355
1358
  const branchPr = prs && prs.nodes.length > 0 && prs.nodes[0] ? prs.nodes[0] : null;
1356
1359
  return {
@@ -1362,7 +1365,8 @@ function extractRawFromResponse(repository, alias) {
1362
1365
  labels: issue.labels.nodes.map((n) => n.name)
1363
1366
  } : null,
1364
1367
  refExists: Boolean(ref && ref.name.length > 0),
1365
- pullRequest: closingPr ?? branchPr
1368
+ pullRequest: closingPr ?? branchPr,
1369
+ closedByActor: timelineNode?.actor?.login ?? null
1366
1370
  };
1367
1371
  }
1368
1372
  function describeError(err) {
@@ -1500,6 +1504,11 @@ var FORGE_FACT_INPUTS = [
1500
1504
  fact: "mergedAt",
1501
1505
  readsFrom: "PullRequest.mergedAt",
1502
1506
  meaning: "Timestamp for the coherence oracle grandfather cutoff. No derivation rule reads it."
1507
+ },
1508
+ {
1509
+ fact: "closedByActor",
1510
+ readsFrom: "ClosedEvent.actor.login (most recent timeline CLOSED_EVENT)",
1511
+ meaning: "GitHub login that hand-closed the Issue. No task-status derivation rule reads it — it is consumed by dispatch-gate.ts/coherence-checks.ts A1 to recognize a hand-closed dependency as resolved (task vinaya-engine-v1 21, #99), not by this status model."
1503
1512
  }
1504
1513
  ];
1505
1514
  var DERIVED_STATUSES = [
@@ -1979,7 +1988,7 @@ function isGrandfathered(isoDate) {
1979
1988
  return isoDate.slice(0, 10) < COHERENCE_ENFORCED_FROM;
1980
1989
  }
1981
1990
  var R1_GRANDFATHERED_ISSUES = new Set([279, 280, 281, 282]);
1982
- function checkA1(entries) {
1991
+ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
1983
1992
  const failures = [];
1984
1993
  for (const e of entries) {
1985
1994
  if (!e.facts)
@@ -1987,6 +1996,9 @@ function checkA1(entries) {
1987
1996
  if (e.facts.stateReason === "not_planned")
1988
1997
  continue;
1989
1998
  if (e.facts.issueState === "closed" && e.facts.prState !== "merged") {
1999
+ const handClosed = e.facts.stateReason === "completed" && isPrincipal(e.facts.closedByActor, principalAllowlist);
2000
+ if (handClosed)
2001
+ continue;
1990
2002
  failures.push({
1991
2003
  issue: e.task.issue,
1992
2004
  tranche: e.trancheSlug,
@@ -2676,9 +2688,13 @@ function parseGlossaryTerms(glossaryContent) {
2676
2688
  return terms;
2677
2689
  }
2678
2690
  // ../../packages/aeg-core/src/dispatch-gate.ts
2691
+ function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
2692
+ return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);
2693
+ }
2679
2694
  function checkDispatchReadiness(input) {
2680
2695
  const { trancheSlug, task } = input;
2681
2696
  const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
2697
+ const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
2682
2698
  const blockers = [];
2683
2699
  if (task.issue === null) {
2684
2700
  blockers.push(`dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
@@ -2689,7 +2705,7 @@ function checkDispatchReadiness(input) {
2689
2705
  blockers.push(`dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
2690
2706
  }
2691
2707
  for (const dep of input.dependsOn) {
2692
- if (!dep.merged) {
2708
+ if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
2693
2709
  const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
2694
2710
  blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
2695
2711
  }
@@ -1218,7 +1218,8 @@ function mapForgeFacts(raw) {
1218
1218
  reviewDecision: mapReviewDecision(raw.pullRequest?.reviewDecision),
1219
1219
  stateReason: mapStateReason(raw.issue.stateReason),
1220
1220
  closedAt: raw.issue.closedAt ?? null,
1221
- mergedAt: raw.pullRequest?.mergedAt ?? null
1221
+ mergedAt: raw.pullRequest?.mergedAt ?? null,
1222
+ closedByActor: raw.closedByActor ?? null
1222
1223
  };
1223
1224
  }
1224
1225
  function mapStateReason(reason) {
@@ -1311,6 +1312,7 @@ function buildBatchQuery(tranche, tasks) {
1311
1312
  timelineItems(last: 1, itemTypes: [CLOSED_EVENT]) {
1312
1313
  nodes {
1313
1314
  ... on ClosedEvent {
1315
+ actor { login }
1314
1316
  closer {
1315
1317
  ... on PullRequest {
1316
1318
  number
@@ -1348,7 +1350,8 @@ function extractRawFromResponse(repository, alias) {
1348
1350
  const issue = repository[`${alias}_issue`];
1349
1351
  const ref = repository[`${alias}_ref`];
1350
1352
  const prs = repository[`${alias}_prs`];
1351
- const rawCloser = issue?.timelineItems?.nodes?.[0]?.closer ?? null;
1353
+ const timelineNode = issue?.timelineItems?.nodes?.[0];
1354
+ const rawCloser = timelineNode?.closer ?? null;
1352
1355
  const closingPr = rawCloser && typeof rawCloser.number === "number" && typeof rawCloser.url === "string" ? rawCloser : null;
1353
1356
  const branchPr = prs && prs.nodes.length > 0 && prs.nodes[0] ? prs.nodes[0] : null;
1354
1357
  return {
@@ -1360,7 +1363,8 @@ function extractRawFromResponse(repository, alias) {
1360
1363
  labels: issue.labels.nodes.map((n) => n.name)
1361
1364
  } : null,
1362
1365
  refExists: Boolean(ref && ref.name.length > 0),
1363
- pullRequest: closingPr ?? branchPr
1366
+ pullRequest: closingPr ?? branchPr,
1367
+ closedByActor: timelineNode?.actor?.login ?? null
1364
1368
  };
1365
1369
  }
1366
1370
  function describeError(err) {
@@ -1498,6 +1502,11 @@ var FORGE_FACT_INPUTS = [
1498
1502
  fact: "mergedAt",
1499
1503
  readsFrom: "PullRequest.mergedAt",
1500
1504
  meaning: "Timestamp for the coherence oracle grandfather cutoff. No derivation rule reads it."
1505
+ },
1506
+ {
1507
+ fact: "closedByActor",
1508
+ readsFrom: "ClosedEvent.actor.login (most recent timeline CLOSED_EVENT)",
1509
+ meaning: "GitHub login that hand-closed the Issue. No task-status derivation rule reads it — it is consumed by dispatch-gate.ts/coherence-checks.ts A1 to recognize a hand-closed dependency as resolved (task vinaya-engine-v1 21, #99), not by this status model."
1501
1510
  }
1502
1511
  ];
1503
1512
  var DERIVED_STATUSES = [
@@ -1977,7 +1986,7 @@ function isGrandfathered(isoDate) {
1977
1986
  return isoDate.slice(0, 10) < COHERENCE_ENFORCED_FROM;
1978
1987
  }
1979
1988
  var R1_GRANDFATHERED_ISSUES = new Set([279, 280, 281, 282]);
1980
- function checkA1(entries) {
1989
+ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
1981
1990
  const failures = [];
1982
1991
  for (const e of entries) {
1983
1992
  if (!e.facts)
@@ -1985,6 +1994,9 @@ function checkA1(entries) {
1985
1994
  if (e.facts.stateReason === "not_planned")
1986
1995
  continue;
1987
1996
  if (e.facts.issueState === "closed" && e.facts.prState !== "merged") {
1997
+ const handClosed = e.facts.stateReason === "completed" && isPrincipal(e.facts.closedByActor, principalAllowlist);
1998
+ if (handClosed)
1999
+ continue;
1988
2000
  failures.push({
1989
2001
  issue: e.task.issue,
1990
2002
  tranche: e.trancheSlug,
@@ -2674,9 +2686,13 @@ function parseGlossaryTerms(glossaryContent) {
2674
2686
  return terms;
2675
2687
  }
2676
2688
  // ../../packages/aeg-core/src/dispatch-gate.ts
2689
+ function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
2690
+ return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);
2691
+ }
2677
2692
  function checkDispatchReadiness(input) {
2678
2693
  const { trancheSlug, task } = input;
2679
2694
  const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
2695
+ const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
2680
2696
  const blockers = [];
2681
2697
  if (task.issue === null) {
2682
2698
  blockers.push(`dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
@@ -2687,7 +2703,7 @@ function checkDispatchReadiness(input) {
2687
2703
  blockers.push(`dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
2688
2704
  }
2689
2705
  for (const dep of input.dependsOn) {
2690
- if (!dep.merged) {
2706
+ if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
2691
2707
  const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
2692
2708
  blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
2693
2709
  }
@@ -1218,7 +1218,8 @@ function mapForgeFacts(raw) {
1218
1218
  reviewDecision: mapReviewDecision(raw.pullRequest?.reviewDecision),
1219
1219
  stateReason: mapStateReason(raw.issue.stateReason),
1220
1220
  closedAt: raw.issue.closedAt ?? null,
1221
- mergedAt: raw.pullRequest?.mergedAt ?? null
1221
+ mergedAt: raw.pullRequest?.mergedAt ?? null,
1222
+ closedByActor: raw.closedByActor ?? null
1222
1223
  };
1223
1224
  }
1224
1225
  function mapStateReason(reason) {
@@ -1311,6 +1312,7 @@ function buildBatchQuery(tranche, tasks) {
1311
1312
  timelineItems(last: 1, itemTypes: [CLOSED_EVENT]) {
1312
1313
  nodes {
1313
1314
  ... on ClosedEvent {
1315
+ actor { login }
1314
1316
  closer {
1315
1317
  ... on PullRequest {
1316
1318
  number
@@ -1348,7 +1350,8 @@ function extractRawFromResponse(repository, alias) {
1348
1350
  const issue = repository[`${alias}_issue`];
1349
1351
  const ref = repository[`${alias}_ref`];
1350
1352
  const prs = repository[`${alias}_prs`];
1351
- const rawCloser = issue?.timelineItems?.nodes?.[0]?.closer ?? null;
1353
+ const timelineNode = issue?.timelineItems?.nodes?.[0];
1354
+ const rawCloser = timelineNode?.closer ?? null;
1352
1355
  const closingPr = rawCloser && typeof rawCloser.number === "number" && typeof rawCloser.url === "string" ? rawCloser : null;
1353
1356
  const branchPr = prs && prs.nodes.length > 0 && prs.nodes[0] ? prs.nodes[0] : null;
1354
1357
  return {
@@ -1360,7 +1363,8 @@ function extractRawFromResponse(repository, alias) {
1360
1363
  labels: issue.labels.nodes.map((n) => n.name)
1361
1364
  } : null,
1362
1365
  refExists: Boolean(ref && ref.name.length > 0),
1363
- pullRequest: closingPr ?? branchPr
1366
+ pullRequest: closingPr ?? branchPr,
1367
+ closedByActor: timelineNode?.actor?.login ?? null
1364
1368
  };
1365
1369
  }
1366
1370
  function describeError(err) {
@@ -1498,6 +1502,11 @@ var FORGE_FACT_INPUTS = [
1498
1502
  fact: "mergedAt",
1499
1503
  readsFrom: "PullRequest.mergedAt",
1500
1504
  meaning: "Timestamp for the coherence oracle grandfather cutoff. No derivation rule reads it."
1505
+ },
1506
+ {
1507
+ fact: "closedByActor",
1508
+ readsFrom: "ClosedEvent.actor.login (most recent timeline CLOSED_EVENT)",
1509
+ meaning: "GitHub login that hand-closed the Issue. No task-status derivation rule reads it — it is consumed by dispatch-gate.ts/coherence-checks.ts A1 to recognize a hand-closed dependency as resolved (task vinaya-engine-v1 21, #99), not by this status model."
1501
1510
  }
1502
1511
  ];
1503
1512
  var DERIVED_STATUSES = [
@@ -1977,7 +1986,7 @@ function isGrandfathered(isoDate) {
1977
1986
  return isoDate.slice(0, 10) < COHERENCE_ENFORCED_FROM;
1978
1987
  }
1979
1988
  var R1_GRANDFATHERED_ISSUES = new Set([279, 280, 281, 282]);
1980
- function checkA1(entries) {
1989
+ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
1981
1990
  const failures = [];
1982
1991
  for (const e of entries) {
1983
1992
  if (!e.facts)
@@ -1985,6 +1994,9 @@ function checkA1(entries) {
1985
1994
  if (e.facts.stateReason === "not_planned")
1986
1995
  continue;
1987
1996
  if (e.facts.issueState === "closed" && e.facts.prState !== "merged") {
1997
+ const handClosed = e.facts.stateReason === "completed" && isPrincipal(e.facts.closedByActor, principalAllowlist);
1998
+ if (handClosed)
1999
+ continue;
1988
2000
  failures.push({
1989
2001
  issue: e.task.issue,
1990
2002
  tranche: e.trancheSlug,
@@ -2674,9 +2686,13 @@ function parseGlossaryTerms(glossaryContent) {
2674
2686
  return terms;
2675
2687
  }
2676
2688
  // ../../packages/aeg-core/src/dispatch-gate.ts
2689
+ function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
2690
+ return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);
2691
+ }
2677
2692
  function checkDispatchReadiness(input) {
2678
2693
  const { trancheSlug, task } = input;
2679
2694
  const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
2695
+ const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
2680
2696
  const blockers = [];
2681
2697
  if (task.issue === null) {
2682
2698
  blockers.push(`dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
@@ -2687,7 +2703,7 @@ function checkDispatchReadiness(input) {
2687
2703
  blockers.push(`dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
2688
2704
  }
2689
2705
  for (const dep of input.dependsOn) {
2690
- if (!dep.merged) {
2706
+ if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
2691
2707
  const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
2692
2708
  blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
2693
2709
  }
@@ -1215,7 +1215,8 @@ function mapForgeFacts(raw) {
1215
1215
  reviewDecision: mapReviewDecision(raw.pullRequest?.reviewDecision),
1216
1216
  stateReason: mapStateReason(raw.issue.stateReason),
1217
1217
  closedAt: raw.issue.closedAt ?? null,
1218
- mergedAt: raw.pullRequest?.mergedAt ?? null
1218
+ mergedAt: raw.pullRequest?.mergedAt ?? null,
1219
+ closedByActor: raw.closedByActor ?? null
1219
1220
  };
1220
1221
  }
1221
1222
  function mapStateReason(reason) {
@@ -1308,6 +1309,7 @@ function buildBatchQuery(tranche, tasks) {
1308
1309
  timelineItems(last: 1, itemTypes: [CLOSED_EVENT]) {
1309
1310
  nodes {
1310
1311
  ... on ClosedEvent {
1312
+ actor { login }
1311
1313
  closer {
1312
1314
  ... on PullRequest {
1313
1315
  number
@@ -1345,7 +1347,8 @@ function extractRawFromResponse(repository, alias) {
1345
1347
  const issue = repository[`${alias}_issue`];
1346
1348
  const ref = repository[`${alias}_ref`];
1347
1349
  const prs = repository[`${alias}_prs`];
1348
- const rawCloser = issue?.timelineItems?.nodes?.[0]?.closer ?? null;
1350
+ const timelineNode = issue?.timelineItems?.nodes?.[0];
1351
+ const rawCloser = timelineNode?.closer ?? null;
1349
1352
  const closingPr = rawCloser && typeof rawCloser.number === "number" && typeof rawCloser.url === "string" ? rawCloser : null;
1350
1353
  const branchPr = prs && prs.nodes.length > 0 && prs.nodes[0] ? prs.nodes[0] : null;
1351
1354
  return {
@@ -1357,7 +1360,8 @@ function extractRawFromResponse(repository, alias) {
1357
1360
  labels: issue.labels.nodes.map((n) => n.name)
1358
1361
  } : null,
1359
1362
  refExists: Boolean(ref && ref.name.length > 0),
1360
- pullRequest: closingPr ?? branchPr
1363
+ pullRequest: closingPr ?? branchPr,
1364
+ closedByActor: timelineNode?.actor?.login ?? null
1361
1365
  };
1362
1366
  }
1363
1367
  function describeError(err) {
@@ -1495,6 +1499,11 @@ var FORGE_FACT_INPUTS = [
1495
1499
  fact: "mergedAt",
1496
1500
  readsFrom: "PullRequest.mergedAt",
1497
1501
  meaning: "Timestamp for the coherence oracle grandfather cutoff. No derivation rule reads it."
1502
+ },
1503
+ {
1504
+ fact: "closedByActor",
1505
+ readsFrom: "ClosedEvent.actor.login (most recent timeline CLOSED_EVENT)",
1506
+ meaning: "GitHub login that hand-closed the Issue. No task-status derivation rule reads it — it is consumed by dispatch-gate.ts/coherence-checks.ts A1 to recognize a hand-closed dependency as resolved (task vinaya-engine-v1 21, #99), not by this status model."
1498
1507
  }
1499
1508
  ];
1500
1509
  var DERIVED_STATUSES = [
@@ -1974,7 +1983,7 @@ function isGrandfathered(isoDate) {
1974
1983
  return isoDate.slice(0, 10) < COHERENCE_ENFORCED_FROM;
1975
1984
  }
1976
1985
  var R1_GRANDFATHERED_ISSUES = new Set([279, 280, 281, 282]);
1977
- function checkA1(entries) {
1986
+ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
1978
1987
  const failures = [];
1979
1988
  for (const e of entries) {
1980
1989
  if (!e.facts)
@@ -1982,6 +1991,9 @@ function checkA1(entries) {
1982
1991
  if (e.facts.stateReason === "not_planned")
1983
1992
  continue;
1984
1993
  if (e.facts.issueState === "closed" && e.facts.prState !== "merged") {
1994
+ const handClosed = e.facts.stateReason === "completed" && isPrincipal(e.facts.closedByActor, principalAllowlist);
1995
+ if (handClosed)
1996
+ continue;
1985
1997
  failures.push({
1986
1998
  issue: e.task.issue,
1987
1999
  tranche: e.trancheSlug,
@@ -2671,9 +2683,13 @@ function parseGlossaryTerms(glossaryContent) {
2671
2683
  return terms;
2672
2684
  }
2673
2685
  // ../../packages/aeg-core/src/dispatch-gate.ts
2686
+ function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
2687
+ return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);
2688
+ }
2674
2689
  function checkDispatchReadiness(input) {
2675
2690
  const { trancheSlug, task } = input;
2676
2691
  const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
2692
+ const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
2677
2693
  const blockers = [];
2678
2694
  if (task.issue === null) {
2679
2695
  blockers.push(`dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
@@ -2684,7 +2700,7 @@ function checkDispatchReadiness(input) {
2684
2700
  blockers.push(`dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
2685
2701
  }
2686
2702
  for (const dep of input.dependsOn) {
2687
- if (!dep.merged) {
2703
+ if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
2688
2704
  const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
2689
2705
  blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
2690
2706
  }
package/dist/index.js CHANGED
@@ -424,6 +424,11 @@ var FORGE_FACT_INPUTS = [
424
424
  fact: "mergedAt",
425
425
  readsFrom: "PullRequest.mergedAt",
426
426
  meaning: "Timestamp for the coherence oracle grandfather cutoff. No derivation rule reads it."
427
+ },
428
+ {
429
+ fact: "closedByActor",
430
+ readsFrom: "ClosedEvent.actor.login (most recent timeline CLOSED_EVENT)",
431
+ meaning: "GitHub login that hand-closed the Issue. No task-status derivation rule reads it — it is consumed by dispatch-gate.ts/coherence-checks.ts A1 to recognize a hand-closed dependency as resolved (task vinaya-engine-v1 21, #99), not by this status model."
427
432
  }
428
433
  ];
429
434
  var DERIVED_STATUSES = [
@@ -2965,8 +2970,7 @@ function coreCheckRegistry() {
2965
2970
  BASE_SHA: { optional: true },
2966
2971
  PR_BODY: { optional: true },
2967
2972
  PR_BODY_FILE: { optional: true },
2968
- PR_LABELS: { optional: true },
2969
- WAIVER_LABEL_ACTOR: { optional: true },
2973
+ PR_NUMBER: { optional: true },
2970
2974
  GITHUB_REPOSITORY: { optional: true },
2971
2975
  GITHUB_TOKEN: { optional: true },
2972
2976
  GH_TOKEN: { optional: true }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attalabs/vinaya",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "Vinaya — Agentic Engineering Harness. Deterministic checks every AI coding agent must satisfy before merge.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -50,10 +50,10 @@
50
50
  "zod": "3.25.76"
51
51
  },
52
52
  "devDependencies": {
53
- "@atta/aeg-core": "workspace:*",
54
- "@atta/aeg-forge-state": "workspace:*",
53
+ "@attalabs/aeg-core": "workspace:*",
54
+ "@attalabs/aeg-forge-state": "workspace:*",
55
55
  "@atta/typescript-config": "workspace:*",
56
- "@atta/vinaya-sources": "workspace:*",
56
+ "@attalabs/vinaya-sources": "workspace:*",
57
57
  "@types/bun": "latest",
58
58
  "typescript": "^5.5.0"
59
59
  }