@fro.bot/systematic 3.13.7 → 3.14.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/dist/cli.js CHANGED
@@ -1991,7 +1991,8 @@ var REVIEW_ARTIFACT_CUSTOM_MESSAGES = [
1991
1991
  "filtered findings require a validation reason",
1992
1992
  "risk-critical dispatches require a non-empty selection surface",
1993
1993
  "satisfied risk coverage requires a citing input finding ID",
1994
- "unsatisfied risk coverage must not cite an input finding ID"
1994
+ "unsatisfied risk coverage must not cite an input finding ID",
1995
+ "passed validation must not include a reason; non-passed validation requires a reason"
1995
1996
  ];
1996
1997
  var boundedText = (maxLength) => exports_external.string().min(1).max(maxLength).regex(/\S/);
1997
1998
  var DispatchOutcomeSchema = exports_external.enum([
@@ -2142,6 +2143,30 @@ var DeclinedMergeSchema = exports_external.object({
2142
2143
  input_finding_ids: exports_external.array(boundedText(MAX_INPUT_ID_LENGTH)).min(2).max(MAX_FINDINGS),
2143
2144
  reason: ReasonSchema
2144
2145
  }).strict();
2146
+ var ValidationSchema = exports_external.object({
2147
+ status: exports_external.enum([
2148
+ "passed",
2149
+ "failed",
2150
+ "unavailable",
2151
+ "not_attempted"
2152
+ ]),
2153
+ reason: ReasonSchema.optional()
2154
+ }).strict().superRefine((validation, ctx) => {
2155
+ if (validation.status === "passed" && validation.reason !== undefined) {
2156
+ ctx.addIssue({
2157
+ code: "custom",
2158
+ path: ["reason"],
2159
+ message: REVIEW_ARTIFACT_CUSTOM_MESSAGES[5]
2160
+ });
2161
+ }
2162
+ if (validation.status !== "passed" && validation.reason === undefined) {
2163
+ ctx.addIssue({
2164
+ code: "custom",
2165
+ path: ["reason"],
2166
+ message: REVIEW_ARTIFACT_CUSTOM_MESSAGES[5]
2167
+ });
2168
+ }
2169
+ });
2145
2170
  var RiskCoverageSchema = exports_external.object({
2146
2171
  persona: RiskCriticalPersonaSchema,
2147
2172
  satisfied: exports_external.boolean(),
@@ -2186,7 +2211,8 @@ var ReviewArtifactSchema = exports_external.object({
2186
2211
  applied_fixes: exports_external.array(ReasonSchema).max(MAX_FINDINGS),
2187
2212
  residual_actionable_work: exports_external.array(ReasonSchema).max(MAX_FINDINGS),
2188
2213
  advisory_outputs: exports_external.array(ReasonSchema).max(MAX_FINDINGS),
2189
- coverage: CoverageSchema
2214
+ coverage: CoverageSchema,
2215
+ validation: ValidationSchema.optional()
2190
2216
  }).strict();
2191
2217
 
2192
2218
  // src/lib/setup.ts
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- export declare const REVIEW_ARTIFACT_CUSTOM_MESSAGES: readonly ['severity count must match rejected finding count', 'filtered findings require a validation reason', 'risk-critical dispatches require a non-empty selection surface', 'satisfied risk coverage requires a citing input finding ID', 'unsatisfied risk coverage must not cite an input finding ID'];
2
+ export declare const REVIEW_ARTIFACT_CUSTOM_MESSAGES: readonly ['severity count must match rejected finding count', 'filtered findings require a validation reason', 'risk-critical dispatches require a non-empty selection surface', 'satisfied risk coverage requires a citing input finding ID', 'unsatisfied risk coverage must not cite an input finding ID', 'passed validation must not include a reason; non-passed validation requires a reason'];
3
3
  export declare const DispatchOutcomeSchema: z.ZodEnum<{
4
4
  empty: "empty";
5
5
  findings: "findings";
@@ -255,6 +255,15 @@ export declare const ReviewArtifactSchema: z.ZodObject<{
255
255
  validator_failures: z.ZodArray<z.ZodString>;
256
256
  intent_uncertainty: z.ZodArray<z.ZodString>;
257
257
  }, z.core.$strict>;
258
+ validation: z.ZodOptional<z.ZodObject<{
259
+ status: z.ZodEnum<{
260
+ failed: "failed";
261
+ not_attempted: "not_attempted";
262
+ passed: "passed";
263
+ unavailable: "unavailable";
264
+ }>;
265
+ reason: z.ZodOptional<z.ZodString>;
266
+ }, z.core.$strict>>;
258
267
  }, z.core.$strict>;
259
268
  export type ReviewArtifact = z.infer<typeof ReviewArtifactSchema>;
260
269
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fro.bot/systematic",
3
- "version": "3.13.7",
3
+ "version": "3.14.0",
4
4
  "description": "Compound-engineering loops for OpenCode, Pi, and Claude Code",
5
5
  "type": "module",
6
6
  "homepage": "https://fro.bot/systematic",
@@ -606,6 +606,23 @@
606
606
  "intent_uncertainty"
607
607
  ],
608
608
  "additionalProperties": false
609
+ },
610
+ "validation": {
611
+ "type": "object",
612
+ "properties": {
613
+ "status": {
614
+ "type": "string",
615
+ "enum": ["passed", "failed", "unavailable", "not_attempted"]
616
+ },
617
+ "reason": {
618
+ "type": "string",
619
+ "minLength": 1,
620
+ "maxLength": 2048,
621
+ "pattern": "\\S"
622
+ }
623
+ },
624
+ "required": ["status"],
625
+ "additionalProperties": false
609
626
  }
610
627
  },
611
628
  "required": [
@@ -216,12 +216,18 @@ When it is available, the parent runs
216
216
  `systematic validate-review-artifact <path>` against it. The executable ships
217
217
  through the npm package's `bin` entry; a harness that installs bundled
218
218
  markdown without that package will not have it. When it is unavailable, the
219
- parent records that validation did not run and why in the run record.
220
- Unavailable validation is distinct from skipped validation, and the parent
221
- does not represent the artifact as validated. A nonzero exit means the run is
222
- not complete. The [executable schema](./review-summary-schema.json) is
223
- generated from a Zod source and is the machine-checkable form of the shape
224
- described here.
219
+ parent records `validation.status: "unavailable"` and a `validation.reason` in
220
+ the run record. When the executable is available and the parent does not run
221
+ it, that is `validation.status: "not_attempted"`, also with a reason. The
222
+ `validation.status` values are `passed`, `failed`, `unavailable`, and
223
+ `not_attempted`; `validation.reason` is required for every status except
224
+ `passed`, where it is forbidden.
225
+
226
+ Unavailable validation is distinct from skipped validation — `unavailable`
227
+ versus `not_attempted` — and in neither case does the parent represent the
228
+ artifact as validated. A nonzero exit means the run is not complete. The
229
+ [executable schema](./review-summary-schema.json) is generated from a Zod
230
+ source and is the machine-checkable form of the shape described here.
225
231
 
226
232
  On validation failure, the parent repairs the artifact and re-runs the
227
233
  validator. It does not report a verdict over an artifact that failed
@@ -235,6 +241,11 @@ in either direction. That is why the command exists as an independently
235
241
  runnable check rather than as a self-validation instruction, and why its result
236
242
  belongs in the run record. An unavailable validator is recorded as unavailable,
237
243
  not as skipped or validated.
244
+ The `validation` field is self-reported: an agent can write `status: passed`
245
+ without running anything, so it is a claim rather than evidence. Its value is
246
+ that it distinguishes states that were previously one indistinguishable
247
+ silence: a check that passed, one that failed, one that could not run, and one
248
+ that was skipped.
238
249
 
239
250
  `mode:report-only` writes no artifact and therefore performs no validation.
240
251