@farmslot/agent-runtime 0.27.0 → 0.28.0

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 CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  - Active-development baseline; add user-facing changes here before release or package publication.
6
6
 
7
+ ## 0.28.0 - 2026-10-16
8
+
9
+ - Accept qualified parent proof references during PR-follow-up closeout while preserving explicit fresh-proof requirements.
10
+
7
11
  ## 0.27.0 - 2026-10-16
8
12
 
9
13
  - Capture validated native review results on the completed command without treating streamed text as proof.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farmslot/agent-runtime",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -60,8 +60,8 @@
60
60
  "test:branch-maintenance-live": "node --import tsx --test test/branch-maintenance-live.mts"
61
61
  },
62
62
  "dependencies": {
63
- "@farmslot/adapter-sdk": "0.17.0",
64
- "@farmslot/protocol": "0.52.0"
63
+ "@farmslot/adapter-sdk": "0.17.1",
64
+ "@farmslot/protocol": "0.53.0"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/node": "^22.0.0",
@@ -16,10 +16,13 @@ const { atomicWrite, readJson } = require('./mark-io.cjs');
16
16
  const HANDOFF_INPUT = path.join('inputs', 'handoff.json');
17
17
  const {
18
18
  ACCEPTANCE_STATUS_ARTIFACT,
19
+ INHERITED_ACCEPTANCE_INPUT,
19
20
  ACCEPTANCE_VERDICTS,
20
21
  ACCEPTANCE_PROOF_MODES,
21
22
  ACCEPTANCE_CRITERION_ID_PATTERN,
22
23
  acceptanceCriterionId,
24
+ acceptanceCriteriaMatch,
25
+ registeredAcceptanceCriteria,
23
26
  validateAcceptanceStatusLedger,
24
27
  acceptanceCriteriaView,
25
28
  summarizeAcceptanceStatus,
@@ -59,9 +62,7 @@ function handoffAcceptanceCriteria(taskDir) {
59
62
  function criteriaFromHandoff(handoff) {
60
63
  const task = isRecord(handoff.task) ? handoff.task : {};
61
64
  const criteria = Array.isArray(task.acceptanceCriteria) ? task.acceptanceCriteria : [];
62
- return criteria
63
- .map((text, index) => ({ id: acceptanceCriterionId(index), text: String(text) }))
64
- .filter((criterion) => criterion.text.trim().length > 0);
65
+ return registeredAcceptanceCriteria(criteria);
65
66
  }
66
67
 
67
68
  /**
@@ -198,6 +199,40 @@ function acceptanceContractIssues(taskDir, options = {}) {
198
199
  return [err.message];
199
200
  }
200
201
  if (criteria.length === 0) return [];
202
+ // This is a cached qualification, not a new verdict or publication authority.
203
+ // The gateway independently checks the approved original parent at completion.
204
+ if (!options.requireFresh && fs.existsSync(path.join(taskDir, INHERITED_ACCEPTANCE_INPUT))) {
205
+ let reference;
206
+ let handoff;
207
+ try {
208
+ reference = readJson(path.join(taskDir, INHERITED_ACCEPTANCE_INPUT));
209
+ handoff = readJson(path.join(taskDir, HANDOFF_INPUT));
210
+ } catch (error) {
211
+ return [`Inherited acceptance reference cannot be read: ${error.message}`];
212
+ }
213
+ if (
214
+ !isRecord(reference) ||
215
+ reference.schemaVersion !== 1 ||
216
+ reference.source !== 'parent-run' ||
217
+ typeof reference.childRunId !== 'string' ||
218
+ !reference.childRunId ||
219
+ typeof reference.project !== 'string' ||
220
+ !reference.project ||
221
+ handoff.flow !== 'pr-complete' ||
222
+ reference.childRunId !== handoff.attemptId ||
223
+ reference.project !== handoff.project ||
224
+ typeof reference.parentRunId !== 'string' ||
225
+ !reference.parentRunId ||
226
+ reference.parentRunId === reference.childRunId ||
227
+ typeof reference.headSha !== 'string' ||
228
+ !/^[a-f0-9]{7,40}$/i.test(reference.headSha) ||
229
+ typeof reference.ledgerHash !== 'string' ||
230
+ !/^[a-f0-9]{64}$/i.test(reference.ledgerHash) ||
231
+ !acceptanceCriteriaMatch(criteria, reference.criteria)
232
+ )
233
+ return ['Inherited acceptance reference does not match this follow-up task.'];
234
+ return [];
235
+ }
201
236
  let stored;
202
237
  try {
203
238
  stored = readAcceptanceLedger(taskDir);
@@ -758,6 +758,7 @@ if (
758
758
  }
759
759
  for (const issue of acceptanceContractIssues(taskDir, {
760
760
  allowWeak: terminalContract?.acceptance?.allowWeak === true,
761
+ requireFresh: terminalContract?.acceptance?.requireFresh === true,
761
762
  deferClaimVerdicts,
762
763
  })) {
763
764
  issues.push(issue);
@@ -33,6 +33,7 @@ const { atomicWrite } = require('./mark-io.cjs');
33
33
  * @typedef {object} WorkerTerminalAcceptanceRules
34
34
  * @property {boolean} [require]
35
35
  * @property {boolean} [allowWeak]
36
+ * @property {boolean} [requireFresh]
36
37
  */
37
38
 
38
39
  /**
@@ -265,6 +266,7 @@ function resolveWorkerTerminalContract(projectConfig, flowType, options = {}) {
265
266
  ? {
266
267
  ...(acceptanceRules.require ? { require: true } : {}),
267
268
  ...(acceptanceRules.allowWeak ? { allowWeak: true } : {}),
269
+ ...(acceptanceRules.requireFresh ? { requireFresh: true } : {}),
268
270
  }
269
271
  : null;
270
272