jules-orchestrator-kit 0.73.1 → 0.74.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
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.74.0] - 2026-09-19
11
+
12
+ ### Added
13
+ - **Native `agentctl.task/v1` Frontmatter Specification & Zero-Dependency Parser (`src/envelope.mjs`, `scripts/validate-envelope.mjs`)**: Added canonical YAML frontmatter envelope support for task files (`.md`) without third-party dependencies. Reconciles `scope.allow`, `scope.deny`, `verification.commands`, `invariants`, `mcp_directives`, and `flags` with 100% backward compatibility for legacy HTML comments (`<!-- JULES_TASK_ENVELOPE: ... -->`).
14
+ - **Taxonomy Delegation Lanes (Green / Amber / Orange / Red) (`src/envelope.mjs`, `src/wizard-task.mjs`)**: Formulated formal risk lane delegation. Red tasks are rejected at validation; Orange tasks warn for external execution only; Amber tasks allow protected path changes (e.g. dependency upgrades in `package.json`, migrations, auth) under explicit plan approval (`requirePlanApproval: true`). Green tasks prevent unapproved protected scope mutations.
15
+ - **Circuit Breaker & Verification Contract in Envelope (`src/envelope.mjs`)**: Added frontmatter declarations for `circuitBreaker` (`max_attempts`, `max_diff_lines`, `stop_if_same_failure_repeats`) and `verification` metadata (`require_nonzero_test_count`, `trusted_base`), alongside `constraints` alias support.
16
+ - **Stack Invariant & MCP Directive Ingestion (`src/wizard-task.mjs`)**: Automatically infers runtime constraints (Zero-JS for Astro, workerd edge runtime constraints for Cloudflare Wrangler) and injects mandatory pre-execution documentation lookup directives into task instructions.
17
+ - **Dependency Upgrade Scope Harmonization (`src/wizard-task.mjs`)**: Updated `buildGuardrailFooter` to exclude explicitly authorized paths (`opts.allowedPaths`) from forbidden path warnings, eliminating conflicting instructions for dependency upgrade workloads.
18
+ - **Diff Payload Governor Branch Base Comparison (`src/wizard-task.mjs`)**: Calculated diff payload sizes against `git diff origin/${baseBranch}...HEAD | wc -c` instead of local uncommitted diffs.
19
+
10
20
  ## [0.73.1] - 2026-09-17
11
21
 
12
22
  ### Changed
package/README.md CHANGED
@@ -12,11 +12,12 @@ Use the kit to describe a scoped coding task, send it to Google Jules or an
12
12
  installed Claude Code, Codex or Gemini CLI, and verify the resulting changes.
13
13
  You can also run local checks without connecting an agent provider.
14
14
 
15
- **Dispatch and verification are separate.** Dispatch sends the task; `gate` runs
16
- configured checks on changes. `gate --fix` can request automated repairs. Provider
17
- output and passing checks still need review before merging.
15
+ **Dispatch, verification and repair are explicit.** Dispatch sends the task;
16
+ `gate` runs configured checks without mutating the working tree. Use
17
+ `agentctl repair` explicitly for repair workflows. Provider output and passing
18
+ checks still need review before merging.
18
19
 
19
- The current release is **v0.73.1**, a pre-1.0 release. A long-term stability policy is a
20
+ The current release is **v0.74.0**, a pre-1.0 release. A long-term stability policy is a
20
21
  [v1.0 goal](https://github.com/FullThrottle83/jules-orchestrator-kit/blob/main/ROADMAP_V1.md).
21
22
 
22
23
  ## Quickstart
@@ -32,9 +33,16 @@ npx jules-orchestrator-kit init
32
33
  ```
33
34
 
34
35
  The wizard detects the project and asks about provider and verification settings.
35
- Use `init --yes` to accept defaults. It creates `.agent/config.yml`, agent
36
- instructions, role prompts and project contract files. Inspect the generated
37
- configuration and diff, especially the test command and protected paths.
36
+ Use `init --yes` to accept defaults. By default it writes only the canonical
37
+ `.agent/config.yml` plus runtime-state entries in `.gitignore` when they are
38
+ missing. Inspect the generated configuration and diff, especially the test
39
+ command and protected paths. Use `init --dry-run` to preview the exact writes
40
+ without changing the repository.
41
+
42
+ The historical full scaffold (AGENTS.md, specialist prompts, rules, workflows and
43
+ contract templates) remains available through `agentctl init --force` and the
44
+ legacy `jules-init` entry point during the 0.x migration window; it is no longer
45
+ default-owned by `agentctl init`.
38
46
 
39
47
  ```bash
40
48
  git diff
@@ -43,9 +51,9 @@ git status --short
43
51
 
44
52
  Stage the generated files you reviewed, then commit them. The committed
45
53
  configuration establishes the trusted base policy used by verification. On an
46
- already initialized repository, back up and review `.agent/config.yml` and
47
- `.agent/jules.yml` before rerunning `init`; the wizard can regenerate those
48
- manifests. Avoid `--force` unless replacement of generated files is intended.
54
+ already initialized repository, back up and review `.agent/config.yml` before
55
+ rerunning `init`. Existing legacy `.agent/jules.yml` files are left untouched
56
+ by the default path. `--force` deliberately opts back into the legacy full scaffold.
49
57
 
50
58
  ### 2. Check provider readiness
51
59
 
@@ -107,7 +115,7 @@ For direct `agentctl` commands, install globally with
107
115
  | Create a scoped task | `agentctl task create` |
108
116
  | Preview queued work | `agentctl queue --dry-run` |
109
117
  | Check changes locally | `agentctl gate` |
110
- | Verify and request repairs | `agentctl gate --fix` |
118
+ | Start an explicit repair workflow | `agentctl repair --input "<failure>" --task` |
111
119
  | Diagnose setup | `agentctl doctor` |
112
120
  | Inspect a command's flags | `agentctl help <command>` |
113
121
 
@@ -166,7 +174,7 @@ npm run package-integrity
166
174
  npm run guard-reach
167
175
  ```
168
176
 
169
- The recorded baseline is **1530 unit tests across 204 suites**. Doc-sync compares
177
+ The recorded baseline is **1553 unit tests across 204 suites**. Doc-sync compares
170
178
  that count with an actual run. Counts do not establish correctness for every
171
179
  provider or project. See
172
180
  [contributing](https://github.com/FullThrottle83/jules-orchestrator-kit/blob/main/CONTRIBUTING.md)
@@ -186,19 +194,12 @@ for the review process.
186
194
  First identify which files setup created and which files already belonged to your
187
195
  project. For shared files, remove only the kit's additions using Git history.
188
196
 
189
- For a repository where **all listed paths belong exclusively to the kit**, remove
190
- the tracked scaffold and runtime state as follows. Adapt this list first if any
191
- path contains your own instructions, configuration or queued work:
192
-
193
- ```bash
194
- git rm -rf --ignore-unmatch .agent AGENTS.md SPEC.md CONSTRAINTS.md DESIGN.md \
195
- .github/workflows/agent-gate.yml .gitlab-ci.agent-gate.yml .cursor/rules/jules.mdc
196
- rm -rf .agent .agentctl
197
- npm uninstall -g jules-orchestrator-kit
198
- ```
197
+ For the default minimal setup, remove `.agent/config.yml` and the kit's
198
+ runtime-state block from `.gitignore`. Repositories that previously used the
199
+ legacy full scaffold may also contain AGENTS.md, prompts, rules, workflows and
200
+ contract files; remove those only after confirming ownership.
199
201
 
200
- Remove the kit's appended `.gitignore` block while preserving other entries.
201
- See the uninstall guide for the complete inventory.
202
+ See the uninstall guide for the complete inventory and legacy cleanup steps.
202
203
 
203
204
  ## Limitations and attribution
204
205
 
package/bin/agentctl.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { parseArgs } from "node:util";
4
4
  import { readFileSync, existsSync, readdirSync, statSync, renameSync, mkdirSync } from "node:fs";
5
- import { join, resolve } from "node:path";
5
+ import { join, resolve, relative } from "node:path";
6
6
  import { applyEnvAliases } from "../src/env-aliases.mjs";
7
7
  import { selectFailureOutput } from "../src/ops/verify-output.mjs";
8
8
  import { loadConfig, resolveRoot, detectStack, bootstrapZeroTestRepo } from "../src/config.mjs";
@@ -22,7 +22,18 @@ import { COMMAND_REGISTRY } from "../src/ops/command-registry.mjs";
22
22
  applyEnvAliases(process.env);
23
23
 
24
24
  const args = process.argv.slice(2);
25
- const command = args[0];
25
+ const requestedCommand = args[0] || "";
26
+ const gateCommands = new Set(["gate", "check", "audit"]);
27
+
28
+ if (gateCommands.has(requestedCommand) && args.includes("--fix")) {
29
+ console.error("Error: `agentctl gate` is non-mutating; `--fix` no longer dispatches automated repairs.");
30
+ console.error("Run the gate normally, then use `agentctl repair` explicitly with the failure trace you want repaired.");
31
+ process.exit(2);
32
+ }
33
+
34
+ // `repair` is the canonical spelling; `fix` remains a 0.x compatibility
35
+ // alias. Both route through the same existing implementation below.
36
+ const command = requestedCommand;
26
37
 
27
38
  export const VERSION = KIT_VERSION;
28
39
 
@@ -294,8 +305,13 @@ async function main() {
294
305
  }
295
306
 
296
307
  const root = resolveRoot();
297
- reapOrphanedIntents(root);
298
- reapStaleMutexDirs(root);
308
+ // Init is a configuration boundary, not an operational maintenance command.
309
+ // In particular, --dry-run must not create .agent/ indirectly by reaping
310
+ // journal intents or mutex directories before init itself gets control.
311
+ if (command !== "init") {
312
+ reapOrphanedIntents(root);
313
+ reapStaleMutexDirs(root);
314
+ }
299
315
  const config = loadConfig(root);
300
316
 
301
317
  switch (command) {
@@ -361,7 +377,7 @@ async function main() {
361
377
  console.error(
362
378
  `Error: Unknown agent role '${values.role}'. Expected matching prompt file in .agent/prompts/ (e.g. Auditor, Performance, Security, Hygiene, Testing).`
363
379
  );
364
- console.error(` Run 'agentctl init' to scaffold the shipped role prompts.`);
380
+ console.error(` Use a canonical shipped role name or add a repository override under .agent/prompts/.`);
365
381
  process.exit(1);
366
382
  }
367
383
  }
@@ -676,7 +692,7 @@ async function main() {
676
692
  } else if (failedPhase === "verify" || failedPhase === "evidence") {
677
693
  console.log(`💡 Remediation Hint (Exit ${res.code} Verification Failed):`);
678
694
  console.log(` • The stage above exited non-zero. Reproduce it locally, then re-run the gate.`);
679
- console.log(` • To let agentctl attempt the repair loop itself, pass: agentctl gate --fix\n`);
695
+ console.log(` • To start an explicit repair workflow, pipe the failing command's output to: agentctl repair\n`);
680
696
  } else if (res.code === 3) {
681
697
  // The same violation has two very different causes. Right after
682
698
  // `init`, every offending path is a file the tool itself just wrote
@@ -965,6 +981,7 @@ async function main() {
965
981
  break;
966
982
  }
967
983
 
984
+ case "repair":
968
985
  case "fix": {
969
986
  const { repair, planTaskCreate, resolveRoot, redactSecrets } = await import("../index.mjs");
970
987
  const { values, positionals } = parseArgs({
@@ -996,7 +1013,7 @@ async function main() {
996
1013
  }
997
1014
 
998
1015
  if (!errorInput.trim()) {
999
- console.error("❌ Error: No error log or failure input provided. Pipe stdout/stderr via `npm test 2>&1 | agentctl fix` or provide --file/--input.");
1016
+ console.error("❌ Error: No error log or failure input provided. Pipe stdout/stderr via `npm test 2>&1 | agentctl repair` or provide --file/--input.");
1000
1017
  process.exit(1);
1001
1018
  }
1002
1019
 
@@ -1957,19 +1974,54 @@ async function main() {
1957
1974
  provider: values.provider,
1958
1975
  profile: values.profile,
1959
1976
  allowDefaults: true,
1977
+ dryRun: values["dry-run"],
1978
+ // --force is retained as the explicit 0.x compatibility path: it
1979
+ // restores the legacy Jules manifest and full repository scaffold.
1980
+ legacyManifest: values.force,
1981
+ // JSON mode is a protocol surface: progress spinners must not prefix
1982
+ // the payload with human-readable status lines.
1983
+ stdout: values.json ? { write() {} } : process.stdout,
1960
1984
  });
1961
1985
 
1962
- // The wizard writes the manifest; the assets the CLI's documented
1963
- // features actually need — AGENTS.md, the role prompts, the guardrails,
1964
- // the gitignore entries — used to be scaffolded only by the separate
1965
- // `jules-init` binary that the README's quickstart never mentions.
1966
- const { scaffoldRepoAssets } = await import("../src/scaffold.mjs");
1967
- const scaffold = scaffoldRepoAssets(root, { force: values.force });
1986
+ // Default init deliberately owns only the canonical config plus the
1987
+ // runtime-state ignore block. The old full scaffold is opt-in via
1988
+ // --force (and remains available through the shipped jules-init binary).
1989
+ const {
1990
+ ensureGitignore,
1991
+ planGitignoreEntries,
1992
+ planScaffoldRepoAssets,
1993
+ scaffoldRepoAssets,
1994
+ } = await import("../src/scaffold.mjs");
1995
+ const plannedGitignore = planGitignoreEntries(root);
1996
+ const legacyPlan = values.force ? planScaffoldRepoAssets(root, { force: true }) : { created: [], gitignore: plannedGitignore };
1997
+ const scaffold =
1998
+ values.force && !values["dry-run"]
1999
+ ? scaffoldRepoAssets(root, { force: true })
2000
+ : { created: legacyPlan.created, gitignore: plannedGitignore };
2001
+ const gitignore =
2002
+ values["dry-run"]
2003
+ ? plannedGitignore
2004
+ : values.force
2005
+ ? scaffold.gitignore
2006
+ : ensureGitignore(root);
2007
+ const writes = [
2008
+ ...res.writes.map((p) => (relative(root, p) || p).replaceAll("\\", "/")),
2009
+ ...legacyPlan.created,
2010
+ ...(plannedGitignore.length > 0 ? [".gitignore"] : []),
2011
+ ].filter((p, index, all) => all.indexOf(p) === index);
1968
2012
 
1969
2013
  if (values.json) {
1970
- console.log(JSON.stringify({ ...res, scaffold }, null, 2));
2014
+ console.log(JSON.stringify({ ...res, writes, gitignore }, null, 2));
1971
2015
  } else {
1972
- console.log(`✅ Onboarding complete! Manifest generated at ${res.configPath}`);
2016
+ console.log(
2017
+ values["dry-run"]
2018
+ ? "🧪 Dry run — no files written."
2019
+ : `✅ Onboarding complete! Manifest generated at ${res.configPath}`
2020
+ );
2021
+ if (values["dry-run"]) {
2022
+ console.log(" Would write:");
2023
+ for (const path of writes) console.log(` - ${path}`);
2024
+ }
1973
2025
  console.log(` Tier: ${res.plan.tier.toUpperCase()} (${res.plan.limits.concurrency} worker(s), ${res.plan.limits.daily_tasks} daily tasks)`);
1974
2026
  {
1975
2027
  const { probeProvider } = await import("../src/provider-readiness.mjs");
@@ -1988,34 +2040,35 @@ async function main() {
1988
2040
  console.log(` Verification Test Command : None detected (run "agentctl bootstrap" to create a test oracle)`);
1989
2041
  }
1990
2042
  console.log(` Active Presets : ${res.plan.presets.join(", ")}`);
1991
- for (const item of scaffold.created) {
1992
- console.log(` Scaffolded : ${item}`);
1993
- }
1994
- if (scaffold.gitignore.length > 0) {
1995
- console.log(` Ignored runtime state : ${scaffold.gitignore.length} entries added to .gitignore`);
1996
- }
1997
-
1998
- // `.agent/config.yml` and `.agent/jules.yml` are both on the gate's deny
1999
- // list, by design — the agent must not edit its own rules. Leaving them
2000
- // uncommitted meant the very first `agentctl gate` rejected the working
2001
- // tree for files init had just written, which reads as the tool
2002
- // catching the user cheating on step three.
2003
- const rootContracts = ["SPEC.md", "CONSTRAINTS.md", "DESIGN.md"].filter((f) => existsSync(join(root, f)));
2004
- // A fresh `npm install`/`npm init -y` just created package manifests the
2005
- // scope rules `protect`. Leaving them out of the hint meant the very
2006
- // first `agentctl gate`/`agentctl task create` still rejected the tree
2007
- // even after the user committed exactly what this message listed. Fold
2008
- // the install-produced, currently-addable artifacts in so the suggested
2009
- // commit actually leaves the tree clean enough for step three.
2010
- const { addableOnboardingArtifacts } = await import("../src/git.mjs");
2011
- const filesToAdd = [".agent", "AGENTS.md", ...rootContracts, ...addableOnboardingArtifacts(root), ".gitignore"]
2012
- .filter((f) => existsSync(join(root, f)));
2013
- console.log(`\n Commit the manifest and contracts so the gate does not read them as agent edits:`);
2014
- console.log(` git add ${filesToAdd.join(" ")} && git commit -m "chore: add agent config"`);
2015
-
2016
- const { resolveNextStep, renderNextStep } = await import("../src/ops/next-step.mjs");
2017
- const next = resolveNextStep(root);
2018
- console.log(renderNextStep({ version: VERSION, root, next, budgetLine: "" }));
2043
+ if (values.force) {
2044
+ const verb = values["dry-run"] ? "Would scaffold (legacy)" : "Scaffolded (legacy)";
2045
+ for (const item of legacyPlan.created) {
2046
+ console.log(` ${verb.padEnd(26)}: ${item}`);
2047
+ }
2048
+ }
2049
+ if (!values["dry-run"] && gitignore.length > 0) {
2050
+ console.log(` Ignored runtime state : ${gitignore.length} entries added to .gitignore`);
2051
+ }
2052
+
2053
+ if (!values["dry-run"]) {
2054
+ // A fresh `npm install`/`npm init -y` may have created package
2055
+ // manifests the scope rules protect. Include only files that really
2056
+ // exist; minimal init itself owns .agent/config.yml and, when needed,
2057
+ // the .gitignore runtime-state block.
2058
+ const { addableOnboardingArtifacts } = await import("../src/git.mjs");
2059
+ const filesToAdd = [
2060
+ ".agent/config.yml",
2061
+ ...(values.force ? [".agent/jules.yml", ...legacyPlan.created] : []),
2062
+ ...addableOnboardingArtifacts(root),
2063
+ ".gitignore",
2064
+ ].filter((p, index, all) => all.indexOf(p) === index && existsSync(join(root, p)));
2065
+ console.log(`\n Commit the config so the gate does not read it as an agent edit:`);
2066
+ console.log(` git add ${filesToAdd.join(" ")} && git commit -m "chore: add agent config"`);
2067
+
2068
+ const { resolveNextStep, renderNextStep } = await import("../src/ops/next-step.mjs");
2069
+ const next = resolveNextStep(root);
2070
+ console.log(renderNextStep({ version: VERSION, root, next, budgetLine: "" }));
2071
+ }
2019
2072
  }
2020
2073
  process.exit(0);
2021
2074
  break;
package/bin/init.js CHANGED
@@ -40,12 +40,14 @@ try {
40
40
 
41
41
  if (isHelp) {
42
42
  console.log(`
43
- Google Jules Orchestration Kit - Init Scaffolding CLI
43
+ Google Jules Orchestration Kit - Legacy Full Scaffold
44
44
 
45
45
  Usage:
46
- npx jules-orchestrator-kit [options]
47
46
  npx jules-init [options]
48
47
 
48
+ For the minimal canonical setup, use:
49
+ npx jules-orchestrator-kit init
50
+
49
51
  Options:
50
52
  -f, --force Overwrite existing AGENTS.md, .agent/jules.yml, and orchestration scripts.
51
53
  -i, --interactive Launch interactive wizard to prompt for repository and branch configuration.
@@ -119,10 +121,9 @@ if (detected.testCmd || detected.buildCmd) {
119
121
  console.log(` - Build Command: ${detected.buildCmd || "(none)"}`);
120
122
  }
121
123
 
122
- // 2-3. Scaffold AGENTS.md, .agent/ structure, role prompts, rules and workflows.
123
- // Shared with `agentctl init` so the two entry points cannot scaffold different
124
- // repositories — which is exactly what they used to do, with the README's
125
- // quickstart pointing at the one that scaffolded less.
124
+ // 2-3. Legacy full scaffold: AGENTS.md, .agent/ role prompts, rules and
125
+ // workflows. The canonical `agentctl init` path is intentionally smaller now;
126
+ // this entry point remains for 0.x repositories that still want the full bundle.
126
127
  const { scaffoldRepoAssets } = await import("../src/scaffold.mjs");
127
128
  const scaffolded = scaffoldRepoAssets(targetDir, { force: isForce });
128
129
  for (const item of scaffolded.created) {
@@ -133,11 +134,8 @@ const agentDir = path.join(targetDir, ".agent");
133
134
 
134
135
  // Scaffold the manifest pair.
135
136
  //
136
- // This entry point used to hand-roll a thinner `.agent/jules.yml` while
137
- // `agentctl init` wrote a `.agent/config.yml` the runtime actually reads, so
138
- // which of the two scaffolders you happened to run decided whether the
139
- // repository had a provider, a tier and a verification profile at all. Both
140
- // now go through `planInit`.
137
+ // This compatibility entry point still writes both manifests, but derives them
138
+ // from the same `planInit` model as the canonical path so values cannot drift.
141
139
  const { planInit } = await import("../src/wizard-init.mjs");
142
140
  const initPlan = planInit(targetDir, {
143
141
  testCmd: detected.testCmd,
@@ -226,8 +224,7 @@ if (fs.existsSync(targetPkgPath) && targetDir !== kitRoot) {
226
224
  }
227
225
  }
228
226
 
229
- // 5b. The .gitignore entries are written by scaffoldRepoAssets above, so the
230
- // two entry points cannot disagree about which runtime paths stay untracked.
227
+ // 5b. Reuse the same runtime ignore policy as canonical init.
231
228
  if (scaffolded.gitignore.length > 0) {
232
229
  console.log(`✅ Added ${scaffolded.gitignore.length} runtime state entries to .gitignore`);
233
230
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "0.73.1",
3
+ "version": "0.74.0",
4
4
  "description": "Zero-dependency safety gatekeeper, test oracle generator, and multi-agent coordination protocol for autonomous coding agents \u2014 Google Jules, Claude Code, Codex and Gemini CLI.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { readFileSync, existsSync } from "node:fs";
4
- import { validateEnvelope } from "../src/envelope.mjs";
4
+ import { validateEnvelope, parseEnvelopeHeader } from "../src/envelope.mjs";
5
5
 
6
6
  const args = process.argv.slice(2);
7
7
  const envelopeFile = args[0];
8
8
 
9
9
  if (!envelopeFile) {
10
- console.log("Usage: node scripts/validate-envelope.mjs <path-to-envelope.json>");
10
+ console.log("Usage: node scripts/validate-envelope.mjs <path-to-envelope.json|path-to-task.md>");
11
11
  process.exit(1);
12
12
  }
13
13
 
@@ -18,7 +18,26 @@ if (!existsSync(envelopeFile)) {
18
18
 
19
19
  try {
20
20
  const content = readFileSync(envelopeFile, "utf-8");
21
- const payload = JSON.parse(content);
21
+ let payload = null;
22
+ if (envelopeFile.endsWith(".json")) {
23
+ payload = JSON.parse(content);
24
+ } else {
25
+ payload = parseEnvelopeHeader(content);
26
+ if (!payload) {
27
+ try {
28
+ payload = JSON.parse(content);
29
+ } catch {
30
+ payload = null;
31
+ }
32
+ }
33
+ }
34
+
35
+ if (!payload || typeof payload !== "object") {
36
+ console.error("❌ TASK ENVELOPE PREMISE VALIDATION FAILED:");
37
+ console.error(" - File does not contain a valid JSON payload or task envelope frontmatter.");
38
+ process.exit(1);
39
+ }
40
+
22
41
  const res = validateEnvelope(payload);
23
42
 
24
43
  if (!res.ok) {
@@ -39,6 +58,6 @@ try {
39
58
  console.log("✅ Task envelope validated successfully.");
40
59
  process.exit(0);
41
60
  } catch (err) {
42
- console.error(`❌ Invalid JSON or execution error: ${err.message}`);
61
+ console.error(`❌ Invalid envelope or execution error: ${err.message}`);
43
62
  process.exit(1);
44
63
  }
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { readFileSync, existsSync } from "node:fs";
3
3
  import { appendTelemetry } from "./telemetry.mjs";
4
+ import { parseEnvelopeHeader } from "./envelope.mjs";
4
5
 
5
6
  /**
6
7
  * Custom error thrown when a circular dependency is detected in the DAG.
@@ -495,20 +496,19 @@ export async function executeQueueDag(root = process.cwd(), options = {}) {
495
496
 
496
497
  let targetFiles = [];
497
498
 
498
- // Check envelope header
499
- const match = content.match(/<!--\s*JULES_TASK_ENVELOPE:\s*({[\s\S]*?})\s*-->/);
500
- if (match) {
501
- try {
502
- const meta = JSON.parse(match[1]);
503
- if (meta.id) taskId = meta.id;
504
- if (meta.title) title = meta.title;
505
- if (meta.role) role = meta.role;
506
- if (meta.tier) tier = meta.tier;
507
- if (Array.isArray(meta.targetFiles)) targetFiles = meta.targetFiles;
508
- else if (Array.isArray(meta.referenced_paths)) targetFiles = meta.referenced_paths;
509
- if (Array.isArray(meta.dependsOn)) dependsOn = meta.dependsOn;
510
- else if (typeof meta.dependsOn === "string") dependsOn = meta.dependsOn.split(",").map((s) => s.trim()).filter(Boolean);
511
- } catch (_) {}
499
+ // Check envelope header (supports agentctl.task/v1 YAML frontmatter and legacy HTML comment)
500
+ const header = parseEnvelopeHeader(content);
501
+ if (header) {
502
+ if (header.id || header.taskId) taskId = header.id || header.taskId;
503
+ if (header.title) title = header.title;
504
+ if (header.role) role = header.role;
505
+ if (header.tier) tier = header.tier;
506
+ if (Array.isArray(header.targetFiles)) targetFiles = header.targetFiles;
507
+ else if (Array.isArray(header.referenced_paths)) targetFiles = header.referenced_paths;
508
+ else if (Array.isArray(header.allowed_paths)) targetFiles = header.allowed_paths;
509
+ else if (header.scope && Array.isArray(header.scope.allow)) targetFiles = header.scope.allow;
510
+ if (Array.isArray(header.dependsOn)) dependsOn = header.dependsOn;
511
+ else if (typeof header.dependsOn === "string") dependsOn = header.dependsOn.split(",").map((s) => s.trim()).filter(Boolean);
512
512
  } else if (file.endsWith(".json")) {
513
513
  try {
514
514
  const parsed = JSON.parse(content);
package/src/engine.mjs CHANGED
@@ -143,6 +143,7 @@ export function fingerprintFailureState(failure = {}, root = process.cwd()) {
143
143
  * FAILS CLOSED ON GIT OR CONFIG ERRORS.
144
144
  */
145
145
  export async function gate(opts = {}) {
146
+ opts = opts && typeof opts === "object" ? { ...opts, fix: false } : { fix: false };
146
147
  const root = opts.root || process.cwd();
147
148
  const mode = opts.mode || (opts.workingTree ? "working-tree" : (process.env.JULES_GATE_MODE || "working-tree"));
148
149
  const config = opts.config || (mode === "working-tree" ? loadConfig(root) : null);