create-agent-rig 0.6.2 → 0.7.1

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +180 -0
  2. package/package.json +1 -1
  3. package/templates/agent-os/stack/node-ts/.claude/rules/node-ts.md +2 -3
  4. package/templates/agent-os/universal/.agents/skills/loop/SKILL.md +117 -80
  5. package/templates/agent-os/universal/.agents/skills/pr-ship/SKILL.md +84 -16
  6. package/templates/agent-os/universal/.claude/hooks/block-no-verify.mjs +23 -3
  7. package/templates/agent-os/universal/.claude/hooks/guard-bash.mjs +65 -7
  8. package/templates/agent-os/universal/.claude/hooks/guard-core-purity.mjs +2 -2
  9. package/templates/agent-os/universal/.claude/hooks/guard-web-boundary.mjs +2 -2
  10. package/templates/agent-os/universal/.claude/hooks/lib/hook-input.mjs +109 -0
  11. package/templates/agent-os/universal/.claude/rules/invariants.md +19 -0
  12. package/templates/agent-os/universal/.claude/scripts/lib/claim-records.mjs +800 -0
  13. package/templates/agent-os/universal/.claude/scripts/lib/revalidation-evidence.mjs +56 -0
  14. package/templates/agent-os/universal/.claude/scripts/lib/shell-tools.mjs +81 -0
  15. package/templates/agent-os/universal/.claude/scripts/preflight.mjs +19 -1
  16. package/templates/agent-os/universal/.claude/scripts/queue/core.mjs +17 -66
  17. package/templates/agent-os/universal/.claude/scripts/queue/github-issues.mjs +29 -7
  18. package/templates/agent-os/universal/.claude/scripts/queue/index.mjs +159 -23
  19. package/templates/agent-os/universal/.claude/scripts/queue/jira.mjs +41 -19
  20. package/templates/agent-os/universal/.claude/scripts/queue/plan-md.mjs +4 -2
  21. package/templates/agent-os/universal/.claude/scripts/revalidate.mjs +640 -59
  22. package/templates/agent-os/universal/.claude/scripts/revalidation-report.mjs +32 -15
  23. package/templates/agent-os/universal/.claude/scripts/run-state.mjs +180 -37
  24. package/templates/agent-os/universal/.claude/settings.json +1 -1
  25. package/templates/agent-os/universal/.claude/skills/loop/SKILL.md +117 -80
  26. package/templates/agent-os/universal/.claude/skills/pr-ship/SKILL.md +84 -16
  27. package/templates/agent-os/universal/.codex/hooks.json +1 -1
  28. package/templates/agent-os/universal/.rig/revalidation.json +10 -0
  29. package/templates/agent-os/universal/docs/decisions/codex-adapter.md +3 -2
  30. package/templates/agent-os/universal/docs/decisions/content-blind-revalidation.md +144 -0
  31. package/templates/agent-os/universal/layers.json +5 -0
  32. package/templates/hash-history.json +104 -29
  33. package/templates/release-ledger.json +3 -1
@@ -26,8 +26,10 @@
26
26
  import { duplicateOf, fingerprintOf, validateProposal, ownerOfLabels, lifecycleOf } from './core.mjs';
27
27
  import { withAsOf } from './as-of.mjs';
28
28
  import { recordEscalation, recordTakeUp } from '../run-state.mjs';
29
+ import { recordClaimTransition } from '../lib/claim-records.mjs';
29
30
 
30
31
  export const name = 'jira';
32
+ export const claimedState = 'in-progress';
31
33
 
32
34
  /** Jira's own default priority ladder. An unrecognised name sorts last, never first. */
33
35
  const PRIORITY = { highest: 1, high: 2, medium: 3, low: 4, lowest: 5 };
@@ -76,6 +78,14 @@ export const toTicket = (issue) => {
76
78
  const labels = fields.labels ?? [];
77
79
  const links = fields.issuelinks ?? [];
78
80
  const category = statusCategory(fields);
81
+ const comments = Array.isArray(fields.comment?.comments) ? fields.comment.comments : [];
82
+ const commentaryIds = comments
83
+ .map((comment) => comment?.id)
84
+ .filter((id) => id !== undefined && id !== null)
85
+ .map(String);
86
+ const commentaryCount = Number.isInteger(fields.comment?.total)
87
+ ? fields.comment.total
88
+ : comments.length;
79
89
 
80
90
  // 🔴 INVARIANT 1: the dependency is the LINK, and the blocker's own status
81
91
  // decides. A `blocked` label is a snapshot nobody updates when the blocker
@@ -116,16 +126,22 @@ export const toTicket = (issue) => {
116
126
  ? Number(fields.priority?.id)
117
127
  : 999),
118
128
  createdAt: toIso(fields.created),
119
- // The take-up marker for revalidation at SELECT (`core.mjs` › revalidationOf):
120
- // the tracker's own last-modified field. That it moves on every status
121
- // change, edit and comment is Jira's contract, assumed and not checked
122
- // here. `null` when the search did not carry it — never `''`, which would
123
- // compare equal to itself and read as "unchanged" where the truth is "not
124
- // looked".
129
+ // Compatibility evidence only: Jira's last-modified field is retained in
130
+ // `takeUps`, but content-blind claim fingerprints decide drift.
125
131
  updatedAt: toIso(fields.updated),
126
132
  // Flattened from the document description — the same text this adapter
127
133
  // already reads internally, now visible to the shared hygiene checks.
128
134
  body: descriptionTextOf(issue) || null,
135
+ commentary: {
136
+ count: commentaryCount,
137
+ ids: commentaryIds,
138
+ // Jira may return only the first page while still declaring the total.
139
+ // A partial set cannot truthfully fingerprint commentary; the shared
140
+ // claim resolver turns this explicit false into UNVERIFIABLE.
141
+ complete:
142
+ commentaryIds.length === commentaryCount &&
143
+ new Set(commentaryIds).size === commentaryIds.length,
144
+ },
129
145
  triage: labels.includes('triage'),
130
146
  trigger: labels.includes('trigger-auto')
131
147
  ? 'auto'
@@ -374,6 +390,7 @@ const FIELDS = [
374
390
  'updated',
375
391
  'issuelinks',
376
392
  'description',
393
+ 'comment',
377
394
  ];
378
395
 
379
396
  // --- the adapter contract ------------------------------------------------------
@@ -529,17 +546,9 @@ export const resolveBlockers = (ticket) => (ticket.blockedBy ?? []).filter((b) =
529
546
  /**
530
547
  * Re-record the item's marker after a write of this adapter's own (AR-140).
531
548
  *
532
- * Every write here a claim, a comment, a close, an escalation moves the
533
- * tracker's `updated`, and the next revalidation compared against the take-up
534
- * from before it the generator's journal records one run whose every
535
- * BEFORE_PR catch was a hold on its own comment (`revalidation-report.mjs`
536
- * over that run). So the marker is read back after the write
537
- * and recorded as the take-up in the declared run; a hold that still fires is
538
- * a move by something other than this adapter.
539
- *
540
- * ⚠ Limit: only writes made THROUGH this adapter re-baseline. A comment the
541
- * session posts by another route — a REST call by hand, a connector — moves
542
- * the marker like anyone else's, and the next check holds on it.
549
+ * Every write here moves Jira's `updated`, so it is read back and retained for
550
+ * attribution and compatibility. It does not re-baseline `.rig/claims/` and
551
+ * cannot produce or clear a drift decision.
543
552
  *
544
553
  * Best-effort, like `proposeTriage`'s baseline: the write has landed by now,
545
554
  * and a read-back the tracker refused or a stale run directory is announced on
@@ -572,7 +581,10 @@ const rebaseline = async (ticket, env) => {
572
581
  recordMarker(ticket, updatedAt, env);
573
582
  };
574
583
 
575
- export const claim = async (ticket, { transitionId = null, env = process.env } = {}) => {
584
+ export const claim = async (
585
+ ticket,
586
+ { transitionId = null, env = process.env, projectRoot = process.cwd() } = {},
587
+ ) => {
576
588
  if (!transitionId) {
577
589
  const available = await request(`/rest/api/3/issue/${ticket.id}/transitions`, { env });
578
590
  const target = available.transitions.find(
@@ -591,8 +603,18 @@ export const claim = async (ticket, { transitionId = null, env = process.env } =
591
603
  body: { transition: { id: transitionId } },
592
604
  env,
593
605
  });
606
+ let workflowClaimRecorded = false;
607
+ try {
608
+ workflowClaimRecorded =
609
+ recordClaimTransition({ projectRoot, ticket, claimedState }) !== null;
610
+ } catch (error) {
611
+ process.stderr.write(
612
+ `${ticket.id}: the workflow claim landed, but its durable acknowledgement was NOT recorded — ` +
613
+ `${error.message}\n`,
614
+ );
615
+ }
594
616
  await rebaseline(ticket, env);
595
- return { ok: true };
617
+ return { ok: true, workflowClaimRecorded };
596
618
  };
597
619
 
598
620
  export const comment = async (ticket, body, { env = process.env } = {}) => {
@@ -17,6 +17,7 @@ import { withAsOf } from './as-of.mjs';
17
17
  import { recordEscalation } from '../run-state.mjs';
18
18
 
19
19
  export const name = 'plan-md';
20
+ export const claimedState = 'open';
20
21
 
21
22
  const AGENT_QUEUE = /^##\s+Agent queue\s*$/i;
22
23
  const OPERATOR_QUEUE = /^##\s+Operator queue\s*$/i;
@@ -129,9 +130,10 @@ export const parsePlan = (plan) => {
129
130
  blocks: [],
130
131
  priority: items.length,
131
132
  createdAt: null,
132
- // A flat list carries no marker at all, so revalidation at SELECT records
133
- // `changed: null` for it — a blind spot, never an "unchanged".
133
+ // A flat list carries no compatibility marker; claim fingerprints still
134
+ // provide the authoritative revalidation baseline.
134
135
  updatedAt: null,
136
+ commentary: { count: 0, ids: [] },
135
137
  triage: MARKERS.triage.test(raw),
136
138
  trigger: MARKERS.triggerAuto.test(raw)
137
139
  ? 'auto'