@fro.bot/systematic 3.16.1 → 3.16.3

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
@@ -33,7 +33,7 @@ import {
33
33
  findCommandsInDir,
34
34
  findSkillsInDir,
35
35
  discoverSkills
36
- } from "./index-ae6mhwth.js";
36
+ } from "./index-65g87tgr.js";
37
37
  var __defProp = Object.defineProperty;
38
38
  var __returnValue = (v) => v;
39
39
  function __exportSetter(name, newValue) {
@@ -2085,6 +2085,23 @@ function isLegacyReviewArtifact(value) {
2085
2085
  function hasParentDirectoryTraversal(input) {
2086
2086
  return input.split(/[\\/]+/).some((segment) => segment === "..");
2087
2087
  }
2088
+ var ALLOW_OUTSIDE_ARTIFACT_ROOT_FLAG = "--allow-outside-artifact-root";
2089
+ var VALIDATE_REVIEW_ARTIFACT_USAGE = "Usage: systematic validate-review-artifact <path> [--allow-outside-artifact-root]";
2090
+ function parseValidateReviewArtifactArguments(argv) {
2091
+ const allowOutsideArtifactRoot = argv.includes(ALLOW_OUTSIDE_ARTIFACT_ROOT_FLAG);
2092
+ const positional = argv.filter((arg) => arg !== ALLOW_OUTSIDE_ARTIFACT_ROOT_FLAG);
2093
+ if (positional.length !== 1)
2094
+ return;
2095
+ const path = positional[0];
2096
+ if (path === undefined)
2097
+ return;
2098
+ return { allowOutsideArtifactRoot, path };
2099
+ }
2100
+ var REVIEW_ARTIFACT_VALID_MESSAGE = "Review artifact is valid";
2101
+ var REVIEW_ARTIFACT_VALID_EXTERNAL_MESSAGE = "Review artifact is valid (validated outside the run directory; not evidence for this run)";
2102
+ function formatReviewArtifactSuccessMessage(allowOutsideArtifactRoot) {
2103
+ return allowOutsideArtifactRoot ? REVIEW_ARTIFACT_VALID_EXTERNAL_MESSAGE : REVIEW_ARTIFACT_VALID_MESSAGE;
2104
+ }
2088
2105
  function pathContainsSymlink(candidate) {
2089
2106
  let current = path3.parse(candidate).root;
2090
2107
  const relative = path3.relative(current, candidate);
@@ -2105,30 +2122,33 @@ function isWithinDirectory(candidate, directory) {
2105
2122
  const relative = path3.relative(directory, candidate);
2106
2123
  return relative !== "" && relative !== ".." && !relative.startsWith(`..${path3.sep}`) && !path3.isAbsolute(relative);
2107
2124
  }
2108
- function resolveReviewArtifactPath(input, cwd) {
2125
+ function resolveReviewArtifactPath(input, cwd, options = {}) {
2126
+ const allowOutsideArtifactRoot = options.allowOutsideArtifactRoot ?? false;
2109
2127
  if (hasParentDirectoryTraversal(input)) {
2110
2128
  return {
2111
2129
  message: "Review artifact path must not contain parent-directory traversal",
2112
2130
  ok: false
2113
2131
  };
2114
2132
  }
2115
- const artifactRoot = path3.resolve(cwd, ".context", "systematic", "ce-review");
2116
2133
  let canonicalRoot;
2117
- try {
2118
- canonicalRoot = fs4.realpathSync(artifactRoot);
2119
- if (!fs4.statSync(canonicalRoot).isDirectory()) {
2120
- return {
2121
- message: "Review artifact directory is not a directory",
2122
- ok: false
2123
- };
2134
+ if (!allowOutsideArtifactRoot) {
2135
+ const artifactRoot = path3.resolve(cwd, ".context", "systematic", "ce-review");
2136
+ try {
2137
+ canonicalRoot = fs4.realpathSync(artifactRoot);
2138
+ if (!fs4.statSync(canonicalRoot).isDirectory()) {
2139
+ return {
2140
+ message: "Review artifact directory is not a directory",
2141
+ ok: false
2142
+ };
2143
+ }
2144
+ } catch {
2145
+ return { message: "Review artifact directory is unavailable", ok: false };
2124
2146
  }
2125
- } catch {
2126
- return { message: "Review artifact directory is unavailable", ok: false };
2127
2147
  }
2128
2148
  const candidate = path3.resolve(cwd, input);
2129
2149
  if (pathContainsSymlink(candidate)) {
2130
2150
  return {
2131
- message: "Review artifact path must not contain symlinks",
2151
+ message: "Review artifact path must not contain symlinks; pass a fully resolved path",
2132
2152
  ok: false
2133
2153
  };
2134
2154
  }
@@ -2138,7 +2158,7 @@ function resolveReviewArtifactPath(input, cwd) {
2138
2158
  } catch {
2139
2159
  return { message: "Review artifact file was not found", ok: false };
2140
2160
  }
2141
- if (!isWithinDirectory(canonicalTarget, canonicalRoot)) {
2161
+ if (canonicalRoot !== undefined && !isWithinDirectory(canonicalTarget, canonicalRoot)) {
2142
2162
  return {
2143
2163
  message: "Review artifact path must remain inside .context/systematic/ce-review",
2144
2164
  ok: false
@@ -2721,9 +2741,12 @@ Usage:
2721
2741
  Commands:
2722
2742
  list [type] List available skills, agents, or commands
2723
2743
  capabilities Read-only standalone-CLI observation; not a host-runtime or canonical-registry view
2724
- validate-review-artifact <path>
2744
+ validate-review-artifact <path> [--allow-outside-artifact-root]
2725
2745
  Validate a ce:review run artifact
2726
2746
  The <path> argument is required by design; no artifact discovery is performed.
2747
+ --allow-outside-artifact-root skips the requirement that <path> resolve
2748
+ inside .context/systematic/ce-review, for validating artifacts copied from
2749
+ CI, issues, or fixtures. Not for the ce:review parent's own run artifact.
2727
2750
  Exit statuses: 0 valid artifact, 1 validation failure,
2728
2751
  2 operational failure, 3 legacy artifact with no schema_version
2729
2752
  config [subcommand] Configuration management
@@ -2744,6 +2767,7 @@ Examples:
2744
2767
  systematic list skills
2745
2768
  systematic capabilities
2746
2769
  systematic validate-review-artifact .context/systematic/ce-review/review-summary.json
2770
+ systematic validate-review-artifact --allow-outside-artifact-root /path/to/external-artifact.json
2747
2771
  systematic list agents
2748
2772
  systematic config show
2749
2773
  systematic config show --json
@@ -2774,7 +2798,6 @@ Scope:
2774
2798
  project <cwd>/.pi/agents (default)
2775
2799
  global $PI_CODING_AGENT_DIR/agents or ~/.pi/agent/agents
2776
2800
  `;
2777
- var VALIDATE_REVIEW_ARTIFACT_USAGE = "Usage: systematic validate-review-artifact <path>";
2778
2801
  var REVIEW_ARTIFACT_SCHEMA_RELATIVE_PATH = "skills/ce-review/references/review-summary-schema.json";
2779
2802
  function defaultCapabilityRoots() {
2780
2803
  const configDir = process.env.XDG_CONFIG_HOME ? path5.join(process.env.XDG_CONFIG_HOME, "opencode") : path5.join(os2.homedir(), ".config/opencode");
@@ -2935,19 +2958,17 @@ function validateReviewArtifactArgument(argv) {
2935
2958
  const commandIndex = argv[0] === "systematic" ? 1 : 0;
2936
2959
  if (argv[commandIndex] !== "validate-review-artifact")
2937
2960
  return;
2938
- if (argv.length !== commandIndex + 2)
2939
- return;
2940
- return argv[commandIndex + 1];
2961
+ return parseValidateReviewArtifactArguments(argv.slice(commandIndex + 1));
2941
2962
  }
2942
2963
  function runValidateReviewArtifact(options) {
2943
2964
  const outputSink = options.outputSink ?? ((message) => console.log(message));
2944
2965
  const errorSink = options.errorSink ?? ((message) => console.error(message));
2945
- const input = validateReviewArtifactArgument(options.argv);
2946
- if (input === undefined) {
2966
+ const parsedArgs = validateReviewArtifactArgument(options.argv);
2967
+ if (parsedArgs === undefined) {
2947
2968
  errorSink(VALIDATE_REVIEW_ARTIFACT_USAGE);
2948
2969
  return 2;
2949
2970
  }
2950
- const resolved = resolveReviewArtifactPath(input, options.cwd ?? process.cwd());
2971
+ const resolved = resolveReviewArtifactPath(parsedArgs.path, options.cwd ?? process.cwd(), { allowOutsideArtifactRoot: parsedArgs.allowOutsideArtifactRoot });
2951
2972
  if (!resolved.ok) {
2952
2973
  errorSink(resolved.message);
2953
2974
  return 2;
@@ -2972,7 +2993,7 @@ function runValidateReviewArtifact(options) {
2972
2993
  errorSink(`Review artifact validation failed: ${result.error.issues.length} issue(s)`);
2973
2994
  return 1;
2974
2995
  }
2975
- outputSink("Review artifact is valid");
2996
+ outputSink(formatReviewArtifactSuccessMessage(parsedArgs.allowOutsideArtifactRoot));
2976
2997
  return 0;
2977
2998
  }
2978
2999
  function runValidateReviewArtifactCli(options) {
@@ -7539,11 +7539,11 @@ var permissionSchema = record(string(), permissionRuleSchema).meta({
7539
7539
  description: "Permission overrides per tool",
7540
7540
  examples: [{ edit: "allow", bash: { curl: "allow", rm: "deny" } }]
7541
7541
  });
7542
- var MODEL_FORMAT_MESSAGE = 'must be in provider/model format (e.g., "anthropic/claude-sonnet-4")';
7542
+ var MODEL_FORMAT_MESSAGE = 'must be in provider/model format (e.g., "anthropic/claude-sonnet-5")';
7543
7543
  var MODEL_FORMAT_REGEX = /^[^\s/]+\/\S+$/;
7544
7544
  var modelSchema = string().min(1).regex(MODEL_FORMAT_REGEX, MODEL_FORMAT_MESSAGE).nullable().meta({
7545
7545
  description: "Model identifier in provider/model format, or null to inherit parent model",
7546
- examples: ["anthropic/claude-sonnet-4", null]
7546
+ examples: ["anthropic/claude-sonnet-5", null]
7547
7547
  });
7548
7548
  var variantSchema = string().min(1).max(128, "variant must be at most 128 characters").regex(/^\S+$/, "must be a non-empty string without whitespace").meta({
7549
7549
  description: "Model variant identifier",
@@ -7618,7 +7618,7 @@ var OpencodeHarnessBlockSchema = object({
7618
7618
  }).strict().meta({
7619
7619
  description: "OpenCode-specific routing block. When present, model/variant here override the flat model/variant fields for OpenCode only; the flat fields remain the harness-neutral default and still apply to Pi. `variant` is bound to whichever layer supplies `model`: it is used only from that layer or a more specific one, and is dropped when a more specific layer sets (or nulls) `model` without repeating the variant. A `variant` with no `model` anywhere in the merged overlay is a config-load error raised by the routing resolver, not a parse-time error.",
7620
7620
  examples: [
7621
- { model: "anthropic/claude-opus-4-7", variant: "v2" },
7621
+ { model: "anthropic/claude-opus-5", variant: "high" },
7622
7622
  { model: null }
7623
7623
  ]
7624
7624
  });
@@ -7628,7 +7628,7 @@ var PiHarnessBlockSchema = object({
7628
7628
  }).strict().meta({
7629
7629
  description: "Pi-specific routing block. When present, model/thinking here override the flat model field and the legacy `pi_subagents.<name>.thinking` value for Pi only; the flat model field remains the harness-neutral default and still applies to OpenCode. Unlike OpenCode's `variant`, `thinking` is independent of `model`: it applies to whatever model the delegate ends up running, including one inherited from the parent session, so `thinking` with no `model` anywhere in the merged overlay is valid and never a config-load error.",
7630
7630
  examples: [
7631
- { model: "anthropic/claude-opus-4-7", thinking: "high" },
7631
+ { model: "anthropic/claude-opus-5", thinking: "high" },
7632
7632
  { model: null }
7633
7633
  ]
7634
7634
  });
@@ -7650,13 +7650,13 @@ var AgentOverlaySchema = object({
7650
7650
  description: "Per-agent configuration overlay",
7651
7651
  examples: [
7652
7652
  {
7653
- model: "anthropic/claude-opus-4-7",
7653
+ model: "anthropic/claude-opus-5",
7654
7654
  temperature: 0.1,
7655
7655
  mode: "subagent"
7656
7656
  },
7657
7657
  {
7658
- opencode: { model: "anthropic/claude-opus-4-7", variant: "v2" },
7659
- pi: { model: "anthropic/claude-opus-4-7", thinking: "high" }
7658
+ opencode: { model: "anthropic/claude-opus-5", variant: "high" },
7659
+ pi: { model: "anthropic/claude-opus-5", thinking: "high" }
7660
7660
  }
7661
7661
  ]
7662
7662
  });
@@ -7676,8 +7676,8 @@ var CategoryOverlaySchema = object({
7676
7676
  }).strict().meta({
7677
7677
  description: "Per-category configuration overlay (same fields as agent minus disable)",
7678
7678
  examples: [
7679
- { model: "anthropic/claude-opus-4-7", temperature: 0.1 },
7680
- { opencode: { variant: "v2" }, pi: { thinking: "high" } }
7679
+ { model: "anthropic/claude-opus-5", temperature: 0.1 },
7680
+ { opencode: { variant: "high" }, pi: { thinking: "high" } }
7681
7681
  ]
7682
7682
  });
7683
7683
  var ProfileOverlaySchema = object({
@@ -7690,8 +7690,8 @@ var ProfileOverlaySchema = object({
7690
7690
  }).strict().meta({
7691
7691
  description: "Routing-only overlay fields permitted inside a named profile bundle entry: model, variant, temperature, top_p, and the opencode/pi harness blocks. Non-routing fields (permission, skills, mode, hidden, disable, steps, color) are rejected.",
7692
7692
  examples: [
7693
- { model: "anthropic/claude-opus-4-7" },
7694
- { opencode: { variant: "v2" }, pi: { thinking: "high" } }
7693
+ { model: "anthropic/claude-opus-5" },
7694
+ { opencode: { variant: "high" }, pi: { thinking: "high" } }
7695
7695
  ]
7696
7696
  });
7697
7697
  function createProfileBundleSchema(agentNames, qualifiedAgentIds) {
@@ -7701,17 +7701,19 @@ function createProfileBundleSchema(agentNames, qualifiedAgentIds) {
7701
7701
  ProfileOverlaySchema.optional()
7702
7702
  ]))).strict().optional().meta({
7703
7703
  description: "Per-agent routing overlays for this profile, keyed by bundled agent name (bare or qualified category/name), using the same routing-only field set as every profile entry. Unknown keys are rejected with a Zod parse error, exactly like the top-level `agents` field.",
7704
- examples: [{ "correctness-reviewer": { model: "openai/gpt-5" } }]
7704
+ examples: [
7705
+ { "correctness-reviewer": { model: "openai/gpt-6-astra" } }
7706
+ ]
7705
7707
  }),
7706
7708
  categories: record(string(), ProfileOverlaySchema).optional().meta({
7707
7709
  description: "Per-category routing overlays for this profile, keyed by category name, using the same routing-only field set as every profile entry.",
7708
- examples: [{ review: { model: "anthropic/claude-opus-4-7" } }]
7710
+ examples: [{ review: { model: "anthropic/claude-opus-5" } }]
7709
7711
  })
7710
7712
  }).strict().meta({
7711
7713
  description: "A named routing-only overlay bundle. Entries under agents/categories have the same shape as the top-level agents/categories overlays, restricted to routing fields.",
7712
7714
  examples: [
7713
7715
  {
7714
- agents: { fixer: { model: "anthropic/claude-opus-4-7" } },
7716
+ agents: { fixer: { model: "anthropic/claude-opus-5" } },
7715
7717
  categories: { review: { pi: { thinking: "high" } } }
7716
7718
  }
7717
7719
  ]
@@ -7810,14 +7812,16 @@ function createSystematicConfigSchema(opts) {
7810
7812
  }),
7811
7813
  categories: record(string(), CategoryOverlaySchema).default({}).meta({
7812
7814
  description: "Per-category configuration overlays keyed by category name",
7813
- examples: [{ review: { model: "anthropic/claude-opus-4-7" } }, {}]
7815
+ examples: [{ review: { model: "anthropic/claude-opus-5" } }, {}]
7814
7816
  }),
7815
7817
  profiles: record(string(), createProfileBundleSchema(agentNames, qualifiedAgentIds)).default({}).meta({
7816
7818
  description: "Named routing-only overlay bundles, selectable by name via the profile field. Only valid in user config or OPENCODE_CONFIG_DIR config \u2014 a project config may select a profile but may not define this field.",
7817
7819
  examples: [
7818
7820
  {
7819
7821
  personal: {
7820
- agents: { "correctness-reviewer": { model: "openai/gpt-5" } }
7822
+ agents: {
7823
+ "correctness-reviewer": { model: "openai/gpt-6-astra" }
7824
+ }
7821
7825
  }
7822
7826
  },
7823
7827
  {}
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  extractCommandFrontmatter,
20
20
  findSkillsInDir,
21
21
  discoverSkills
22
- } from "./index-ae6mhwth.js";
22
+ } from "./index-65g87tgr.js";
23
23
 
24
24
  // src/index.ts
25
25
  import { createHash as createHash4 } from "crypto";
@@ -16,5 +16,16 @@ export declare function formatReviewArtifactIssuePath(issuePath: readonly Proper
16
16
  export declare function readReviewArtifact(filePath: string): ReadArtifactResult;
17
17
  export declare function isLegacyReviewArtifact(value: unknown): boolean;
18
18
  export declare function hasParentDirectoryTraversal(input: string): boolean;
19
+ export declare const ALLOW_OUTSIDE_ARTIFACT_ROOT_FLAG = "--allow-outside-artifact-root";
20
+ export declare const VALIDATE_REVIEW_ARTIFACT_USAGE = "Usage: systematic validate-review-artifact <path> [--allow-outside-artifact-root]";
21
+ export interface ValidateReviewArtifactArguments {
22
+ readonly path: string;
23
+ readonly allowOutsideArtifactRoot: boolean;
24
+ }
25
+ export declare function parseValidateReviewArtifactArguments(argv: readonly string[]): ValidateReviewArtifactArguments | undefined;
26
+ export declare function formatReviewArtifactSuccessMessage(allowOutsideArtifactRoot: boolean): string;
19
27
  export declare function pathContainsSymlink(candidate: string): boolean;
20
- export declare function resolveReviewArtifactPath(input: string, cwd: string): ArtifactPathResult;
28
+ export interface ResolveReviewArtifactPathOptions {
29
+ readonly allowOutsideArtifactRoot?: boolean;
30
+ }
31
+ export declare function resolveReviewArtifactPath(input: string, cwd: string, options?: ResolveReviewArtifactPathOptions): ArtifactPathResult;
package/dist/pi.js CHANGED
@@ -9217,11 +9217,11 @@ var permissionSchema = record(string2(), permissionRuleSchema).meta({
9217
9217
  description: "Permission overrides per tool",
9218
9218
  examples: [{ edit: "allow", bash: { curl: "allow", rm: "deny" } }]
9219
9219
  });
9220
- var MODEL_FORMAT_MESSAGE = 'must be in provider/model format (e.g., "anthropic/claude-sonnet-4")';
9220
+ var MODEL_FORMAT_MESSAGE = 'must be in provider/model format (e.g., "anthropic/claude-sonnet-5")';
9221
9221
  var MODEL_FORMAT_REGEX = /^[^\s/]+\/\S+$/;
9222
9222
  var modelSchema = string2().min(1).regex(MODEL_FORMAT_REGEX, MODEL_FORMAT_MESSAGE).nullable().meta({
9223
9223
  description: "Model identifier in provider/model format, or null to inherit parent model",
9224
- examples: ["anthropic/claude-sonnet-4", null]
9224
+ examples: ["anthropic/claude-sonnet-5", null]
9225
9225
  });
9226
9226
  var variantSchema = string2().min(1).max(128, "variant must be at most 128 characters").regex(/^\S+$/, "must be a non-empty string without whitespace").meta({
9227
9227
  description: "Model variant identifier",
@@ -9296,7 +9296,7 @@ var OpencodeHarnessBlockSchema = object({
9296
9296
  }).strict().meta({
9297
9297
  description: "OpenCode-specific routing block. When present, model/variant here override the flat model/variant fields for OpenCode only; the flat fields remain the harness-neutral default and still apply to Pi. `variant` is bound to whichever layer supplies `model`: it is used only from that layer or a more specific one, and is dropped when a more specific layer sets (or nulls) `model` without repeating the variant. A `variant` with no `model` anywhere in the merged overlay is a config-load error raised by the routing resolver, not a parse-time error.",
9298
9298
  examples: [
9299
- { model: "anthropic/claude-opus-4-7", variant: "v2" },
9299
+ { model: "anthropic/claude-opus-5", variant: "high" },
9300
9300
  { model: null }
9301
9301
  ]
9302
9302
  });
@@ -9306,7 +9306,7 @@ var PiHarnessBlockSchema = object({
9306
9306
  }).strict().meta({
9307
9307
  description: "Pi-specific routing block. When present, model/thinking here override the flat model field and the legacy `pi_subagents.<name>.thinking` value for Pi only; the flat model field remains the harness-neutral default and still applies to OpenCode. Unlike OpenCode's `variant`, `thinking` is independent of `model`: it applies to whatever model the delegate ends up running, including one inherited from the parent session, so `thinking` with no `model` anywhere in the merged overlay is valid and never a config-load error.",
9308
9308
  examples: [
9309
- { model: "anthropic/claude-opus-4-7", thinking: "high" },
9309
+ { model: "anthropic/claude-opus-5", thinking: "high" },
9310
9310
  { model: null }
9311
9311
  ]
9312
9312
  });
@@ -9328,13 +9328,13 @@ var AgentOverlaySchema = object({
9328
9328
  description: "Per-agent configuration overlay",
9329
9329
  examples: [
9330
9330
  {
9331
- model: "anthropic/claude-opus-4-7",
9331
+ model: "anthropic/claude-opus-5",
9332
9332
  temperature: 0.1,
9333
9333
  mode: "subagent"
9334
9334
  },
9335
9335
  {
9336
- opencode: { model: "anthropic/claude-opus-4-7", variant: "v2" },
9337
- pi: { model: "anthropic/claude-opus-4-7", thinking: "high" }
9336
+ opencode: { model: "anthropic/claude-opus-5", variant: "high" },
9337
+ pi: { model: "anthropic/claude-opus-5", thinking: "high" }
9338
9338
  }
9339
9339
  ]
9340
9340
  });
@@ -9354,8 +9354,8 @@ var CategoryOverlaySchema = object({
9354
9354
  }).strict().meta({
9355
9355
  description: "Per-category configuration overlay (same fields as agent minus disable)",
9356
9356
  examples: [
9357
- { model: "anthropic/claude-opus-4-7", temperature: 0.1 },
9358
- { opencode: { variant: "v2" }, pi: { thinking: "high" } }
9357
+ { model: "anthropic/claude-opus-5", temperature: 0.1 },
9358
+ { opencode: { variant: "high" }, pi: { thinking: "high" } }
9359
9359
  ]
9360
9360
  });
9361
9361
  var ProfileOverlaySchema = object({
@@ -9368,8 +9368,8 @@ var ProfileOverlaySchema = object({
9368
9368
  }).strict().meta({
9369
9369
  description: "Routing-only overlay fields permitted inside a named profile bundle entry: model, variant, temperature, top_p, and the opencode/pi harness blocks. Non-routing fields (permission, skills, mode, hidden, disable, steps, color) are rejected.",
9370
9370
  examples: [
9371
- { model: "anthropic/claude-opus-4-7" },
9372
- { opencode: { variant: "v2" }, pi: { thinking: "high" } }
9371
+ { model: "anthropic/claude-opus-5" },
9372
+ { opencode: { variant: "high" }, pi: { thinking: "high" } }
9373
9373
  ]
9374
9374
  });
9375
9375
  function createProfileBundleSchema(agentNames, qualifiedAgentIds) {
@@ -9379,17 +9379,19 @@ function createProfileBundleSchema(agentNames, qualifiedAgentIds) {
9379
9379
  ProfileOverlaySchema.optional()
9380
9380
  ]))).strict().optional().meta({
9381
9381
  description: "Per-agent routing overlays for this profile, keyed by bundled agent name (bare or qualified category/name), using the same routing-only field set as every profile entry. Unknown keys are rejected with a Zod parse error, exactly like the top-level `agents` field.",
9382
- examples: [{ "correctness-reviewer": { model: "openai/gpt-5" } }]
9382
+ examples: [
9383
+ { "correctness-reviewer": { model: "openai/gpt-6-astra" } }
9384
+ ]
9383
9385
  }),
9384
9386
  categories: record(string2(), ProfileOverlaySchema).optional().meta({
9385
9387
  description: "Per-category routing overlays for this profile, keyed by category name, using the same routing-only field set as every profile entry.",
9386
- examples: [{ review: { model: "anthropic/claude-opus-4-7" } }]
9388
+ examples: [{ review: { model: "anthropic/claude-opus-5" } }]
9387
9389
  })
9388
9390
  }).strict().meta({
9389
9391
  description: "A named routing-only overlay bundle. Entries under agents/categories have the same shape as the top-level agents/categories overlays, restricted to routing fields.",
9390
9392
  examples: [
9391
9393
  {
9392
- agents: { fixer: { model: "anthropic/claude-opus-4-7" } },
9394
+ agents: { fixer: { model: "anthropic/claude-opus-5" } },
9393
9395
  categories: { review: { pi: { thinking: "high" } } }
9394
9396
  }
9395
9397
  ]
@@ -9488,14 +9490,16 @@ function createSystematicConfigSchema(opts) {
9488
9490
  }),
9489
9491
  categories: record(string2(), CategoryOverlaySchema).default({}).meta({
9490
9492
  description: "Per-category configuration overlays keyed by category name",
9491
- examples: [{ review: { model: "anthropic/claude-opus-4-7" } }, {}]
9493
+ examples: [{ review: { model: "anthropic/claude-opus-5" } }, {}]
9492
9494
  }),
9493
9495
  profiles: record(string2(), createProfileBundleSchema(agentNames, qualifiedAgentIds)).default({}).meta({
9494
9496
  description: "Named routing-only overlay bundles, selectable by name via the profile field. Only valid in user config or OPENCODE_CONFIG_DIR config — a project config may select a profile but may not define this field.",
9495
9497
  examples: [
9496
9498
  {
9497
9499
  personal: {
9498
- agents: { "correctness-reviewer": { model: "openai/gpt-5" } }
9500
+ agents: {
9501
+ "correctness-reviewer": { model: "openai/gpt-6-astra" }
9502
+ }
9499
9503
  }
9500
9504
  },
9501
9505
  {}
@@ -659,17 +659,17 @@
659
659
  "description": "Per-agent configuration overlay",
660
660
  "examples": [
661
661
  {
662
- "model": "anthropic/claude-opus-4-7",
662
+ "model": "anthropic/claude-opus-5",
663
663
  "temperature": 0.1,
664
664
  "mode": "subagent"
665
665
  },
666
666
  {
667
667
  "opencode": {
668
- "model": "anthropic/claude-opus-4-7",
669
- "variant": "v2"
668
+ "model": "anthropic/claude-opus-5",
669
+ "variant": "high"
670
670
  },
671
671
  "pi": {
672
- "model": "anthropic/claude-opus-4-7",
672
+ "model": "anthropic/claude-opus-5",
673
673
  "thinking": "high"
674
674
  }
675
675
  }
@@ -698,7 +698,7 @@
698
698
  }
699
699
  ],
700
700
  "description": "Model identifier in provider/model format, or null to inherit parent model",
701
- "examples": ["anthropic/claude-sonnet-4", null]
701
+ "examples": ["anthropic/claude-sonnet-5", null]
702
702
  },
703
703
  "__schema8": {
704
704
  "trust": "project-or-higher",
@@ -932,8 +932,8 @@
932
932
  "description": "OpenCode-specific routing block. When present, model/variant here override the flat model/variant fields for OpenCode only; the flat fields remain the harness-neutral default and still apply to Pi. `variant` is bound to whichever layer supplies `model`: it is used only from that layer or a more specific one, and is dropped when a more specific layer sets (or nulls) `model` without repeating the variant. A `variant` with no `model` anywhere in the merged overlay is a config-load error raised by the routing resolver, not a parse-time error.",
933
933
  "examples": [
934
934
  {
935
- "model": "anthropic/claude-opus-4-7",
936
- "variant": "v2"
935
+ "model": "anthropic/claude-opus-5",
936
+ "variant": "high"
937
937
  },
938
938
  {
939
939
  "model": null
@@ -984,7 +984,7 @@
984
984
  "description": "Pi-specific routing block. When present, model/thinking here override the flat model field and the legacy `pi_subagents.<name>.thinking` value for Pi only; the flat model field remains the harness-neutral default and still applies to OpenCode. Unlike OpenCode's `variant`, `thinking` is independent of `model`: it applies to whatever model the delegate ends up running, including one inherited from the parent session, so `thinking` with no `model` anywhere in the merged overlay is valid and never a config-load error.",
985
985
  "examples": [
986
986
  {
987
- "model": "anthropic/claude-opus-4-7",
987
+ "model": "anthropic/claude-opus-5",
988
988
  "thinking": "high"
989
989
  },
990
990
  {
@@ -998,7 +998,7 @@
998
998
  "examples": [
999
999
  {
1000
1000
  "review": {
1001
- "model": "anthropic/claude-opus-4-7"
1001
+ "model": "anthropic/claude-opus-5"
1002
1002
  }
1003
1003
  },
1004
1004
  {}
@@ -1058,12 +1058,12 @@
1058
1058
  "description": "Per-category configuration overlay (same fields as agent minus disable)",
1059
1059
  "examples": [
1060
1060
  {
1061
- "model": "anthropic/claude-opus-4-7",
1061
+ "model": "anthropic/claude-opus-5",
1062
1062
  "temperature": 0.1
1063
1063
  },
1064
1064
  {
1065
1065
  "opencode": {
1066
- "variant": "v2"
1066
+ "variant": "high"
1067
1067
  },
1068
1068
  "pi": {
1069
1069
  "thinking": "high"
@@ -1172,7 +1172,7 @@
1172
1172
  "personal": {
1173
1173
  "agents": {
1174
1174
  "correctness-reviewer": {
1175
- "model": "openai/gpt-5"
1175
+ "model": "openai/gpt-6-astra"
1176
1176
  }
1177
1177
  }
1178
1178
  }
@@ -1206,7 +1206,7 @@
1206
1206
  {
1207
1207
  "agents": {
1208
1208
  "fixer": {
1209
- "model": "anthropic/claude-opus-4-7"
1209
+ "model": "anthropic/claude-opus-5"
1210
1210
  }
1211
1211
  },
1212
1212
  "categories": {
@@ -1225,7 +1225,7 @@
1225
1225
  "examples": [
1226
1226
  {
1227
1227
  "correctness-reviewer": {
1228
- "model": "openai/gpt-5"
1228
+ "model": "openai/gpt-6-astra"
1229
1229
  }
1230
1230
  }
1231
1231
  ],
@@ -1785,11 +1785,11 @@
1785
1785
  "description": "Routing-only overlay fields permitted inside a named profile bundle entry: model, variant, temperature, top_p, and the opencode/pi harness blocks. Non-routing fields (permission, skills, mode, hidden, disable, steps, color) are rejected.",
1786
1786
  "examples": [
1787
1787
  {
1788
- "model": "anthropic/claude-opus-4-7"
1788
+ "model": "anthropic/claude-opus-5"
1789
1789
  },
1790
1790
  {
1791
1791
  "opencode": {
1792
- "variant": "v2"
1792
+ "variant": "high"
1793
1793
  },
1794
1794
  "pi": {
1795
1795
  "thinking": "high"
@@ -1850,7 +1850,7 @@
1850
1850
  "examples": [
1851
1851
  {
1852
1852
  "review": {
1853
- "model": "anthropic/claude-opus-4-7"
1853
+ "model": "anthropic/claude-opus-5"
1854
1854
  }
1855
1855
  }
1856
1856
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fro.bot/systematic",
3
- "version": "3.16.1",
3
+ "version": "3.16.3",
4
4
  "description": "Compound-engineering loops for OpenCode, Pi, and Claude Code",
5
5
  "type": "module",
6
6
  "homepage": "https://fro.bot/systematic",
@@ -234,9 +234,9 @@ enum ModelTier {
234
234
 
235
235
  var modelId: String {
236
236
  switch self {
237
- case .fast: return "claude-3-haiku-20240307"
238
- case .balanced: return "claude-sonnet-4-20250514"
239
- case .powerful: return "claude-opus-4-20250514"
237
+ case .fast: return "claude-haiku-4-5"
238
+ case .balanced: return "claude-sonnet-5"
239
+ case .powerful: return "claude-opus-5"
240
240
  }
241
241
  }
242
242
  }
@@ -247,6 +247,18 @@ validation, and it does not delete the artifact to escape the check. A failing
247
247
  artifact is evidence and stays on disk; an absent artifact is never evidence
248
248
  of a clean run.
249
249
 
250
+ Both commands also accept `--allow-outside-artifact-root`, which skips the
251
+ containment check so a copy fetched from CI, an issue attachment, or a fixture
252
+ can be validated from outside `.context/systematic/ce-review`. It also drops
253
+ the requirement that `.context/systematic/ce-review` exist at all. That is not
254
+ a workaround for the "directory is unavailable" degradation described above:
255
+ that degradation is deliberate visible failure for the parent's own run
256
+ artifact, and the flag does not change how the parent's own validation run is
257
+ judged. This is an external-validation mode for artifacts the parent did not
258
+ just produce. The parent must never pass this flag when validating its own
259
+ run's `review-summary.json`; doing so would silently accept a mismatched or
260
+ misplaced artifact as this run's evidence.
261
+
250
262
  This is enforcement by visible failure, not by containment. An agent that
251
263
  never runs the command can still finalize an artifact, but produces no evidence
252
264
  in either direction. That is why the command exists as an independently