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.
- package/CHANGELOG.md +180 -0
- package/package.json +1 -1
- package/templates/agent-os/stack/node-ts/.claude/rules/node-ts.md +2 -3
- package/templates/agent-os/universal/.agents/skills/loop/SKILL.md +117 -80
- package/templates/agent-os/universal/.agents/skills/pr-ship/SKILL.md +84 -16
- package/templates/agent-os/universal/.claude/hooks/block-no-verify.mjs +23 -3
- package/templates/agent-os/universal/.claude/hooks/guard-bash.mjs +65 -7
- package/templates/agent-os/universal/.claude/hooks/guard-core-purity.mjs +2 -2
- package/templates/agent-os/universal/.claude/hooks/guard-web-boundary.mjs +2 -2
- package/templates/agent-os/universal/.claude/hooks/lib/hook-input.mjs +109 -0
- package/templates/agent-os/universal/.claude/rules/invariants.md +19 -0
- package/templates/agent-os/universal/.claude/scripts/lib/claim-records.mjs +800 -0
- package/templates/agent-os/universal/.claude/scripts/lib/revalidation-evidence.mjs +56 -0
- package/templates/agent-os/universal/.claude/scripts/lib/shell-tools.mjs +81 -0
- package/templates/agent-os/universal/.claude/scripts/preflight.mjs +19 -1
- package/templates/agent-os/universal/.claude/scripts/queue/core.mjs +17 -66
- package/templates/agent-os/universal/.claude/scripts/queue/github-issues.mjs +29 -7
- package/templates/agent-os/universal/.claude/scripts/queue/index.mjs +159 -23
- package/templates/agent-os/universal/.claude/scripts/queue/jira.mjs +41 -19
- package/templates/agent-os/universal/.claude/scripts/queue/plan-md.mjs +4 -2
- package/templates/agent-os/universal/.claude/scripts/revalidate.mjs +640 -59
- package/templates/agent-os/universal/.claude/scripts/revalidation-report.mjs +32 -15
- package/templates/agent-os/universal/.claude/scripts/run-state.mjs +180 -37
- package/templates/agent-os/universal/.claude/settings.json +1 -1
- package/templates/agent-os/universal/.claude/skills/loop/SKILL.md +117 -80
- package/templates/agent-os/universal/.claude/skills/pr-ship/SKILL.md +84 -16
- package/templates/agent-os/universal/.codex/hooks.json +1 -1
- package/templates/agent-os/universal/.rig/revalidation.json +10 -0
- package/templates/agent-os/universal/docs/decisions/codex-adapter.md +3 -2
- package/templates/agent-os/universal/docs/decisions/content-blind-revalidation.md +144 -0
- package/templates/agent-os/universal/layers.json +5 -0
- package/templates/hash-history.json +104 -29
- 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
|
-
//
|
|
120
|
-
//
|
|
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
|
|
533
|
-
*
|
|
534
|
-
*
|
|
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 (
|
|
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
|
|
133
|
-
//
|
|
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'
|