jules-orchestrator-kit 0.67.0 → 0.69.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/README.md CHANGED
@@ -68,7 +68,8 @@ git add .agent AGENTS.md SPEC.md CONSTRAINTS.md .gitignore && git commit -m "cho
68
68
 
69
69
  ```bash
70
70
  # 3. Author a scoped, verified task envelope with guardrails & secret scrubbing
71
- npx jules-orchestrator-kit task create
71
+ # Interactive by default. Pass the prompt to skip straight to review:
72
+ npx jules-orchestrator-kit task create -p "Refactor the invoice module"
72
73
  ```
73
74
 
74
75
  `init` reads the repository, not a template: it detects the stack, picks a
@@ -207,7 +208,7 @@ To maximize PR merge rates, dispatch tasks according to deterministic boundaries
207
208
  * **Fail-Closed Security & Secret Redaction:** Evaluates explicit Deny rules before Allow rules against canonicalized, case-folded paths. Redacts high-entropy keys and base64-encoded credentials (such as Kubernetes `Secret` manifests).
208
209
  * **Complexity & Cost Router:** Zero-dependency heuristic classifier (`src/router.mjs`) routing mechanical tasks to lightweight models while reserving primary models for complex refactors, with a `node --check` syntax-verification gate that transparently escalates a FAST-tier result to the primary provider if it left broken JS on disk.
209
210
  * **Terminal UI & Diagnostic Matrix (`agentctl doctor`):** Interactive terminal dashboard, task sidecar manager, and automated transactional self-repair.
210
- * **Verified Test Suite:** Tested with **1154 unit tests across 166 suites passing in < 15.0s**.
211
+ * **Verified Test Suite:** Tested with **1223 unit tests across 173 suites**, green on every supported platform.
211
212
 
212
213
  <br/>
213
214
 
@@ -240,7 +241,7 @@ To maximize PR merge rates, dispatch tasks according to deterministic boundaries
240
241
  | `doctor` | `agentctl doctor [--probe] [--json]` | Diagnostic check runner. `--probe` additionally starts the configured provider's CLI to confirm it answers, rather than only finding it on `PATH`. | `0` (Healthy), `1` (Failures) |
241
242
  | `queue` | `agentctl queue [--dag] [--concurrency <n>] [--dry-run] [--json]` | Consumes and executes task envelopes in `.agent/jules-queue/` with Kahn's DAG dependency resolution. Non-task files (manifests, `README.md`) are skipped, and `--dry-run` previews without moving anything. | `0` (Complete) |
242
243
  | `swarm` | `agentctl swarm [--json]` | Runs parallel multi-agent swarm across worker slots with PID liveness detection. | `0` (Complete) |
243
- | `check` / `gate` / `audit`| `agentctl check [--mode working-tree] [--fix] [--allow-protected] [--allow-test-change <kind>] [--json] [--json-report <path>]` | Runs security, secret scanning, rules budget audit, and tiered verification gates (with declarative assertion support) against working tree or branch. | `0` (Approved), `1` (Budget/Arg), `3` (Scope), `4` (Verify), `5` (Diff >75K), `6` (Secret), `8` (Flaky) |
244
+ | `check` / `gate` / `audit`| `agentctl check [--mode working-tree] [--fix] [--allow-protected] [--allow-test-change <kind>] [--json] [--json-report <path>]` | Runs security, secret scanning, rules budget audit, and tiered verification gates (with declarative assertion support) against working tree or branch. | `0` (Approved), `1` (Budget/Arg), `3` (Scope), `4` (Verify), `5` (Diff >75K), `6` (Secret **or** test integrity), `8` (Flaky) |
244
245
  | `mutate` / `mutation` | `agentctl mutate [--min-score <n>] [--max-mutants <n>] [--cmd <testCmd>] [--json]` | Runs zero-dependency diff mutation testing harness on changed hunks with operator inversion and safety rollback. | `0` (Passed), `1` (Score Low) |
245
246
  | `coverage` | `agentctl coverage [--min <pct>] [--cmd <testCmd>] [--base <ref>] [--json]` | Runs native zero-dependency V8 diff coverage check against added diff lines. | `0` (Passed), `1` (Low Coverage) |
246
247
  | `probe` / `stability` | `agentctl probe [--repeat <n>] [--min <passRate>] [--cmd <testCmd>] [--json]` | Probes test suite flakiness across N consecutive iterations with oscillation detection. | `0` (Passed), `1` (Flaky) |
@@ -285,6 +286,9 @@ branchPrefix: "agent/" # Prefix for task branches
285
286
  verify:
286
287
  test: "npm test"
287
288
  build: "npm run build"
289
+ timeout_ms: 300000 # Per-stage kill time in ms (default 300000)
290
+ minTests: 1 # Floor for "the suite actually ran" (0 disables)
291
+ required: true # false = this repo uses only the scope/secret phases
288
292
 
289
293
  # Scope protection rules (Deny-first evaluation)
290
294
  scope:
package/ROADMAP_V1.md ADDED
@@ -0,0 +1,210 @@
1
+ # 🗺️ Jules Orchestrator Kit — Roadmap to v1.0 & Beyond
2
+
3
+ The **jules-orchestrator-kit** is the zero-dependency safety gatekeeper and self-healing engineering kernel for autonomous coding agents — **Google Jules**, **Claude Code**, **Codex** and the **Gemini CLI** — in any repository and any stack.
4
+
5
+ > [!IMPORTANT]
6
+ > **Zero Runtime Dependencies is a strict core invariant.**
7
+ > Every feature on this roadmap is built strictly with native Node.js 20+ built-in modules (`node:fs`, `node:child_process`, `node:crypto`, `node:http`, `node:readline`, `node:test`).
8
+
9
+ ---
10
+
11
+ ## 📌 Release Milestones Overview
12
+
13
+ ```
14
+ v0.69.0 (Current Stable) ──► v0.70.0 (Distributed Swarms & Leases) ──► v1.0.0 (Production Hardened Kernel)
15
+ (Read, Not Just Counted) (Multi-Agent DAG & Resource Locks) (Enterprise Telemetry & SLA)
16
+ ```
17
+
18
+ ---
19
+
20
+ ## ✅ Shipped Milestones (v0.20.0 – v0.69.0)
21
+
22
+
23
+ ### v0.69.0: Read, Not Just Counted
24
+ - [x] **Expected-Value-First Assertions (`src/security.mjs`)** — JUnit and PHPUnit document the order the guard read as prose.
25
+ - [x] **A Regex Is An Expected Value (`src/security.mjs`)** — a rewritten pattern was neither a change nor a loss.
26
+ - [x] **Renaming A Test Is Not Tampering (`src/security.mjs`)** — on the one-line form, the name blanked into the expectation.
27
+ - [x] **An Environment Prefix Runs (`src/git.mjs`)** — `PYTHONPATH=src pytest` never started, and was reported as a failure.
28
+ - [x] **`node -e ""` Is A Placeholder (`src/stack-detector.mjs`)** — the same no-op, spelled to look like work.
29
+ - [x] **`init` Rejects An Oracle That Proves Nothing (`src/wizard-init.mjs`)** — silence at setup is silence at every gate after it.
30
+
31
+ ### v0.68.0: Not An Approval Either
32
+ - [x] **An Unreadable Dialect Blocks (`src/security.mjs`)** — the guard said it had not checked, and approved anyway.
33
+ - [x] **A Command That Cannot Fail Is Not An Oracle (`src/engine.mjs`)** — `task create` already refused what the gate accepted.
34
+ - [x] **chai And node-tap (`src/security.mjs`)** — a dot chain where RSpec has a space, and a receiver named `ct`.
35
+ - [x] **A Reformat Is Not A Weakening (`src/security.mjs`)** — the weakening count was still line-based.
36
+ - [x] **`bootstrap` Declines (`src/stack-detector.mjs`)** — rather than writing an oracle that asserts its own impossibility.
37
+
38
+ ### v0.67.0: A Move Is Not A Deletion
39
+ - [x] **In-Body Skips (`src/security.mjs`)** — a test could be silenced with `self.skipTest()`, the form unittest's own documentation uses.
40
+ - [x] **Cross-File Moves (`src/security.mjs`)** — refactoring produced CRITICAL findings for assertions that still run.
41
+ - [x] **The Published Package Explains Itself (`scripts/run-tests.mjs`)** — `npm test` crashed with a raw ENOENT in an installed copy.
42
+ - [x] **The Label Names What Failed (`bin/agentctl.mjs`)** — a deleted assertion was reported under a bare SECRETS heading.
43
+
44
+ ### v0.66.0: Saying Nothing Is Not Saying Approved
45
+ - [x] **An Assertion Is A Statement (`src/security.mjs`)** — a rewritten expected value whose keyword sat on a context line collected five green phases.
46
+ - [x] **Line Comments Are Stripped (`src/security.mjs`)** — they never were; `pending` was left at the comment and the copy after the loop put it back.
47
+ - [x] **The Boundary Reaches The Operator (`src/security.mjs`)** — the warning was wired to a function the gate does not call.
48
+ - [x] **Evidence Before Rules (`src/memory.mjs`)** — a hardcoded sentence was being injected into every prompt as if it were a fix.
49
+
50
+ ### v0.65.0: The First Hour
51
+ - [x] **The Quickstart No Longer Rejects Its Own Output (`src/engine.mjs`)** — `init` wrote the scaffold, told the user to commit it, and the gate called that commit a scope violation while advising `--allow-protected`.
52
+ - [x] **A Stated Count Outranks A Phrase (`src/ops/test-collection.mjs`)** — a healthy 190-test TAP suite was rejected as empty because a skipped fixture printed `no tests found`.
53
+ - [x] **`init` Probes On The Headless Path (`src/wizard-init.mjs`)** — `--yes` means "do not ask me", not "do not check".
54
+ - [x] **`verify.minTests` Reaches The Floor (`src/engine.mjs`)** — the failure message named a lever that was connected to nothing.
55
+
56
+ ### v0.64.0: What The Guard Could Not Read
57
+ - [x] **The Package Is Checked The Way It Is Installed (`scripts/package-integrity-check.mjs`)** — the tarball's own import graph, resolved inside the tarball. The previous release shipped the activation-coverage check without the contract it imports.
58
+ - [x] **Five Silent Ecosystems (`src/security.mjs`)** — JUnit, RSpec, PHPUnit, Minitest and XCTest all returned `PASS` on a rewritten expected value, and `assertEqual` had been working only by accident.
59
+ - [x] **`UNREADABLE` (`src/security.mjs`)** — coverage ending is fine; coverage ending silently is not.
60
+ - [x] **Contracts For The Opposite Failure (`src/guard-policy.mjs`)** — ten ordinary edits that must produce no finding, because a guard that flags everything is switched off within a week.
61
+
62
+ ### v0.63.0: Can It Still Go Red?
63
+ - [x] **Activation Coverage (`scripts/guard-reach-check.mjs`)** — Blocking in CI and in the release. Eleven known-bad canaries across six ecosystems must each still produce the finding they name; four hand-written mutants of the applicability predicate must each break at least one. Runs in 88 ms.
64
+ - [x] **A Hand-Written Policy Contract (`src/guard-policy.mjs`)** — Derived from what the tool advertises, never from the regexes that implement it, so the implementation cannot be its own oracle.
65
+ - [x] **Verdicts Carry Their Denominator (`src/security.mjs`)** — `checkTestTampering` reports `inputsSeen` and a `PASS`/`FAIL`/`NOT_APPLICABLE` status, so "checked and clean" is no longer byte-identical to "nothing was checked".
66
+
67
+
68
+ ### v0.62.0: No Denominator, No Claim
69
+ - [x] **A File Is Not A Claim (`src/stack-detector.mjs`)** — `make test` requires the Makefile to declare the target; `app.json` requires a package.json beside it; a `test` script that is `echo … && exit 0` is a placeholder, not an oracle.
70
+ - [x] **The Generated Fallback Oracle Can Fail (`src/stack-detector.mjs`)** — It checks that every source file parses instead of asserting that the working directory exists, and refuses to pass with nothing to check.
71
+ - [x] **Lockfiles And Toolchain Pins Are Protected (`src/config.mjs`)** — `package.json` was protected and `package-lock.json` was not. Plus `.envrc`, `.git-credentials`, cloud credential trees and CodeBuild.
72
+ - [x] **The Sixth Spelling Of "Where Are The Tests" (`src/evidence.mjs`)** — Go co-located tests and every monorepo produced `fileCount: 0`, which switched `strictTestLock` off in silence.
73
+ - [x] **Seven More Runners In The Collection Floor** — Maven, Gradle, PHPUnit, RSpec, dotnet, XCTest, ctest, ExUnit.
74
+ - [x] **100% Of Nothing Is Not 100% (`src/coverage.mjs`)** — Diff coverage reports `scored: false` where V8 measured nothing, instead of the best possible number.
75
+
76
+
77
+ ### v0.61.0: Green Against Nothing
78
+ - [x] **Collection Floor (`src/ops/test-collection.mjs`)** — A verification command that exits 0 having collected zero tests is no longer a pass. The count is read from the runner's own summary across node:test, pytest, cargo, jest, vitest, mocha and go; an unrecognised runner states no count and is not failed for it.
79
+
80
+
81
+ ### v0.60.0: A Guard Worth Reading
82
+ - [x] **Per-Check Overrides (`--allow-test-change <kind>`)** — Answering one tamper finding no longer silences the five other checks in the bundle. `--allow-test-modifications` remains as the blunt form.
83
+ - [x] **Unchanged Assertions Cancel Before Pairing (`src/security.mjs`)** — Reordering or moving an assertion is no longer reported as two rewritten expectations.
84
+ - [x] **A Reworded Message Is Not A Rewritten Expectation (`src/security.mjs`)** — Message-position string arguments are compared apart from value positions, so improving the wording of a failure is silent while `assert.equal(name(), "Alice")` → `"Bob"` still fires.
85
+
86
+
87
+ ### v0.59.0: One Answer Per Question
88
+ - [x] **One Test-Path Classifier (`src/test-paths.mjs`)** — Five modules had five definitions of "is this a test file?"; the tamper guard's could not see `tests/test_calc.py`, so every check in it was off for the standard pytest, Rust and RSpec layouts. All five now share `isTestPath`.
89
+ - [x] **Statement-Level Expectation Pairing (`src/security.mjs`)** — Assertions are reassembled from the diff's two images before pairing, so wrapping one across lines no longer walks through the guard. Contributed as PR #14 by an external agent; verified and merged.
90
+ - [x] **Numeric Literals Beyond Decimal (`src/security.mjs`)** — Hex, binary, octal, exponent and underscored literals normalise, so `0xFF` → `0xFE` is a rewritten expectation like any other.
91
+
92
+
93
+ ### v0.58.0: Locks That Lock
94
+ - [x] **A CLI Lock Is A Lease (`src/state.mjs`, `bin/agentctl.mjs`)** — `lock acquire` no longer witnesses its own exiting process, so two agents can no longer both hold the same file. `--ttl` bounds it; `--pid` binds it to a real long-lived process instead.
95
+ - [x] **A URL No Longer Disables The Secret Scanner (`src/security.mjs`)** — URLs, data: URIs and integrity hashes are stripped from a line rather than skipping the whole line, and a URL's userinfo and query values are scanned on their own.
96
+ - [x] **Untracked Symlinks Are Judged By Their Target (`src/git.mjs`)** — Working-tree mode resolves links git has never seen, and a symlink is rendered as its target *path* rather than read through.
97
+ - [x] **Rewritten Expectations Are Reported (`src/security.mjs`)** — An assertion whose literal changed while its shape did not is `ASSERTION_EXPECTATION_CHANGED`, with `--allow-test-modifications` as the documented override.
98
+ - [x] **Scope Guard Covers Every Forge (`src/config.mjs`)** — GitLab, CircleCI, Jenkins, Azure, Travis, Drone, Buildkite and Woodpecker definitions are denied alongside `.github/**`; build and test-runner configuration is protected.
99
+ - [x] **Python Runs As A Module (`src/stack-detector.mjs`)** — `python -m pytest` under whichever interpreter name this machine has, so an ordinary layout is not rejected on first contact.
100
+ - [x] **Build Artifacts Do Not Read As Tampering (`src/evidence.mjs`)** — Caches a test run writes itself are excluded from the integrity hash.
101
+
102
+
103
+ ### v0.57.0: Honest Reporting
104
+ - [x] **Stack-Native TDD Oracles (`src/ops/tdd-generator.mjs`)** — Generated in the runner's own language, with a RED check that requires the assertion to have actually run rather than accepting any non-zero exit.
105
+ - [x] **JSON Envelopes Reach The DAG Runner (`src/dag-engine.mjs`)** — `isDagTaskFile` is exported and used for the CLI's count, so a queue of `.json` envelopes is no longer reported as empty.
106
+ - [x] **`swarm` Honours Its Flags (`bin/agentctl.mjs`)** — `--json`, `--dry-run` and `--concurrency` are parsed, and the registry describes a dispatcher rather than an inspector.
107
+ - [x] **No Score Where Nothing Was Measured (`src/mutation.mjs`)** — An empty mutant population reports `null` and a reason instead of a vacuous 100%.
108
+ - [x] **New Files Are Visible To Every Gate (`src/git.mjs`)** — The synthetic diff for an untracked file carries a `@@` header, so mutation and diff coverage can place its lines at all.
109
+
110
+
111
+ ### v0.56.0: Wired, Not Just Shipped
112
+ - [x] **File-Aware Locking (`src/state.mjs`)** — `acquireLock()` compares the requested paths against every live lock instead of only the task id.
113
+ - [x] **Checkpoints That Exist (`src/engine.mjs`, `src/session-ops.mjs`)** — Taken before a dispatch and before `patch --apply`, so `agentctl rollback` finally has something to restore.
114
+ - [x] **Reachable Monorepo Scoping (`verify.scope: affected`)** — The boundary resolver is called by the gate, opt-in, defaulted on for repositories detected as monorepos and widening again when a shared file changes.
115
+ - [x] **Process-Group Reaping For The Test Runner (`scripts/run-tests.mjs`)** — An interrupted run takes its whole tree with it instead of orphaning every process below the first level. A first instance of the v0.60.0 guillotine, applied where the leak was actually observed.
116
+ - [x] **Live-Path Corrections (`src/provider.mjs`, `src/engine.mjs`)** — `listSources()` works against the real API, and Node's test-runner context no longer masks a failing verification command.
117
+
118
+
119
+ ### v0.55.0: Verification Integrity
120
+ - [x] **Assertion Weakening Detection (`src/security.mjs`)** — A specific expectation swapped for a vague one is reported as `ASSERTION_WEAKENED`, closing the one-out-one-in bypass that counting assertions could never see.
121
+ - [x] **Symlink-Aware Scope (`src/git.mjs`, `src/engine.mjs`)** — Added links are judged by the path they resolve to as well as their own name, without following anything on disk.
122
+ - [x] **Source-Bound Evidence (`src/evidence.mjs`)** — Manifests attest to the source tree, not only the tests, and the integrity hash finally sees test files that live at the repository root.
123
+
124
+
125
+ ### v0.54.1: The Gate Fails Closed
126
+ - [x] **No Oracle, No Approval (`src/engine.mjs`)** — A change that ran zero verification commands is rejected rather than approved; `verify.required: false` is the deliberate opt-out, read from the base commit.
127
+ - [x] **Binary Payload Inspection (`src/security.mjs`, `src/git.mjs`)** — Files git renders as "Binary files ... differ" are read directly for structured credentials, closing the NUL-byte bypass, and their real size is charged against the payload ceiling.
128
+
129
+
130
+ ### v0.54.0: First-Run Friction — Honest Errors, Detected Defaults
131
+ - [x] **Base Branch Detection (`src/git.mjs`)** — `detectDefaultBranch()` resolves `origin/HEAD`, a local `main`/`master`, then the checked-out branch, replacing the hardcoded `main` that failed the first gate in every `master` or `develop` repository.
132
+ - [x] **Diagnosable Gate Output (`bin/agentctl.mjs`, `src/ops/verify-output.mjs`)** — Phase errors are rendered instead of swallowed, and a failing stage's stdout is no longer discarded because the spawn wrapper wrote one line to stderr.
133
+ - [x] **Truthful Remediation Hints (`bin/agentctl.mjs`)** — Test tampering no longer advises rotating API keys, and a provider that refused the dispatch is no longer reported as an exhausted repair loop.
134
+ - [x] **Provider-First Onboarding & `agentctl provider set` (`src/wizard-init.mjs`, `src/config-edit.mjs`)** — The wizard asks which agent before asking about one vendor's plans, and switching providers no longer means re-running onboarding.
135
+ - [x] **Named Targets And Flags Actually Take Effect (`bin/agentctl.mjs`, `src/ops/ide-scaffold.mjs`, `src/ops/cli-intent.mjs`)** — `mcp init <target>`, `task create --title/--prompt` and `doctor --probe` all did something other than what they said; each now does what it says, or is no longer advertised.
136
+ - [x] **Onboarding Trap Distinguished From Overreach (`src/git.mjs`)** — `partitionTracked()` tells uncommitted scaffolding apart from an agent editing its own rules, so the first-run exit 3 advises committing rather than bypassing the gate.
137
+ - [x] **Rehearsals Write Nothing (`src/wizard-task.mjs`)** — `task create --dry-run` synthesizes and validates the envelope without queueing it.
138
+
139
+
140
+ ### v0.53.0: Universal Portability — Any Stack, Any Agent, Any CI
141
+ - [x] **Provider Readiness Probe (`src/provider-readiness.mjs`, `agentctl providers`)** — Per-provider capability descriptors: a credential for the hosted `jules` adapter, a `PATH` binary for the `claude-code`/`codex`/`gemini-flash` exec adapters, with a cross-platform PATHEXT-aware resolver that spawns nothing.
142
+ - [x] **Vendor-Neutral Environment Spellings (`src/env-aliases.mjs`)** — Every `JULES_*` knob also answers to `AGENT_*`, normalised once at CLI entry, with the legacy name always winning.
143
+ - [x] **Verification Profiles (`src/profiles.mjs`, `agentctl profile`)** — `minimal | standard | max` expanded at load time into a stack-aware pipeline that skips unsupported gates with a stated reason rather than failing the diff.
144
+ - [x] **Generated Stack-Aware CI (`src/ci-templates.mjs`, `agentctl ci init`)** — GitHub Actions and GitLab jobs carrying the detected stack's toolchain, replacing the copy of this repository's own nine-way Node matrix.
145
+ - [x] **Consumer Repository Hygiene (`bin/init.js`)** — `init` no longer copies the kit's twenty internal scripts into the target repository, and both entry points now write the same manifest pair.
146
+
147
+
148
+ ### v0.52.x: Entropy Hardening, Egress Auditing & CI Defense
149
+ - [x] **Binary Asset Classification Guard (`src/security.mjs`)** — Restricts binary asset skipping to tokens $\ge 256$ chars, preventing length $\pmod 4 = 0$ keys from bypassing entropy checks.
150
+ - [x] **CI Runtime & Egress Security (`.github/workflows/*.yml`)** — Linux eBPF runtime monitoring and network egress auditing via StepSecurity.
151
+ - [x] **GitHub Actions Supply Chain Hardening (`.github/`)** — Action SHA pinning, least-privilege `contents: read`, and automated Zizmor CI gate.
152
+ - [x] **Assertion Removal & Anti-Tamper Guard (`src/security.mjs`)** — Detects test deletion bypass (`ASSERTION_REMOVAL`) and vacuous assertion weakening.
153
+ - [x] **Shannon Entropy Diff Scanner (`src/security.mjs`)** — High-entropy token detection (>4.5 entropy, $\ge 24$ chars) on added diff lines.
154
+ - [x] **Jules API Patch Ingestion & Edge Webhooks (`src/session-ops.mjs`, `src/webhook.mjs`)** — API changeset extraction and Webhook `Uint8Array` Edge runtime parity.
155
+ - [x] **Zero-Test Oracle Bootstrapping & Git Remote Origin Auto-Detection (`src/stack-detector.mjs`, `src/git.mjs`)** — Auto-configuration for virgin repositories.
156
+
157
+ ### v0.51.0: Mechanical Falsification, AST Mutation & V8 Coverage
158
+ - [x] **Diff-Hunk Mutation Testing Engine (`src/mutation.mjs`, `agentctl mutate`)** — Transactional operator inversion with automatic shadow disk rollback.
159
+ - [x] **Native V8 Diff Coverage Enforcer (`src/coverage.mjs`, `agentctl coverage`)** — Zero-dependency V8 coverage mapping against added `+` hunks.
160
+ - [x] **Test-Oscillation Cycle Detector (`src/remediation.mjs`, `src/engine.mjs`)** — Halts multi-test oscillation cycles in OODA repair loops.
161
+ - [x] **Flakiness Stability Prober & Event Loop Delay Guard (`src/stability.mjs`, `src/perf.mjs`)** — Multi-pass repetition probing and Big-O event loop delay monitor.
162
+
163
+ ### v0.40.0 – v0.50.0: Architecture Governance & Universal Envelopes
164
+ - [x] **Universal Stack-Agnostic Task Templates (`src/web-templates.mjs`)** — Pre-calibrated templates for Cargo, Go, Python, PHP, .NET, Java, and Node.
165
+ - [x] **Specialist Agent Roles (`.agent/prompts/`)** — Eight stack-neutral specialist personas with dynamic command hydration.
166
+ - [x] **All-In-One CI Verification Gate (`agentctl check`)** — Unified security, scope, payload, and stack test gatekeeper.
167
+ - [x] **Stack-Tailored Contract Scaffolding (`src/scaffold.mjs`)** — Automatic `SPEC.md`, `CONSTRAINTS.md`, and `DESIGN.md` generation on init.
168
+
169
+ ### v0.30.0 – v0.39.0: Budget Provenance, Terminal UX & Autonomous Resilience
170
+ - [x] **Rolling 24h Budget Ledger & Limit Provenance (`src/budget.mjs`, `agentctl budget`)** — Multi-user attribution, rolling window accounting, and graceful estimate warnings.
171
+ - [x] **Terminal UI Engine (`src/tui.mjs`, `src/key-decoder.mjs`)** — Zero-dependency interactive selection, multi-select, secret masking, and raw keyboard decoding.
172
+ - [x] **Multi-OS CI Matrix Parity** — 100% green suite across Linux, macOS Darwin, and Windows CMD/PowerShell on Node 20, 22, and 24.
173
+ - [x] **Atomic Git Checkpoint & Rollback (`src/ops/checkpoint.mjs`, `agentctl rollback`)** — Pre-flight working tree snapshots and 1-command git restore.
174
+ - [x] **Documentation Sync Gate (`scripts/doc-sync-check.mjs`, `npm run jules:doc-sync`)** — Mechanical enforcement preventing documentation drift across manifests, roadmaps, and tests.
175
+
176
+ ---
177
+
178
+ ## 🎯 Target Milestones (v0.60.0 & v1.0.0)
179
+
180
+ ### v0.60.0: Distributed File Leases & Preemptive DAG Scheduling
181
+ - [ ] **Atomic Filesystem Lease & Heartbeat Protocol (`src/engine.mjs`, `src/flaky-ledger.mjs`)** — Directory-mutex file leasing with heartbeat timestamps, stale-lock detection via PID liveness inspection, and tombstone rotation without third-party daemons or Redis.
182
+ - [ ] **Preemptive Task Cancellation & Interface Fingerprints (`src/dag-engine.mjs`)** — Automatically aborts and yields downstream swarm tasks when upstream exported symbol interfaces diverge from their cryptographic SHA-256 fingerprints.
183
+ - [ ] **POSIX/Win32 Process Group Guillotine (`src/engine.mjs`)** — Tree teardown via `process.kill(-pid, 'SIGKILL')` on POSIX and `taskkill /T /F /PID` on Windows to eliminate orphaned dev-servers and background watchers.
184
+ - [ ] **Unicode Trojan Source & Homoglyph Fencing (`src/security.mjs`)** — Deterministic token scanner using V8 Unicode Property Escapes (`\p{Script=...}`) and NFKC normalization to block invisible Bidi overrides (CVE-2021-42574) and mixed-script homoglyphs.
185
+
186
+ ---
187
+
188
+ ## 🏁 Target Milestone v1.0.0: The Production-Grade Autonomous Engineering Kernel
189
+ *Focus: Long-term API stability, cryptographic compliance, and enterprise deployment guarantees.*
190
+
191
+ - [ ] **Cryptographic Compliance & SOC2 Audit Exporter (`agentctl audit export`)**:
192
+ - Export tamper-evident, signed JSON-LD / SPDX receipts of all agent activities linked to the SHA-256 telemetry ledger.
193
+ - *Foundation shipped:* `agentctl evidence generate|verify|show` (`src/evidence.mjs`) already produces SHA-256 evidence manifests with test-tamper locking.
194
+ - [ ] **Zero-Dependency Core Freezing & Stability Guarantee**:
195
+ - 100% API stability for `index.mjs` SDK exports, CLI exit codes (0–8), and configuration schema (`.agent/config.yml`).
196
+ - [ ] **High-Concurrency Swarm Benchmarking (500+ Daily Sessions)**:
197
+ - Stress testing with 50+ concurrent worker slots across 100k+ file repositories with zero lock contention or memory leaks.
198
+ - [ ] **Comprehensive Multi-Language Enterprise Test Matrix**:
199
+ - Automated CI test fixtures for polyglot environments (Node, Python, Go, Rust, .NET, PHP, Java, Flutter).
200
+ - [ ] **OODA Attempt Diff Retention & Inspection (`.agent/state/ooda/*.patch`, `agentctl patch --attempt <n>`)**:
201
+ - Retains intermediate working tree diffs and failure traces across OODA repair turns so developers can inspect failed hypotheses when an agent exhausts its retry budget.
202
+
203
+ ---
204
+
205
+ ## 🔮 Post-1.0 Long-Term Horizon (v1.x+)
206
+
207
+ - **Proactive Telemetry Ingestion (Type III Situational Awareness)**: Ingest dev-server crash logs, APM traces, and Playwright test artifacts into auto-synthesized task envelopes for background diagnosis.
208
+ - **Cross-Repository Swarm Orchestration**: Orchestrate breaking API contract changes across multiple distinct git repositories with atomic synchronization.
209
+ - **Multimodal Visual Verification Loop**: Direct integration with headless browser video/screenshot streams for autonomous visual regression repairs.
210
+ - **Wasm-Powered Structural AST Invariant Engine**: In-memory WebAssembly tree-sitter bindings (zero npm dependencies) for deep multi-language semantic AST verification.
package/bin/agentctl.mjs CHANGED
@@ -383,6 +383,7 @@ async function main() {
383
383
  committed: { type: "boolean" },
384
384
  fix: { type: "boolean" },
385
385
  "allow-protected": { type: "boolean" },
386
+ "allow-unreadable-tests": { type: "boolean" },
386
387
  // The tamper guard has always had an override — `allowTestModifications`
387
388
  // — and it was reachable only from JavaScript. So a legitimate change
388
389
  // of spec, which necessarily rewrites what a test expects, hit a
@@ -413,6 +414,7 @@ async function main() {
413
414
  mode: selectedMode,
414
415
  fix: values.fix,
415
416
  allowProtected: values["allow-protected"],
417
+ allowUnreadableTests: values["allow-unreadable-tests"],
416
418
  allowTestModifications: values["allow-test-modifications"],
417
419
  allowTestChanges: values["allow-test-change"],
418
420
  jsonReport: values["json-report"],
@@ -1611,6 +1613,13 @@ async function main() {
1611
1613
  console.log(` Stack detected : ${res.stack}`);
1612
1614
  console.log(` Oracle TestCmd : ${res.testCmd}`);
1613
1615
  console.log(` Config written : ${res.configPath}`);
1616
+ } else if (res.reason === "NO_ORACLE_POSSIBLE") {
1617
+ // Declining is a usable answer. Writing a config whose very first run
1618
+ // asserts its own impossibility is not: it left the user following
1619
+ // the gate's own repair advice into a dead end.
1620
+ console.error(`❌ No oracle could be generated for this repository.`);
1621
+ console.error(` ${res.detail}`);
1622
+ process.exit(1);
1614
1623
  } else {
1615
1624
  console.log(`ℹ️ Repository already has a verification oracle (${res.testCmd}). Use --force to overwrite.`);
1616
1625
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "0.67.0",
3
+ "version": "0.69.0",
4
4
  "description": "Zero-dependency safety gatekeeper, test oracle generator, and multi-agent coordination protocol for autonomous coding agents — Google Jules, Claude Code, Codex and Gemini CLI.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,7 +32,10 @@
32
32
  "JULES_RULES_TEMPLATE.md",
33
33
  ".agent/rules/",
34
34
  ".agent/prompts/",
35
- ".agent/workflows/"
35
+ ".agent/workflows/",
36
+ "CHANGELOG.md",
37
+ "ROADMAP_V1.md",
38
+ "AGENTS.md"
36
39
  ],
37
40
  "scripts": {
38
41
  "init": "node bin/init.js",
@@ -331,6 +331,15 @@ function compareSemver(a, b) {
331
331
 
332
332
  // ---- CLI ----
333
333
  const isMain = process.argv[1] && process.argv[1].endsWith("doc-sync-check.mjs");
334
+ // This gate compares the documentation against the test suite, which the
335
+ // published package does not contain. Run from an installed copy it reported
336
+ // six failures about files that were never meant to be there.
337
+ if (isMain && !existsSync(join(process.cwd(), "test"))) {
338
+ console.error("Documentation sync is a release gate for the repository, not a check on an installed package.");
339
+ console.error("It compares README/ROADMAP/CHANGELOG against the test suite, which is not shipped.");
340
+ console.error("Clone the repository to run it. To check an installed copy, run: npm run guard-reach");
341
+ process.exit(1);
342
+ }
334
343
  if (isMain) {
335
344
  const argv = process.argv.slice(2);
336
345
  const flag = (name) => {
package/src/config.mjs CHANGED
@@ -626,7 +626,13 @@ export function loadConfig(root = resolveRoot(), explicitPath = null) {
626
626
  const rawTeardown = parsed.teardown_cmd ?? parsed.verify?.teardown;
627
627
  const rawBuild = parsed.build_cmd ?? parsed.verify?.build;
628
628
  const rawUnit = parsed.verify?.unit;
629
- const verifyTimeoutMs = parsed.verify?.timeoutMs ?? parsed.verify?.timeout_ms ?? 60000;
629
+ // Five minutes, matching the value `init` writes and the README documents.
630
+ // The engine carries the same number as a fallback, but this loader always
631
+ // supplies one — so the fallback never fired, and every repository without
632
+ // an explicit `timeout_ms` kept the old one-minute limit while the
633
+ // changelog said otherwise. The default has to live where the value is
634
+ // resolved, not where it is consumed.
635
+ const verifyTimeoutMs = parsed.verify?.timeoutMs ?? parsed.verify?.timeout_ms ?? 300_000;
630
636
  const autoVerify = resolveVerify(root);
631
637
 
632
638
  const rawTier = String(process.env.JULES_TIER || parsed.tier || FALLBACK_TIER).toLowerCase();
@@ -697,7 +703,7 @@ export function loadConfig(root = resolveRoot(), explicitPath = null) {
697
703
  // Named so `doctor` can tell an operator that the gate they enabled is
698
704
  // running one fewer check than they think, and why.
699
705
  profileSkipped: profilePlan?.skipped ?? [],
700
- timeoutMs: Number.isFinite(Number(verifyTimeoutMs)) ? Number(verifyTimeoutMs) : 60000,
706
+ timeoutMs: Number.isFinite(Number(verifyTimeoutMs)) ? Number(verifyTimeoutMs) : 300_000,
701
707
  },
702
708
  evidence: {
703
709
  enabled: parsed.evidence?.enabled ?? true,
package/src/engine.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { loadConfig, parseYaml, normalizeScope } from "./config.mjs";
2
2
  import { isTestPath } from "./test-paths.mjs";
3
+ import { isPlaceholderTestScript } from "./stack-detector.mjs";
3
4
  import { checkCollectionFloor } from "./ops/test-collection.mjs";
4
5
  import { checkScope, scanDiff, scanBinaryPayloads, redactSecrets } from "./security.mjs";
5
6
  import { changedFiles, diffBytes, diffText, binaryDiffEntries, symlinkChanges, showFromOrigin, runCmd } from "./git.mjs";
@@ -214,6 +215,7 @@ export async function gate(opts = {}) {
214
215
  : parsed.verify?.min_tests !== undefined
215
216
  ? parsed.verify.min_tests
216
217
  : config.verify.minTests,
218
+ tamperGuard: parsed.verify?.tamperGuard || parsed.verify?.tamper_guard || config.verify.tamperGuard,
217
219
  scope: parsed.verify?.scope || config.verify.scope || "global",
218
220
  timeoutMs: parsed.verify?.timeoutMs || parsed.verify?.timeout_ms || config.verify.timeoutMs,
219
221
  };
@@ -328,6 +330,11 @@ export async function gate(opts = {}) {
328
330
  root,
329
331
  allowTestModifications: opts.allowTestModifications === true,
330
332
  allowTestChanges: opts.allowTestChanges,
333
+ // Read from the base commit like every other trusted field: a repository
334
+ // opts out of the tamper guard deliberately and on the record, and an
335
+ // uncommitted edit must not be able to do it.
336
+ tamperGuard: trustedVerify.tamperGuard,
337
+ allowUnreadableTests: opts.allowUnreadableTests === true,
331
338
  });
332
339
  // A binary file reaches the scanner as one summary line, so its contents were
333
340
  // never looked at — a NUL byte in front of a token was enough to hide it.
@@ -386,7 +393,11 @@ export async function gate(opts = {}) {
386
393
  }
387
394
 
388
395
  let flakyVerdictResult = null;
389
- const verifyTimeout = trustedVerify.timeoutMs || 60000;
396
+ // Five minutes. A minute was too short for an ordinary suite — a mid-sized
397
+ // Node repository takes longer than that on cold caches — and the timeout
398
+ // surfaced as a plain non-zero verification, so the gate told the user their
399
+ // tests failed when it had killed them. Raise it with verify.timeout_ms.
400
+ const verifyTimeout = trustedVerify.timeoutMs || 300_000;
390
401
 
391
402
  // Monorepo scoping: run the suites the change can actually break.
392
403
  //
@@ -588,7 +599,22 @@ export async function gate(opts = {}) {
588
599
  : { ok: true, count: null, runner: null, reason: null };
589
600
  const emptySuite = !collectionFloor.ok;
590
601
 
591
- const verifyOk = !failingCmd && !testTampered && !missingOracle && !emptySuite;
602
+ // A command that cannot fail is not an oracle, wherever it is configured.
603
+ //
604
+ // `task create` already refuses one — "Unfalsifiable Task Rejected: Task
605
+ // must include a non-trivial verification test/build command" — while the
606
+ // gate approved against `verify.test: "true"` and printed an advisory. The
607
+ // same kit disagreed with itself about the same string. The collection
608
+ // floor cannot catch this: `true` states no count, and failing on "I could
609
+ // not tell" would break every unlisted runner. But a command that is
610
+ // *recognisably* a placeholder is not an unlisted runner.
611
+ const placeholderOracle =
612
+ verificationRequired &&
613
+ !missingOracle &&
614
+ Boolean(testResult?.command) &&
615
+ isPlaceholderTestScript(testResult.command);
616
+
617
+ const verifyOk = !failingCmd && !testTampered && !missingOracle && !emptySuite && !placeholderOracle;
592
618
 
593
619
  // What actually broke. Without this the verify phase reported `ok: false` and
594
620
  // nothing else — not the stage, not the exit code, not a line of output — so
@@ -635,7 +661,22 @@ export async function gate(opts = {}) {
635
661
  "The gate approves a change because verification passed. Zero stages executed is not a pass.",
636
662
  ],
637
663
  }
638
- : emptySuite
664
+ : placeholderOracle
665
+ ? {
666
+ stageId: "placeholder-oracle",
667
+ command: testResult?.command || null,
668
+ exitCode: 0,
669
+ stdout: "",
670
+ stderr:
671
+ `The verification command is ${JSON.stringify(testResult.command)}, which cannot fail. ` +
672
+ "Nothing about this change was checked, so approving it would certify nothing. " +
673
+ "Point verify.test at a command that runs this repository's tests, or — if this repository " +
674
+ "intentionally uses only the scope and secret phases — set verify.required: false, which says so.",
675
+ diagnostics: [
676
+ "`agentctl task create` already refuses a task with this command; the gate now agrees with it.",
677
+ ],
678
+ }
679
+ : emptySuite
639
680
  ? {
640
681
  stageId: "empty-suite",
641
682
  command: testResult?.command || null,
package/src/git.mjs CHANGED
@@ -15,6 +15,10 @@ export class GateError extends Error {
15
15
  }
16
16
  }
17
17
 
18
+ // `NAME=value` at the head of a command line: a POSIX environment prefix,
19
+ // not the name of a program.
20
+ const ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
21
+
18
22
  const DEFAULT_TIMEOUT = 10 * 60 * 1000; // 10 minutes
19
23
  const DEFAULT_MAX_BUFFER = 10 * 1024 * 1024; // 10 MB
20
24
 
@@ -139,6 +143,8 @@ export function runCmd(command, opts = {}) {
139
143
  let args = [];
140
144
  let useShell = false;
141
145
  let shellCmd = "";
146
+ /** @type {Record<string,string>|null} */
147
+ let envOverlay = null;
142
148
  // True when `args` came from splitting a whitespace-separated string, which
143
149
  // means no element can itself contain whitespace. See the Windows note below.
144
150
  let tokenized = false;
@@ -153,6 +159,24 @@ export function runCmd(command, opts = {}) {
153
159
  shellCmd = trimmed;
154
160
  } else {
155
161
  const tokens = trimmed.split(/\s+/).filter(Boolean);
162
+
163
+ // A POSIX command may be prefixed with environment assignments, and
164
+ // half the Python world writes its test command exactly that way:
165
+ // `PYTHONPATH=src python3 -m pytest`. execFileSync took the first token
166
+ // as the program and failed with `spawnSync PYTHONPATH=src ENOENT` —
167
+ // which the gate then reported as a failed verification, so the user
168
+ // was told their tests broke when the command had never started.
169
+ //
170
+ // Peeling the assignments into the child's environment behaves the same
171
+ // on every platform. Handing the string to a shell would not: cmd.exe
172
+ // has no such syntax, so the Windows matrix would keep failing.
173
+ while (tokens.length > 1 && ENV_ASSIGNMENT.test(tokens[0])) {
174
+ const token = tokens.shift();
175
+ const eq = token.indexOf("=");
176
+ if (!envOverlay) envOverlay = {};
177
+ envOverlay[token.slice(0, eq)] = token.slice(eq + 1);
178
+ }
179
+
156
180
  binary = tokens[0] || "";
157
181
  args = tokens.slice(1);
158
182
  tokenized = true;
@@ -177,10 +201,12 @@ export function runCmd(command, opts = {}) {
177
201
  // wrapped in cmd.exe with every element quoted per the C-runtime + cmd.exe
178
202
  // rules, which preserves argv exactly while still letting the shell resolve
179
203
  // the `.cmd` shim (P-07).
204
+ const childEnv = envOverlay ? { ...(opts.env || process.env), ...envOverlay } : opts.env || process.env;
205
+
180
206
  const winShim = tokenized && process.platform === "win32";
181
207
  const winSpawn =
182
208
  !useShell && !winShim && process.platform === "win32"
183
- ? resolveWindowsSpawn(binary, args, opts.env || process.env)
209
+ ? resolveWindowsSpawn(binary, args, childEnv)
184
210
  : null;
185
211
 
186
212
  if (!binary && !useShell) {
@@ -194,7 +220,7 @@ export function runCmd(command, opts = {}) {
194
220
  cwd,
195
221
  encoding: "utf-8",
196
222
  stdio: ["ignore", "pipe", "pipe"],
197
- env: opts.env || process.env,
223
+ env: childEnv,
198
224
  timeout,
199
225
  maxBuffer,
200
226
  })
@@ -204,7 +230,7 @@ export function runCmd(command, opts = {}) {
204
230
  shell: winShim,
205
231
  windowsVerbatimArguments: Boolean(winSpawn && winSpawn.verbatim),
206
232
  stdio: ["ignore", "pipe", "pipe"],
207
- env: opts.env || process.env,
233
+ env: childEnv,
208
234
  timeout,
209
235
  maxBuffer,
210
236
  });
@@ -218,8 +244,16 @@ export function runCmd(command, opts = {}) {
218
244
  let stdout = (err.stdout || "").toString().trim();
219
245
  let stderr = (err.stderr || err.message || "").toString().trim();
220
246
 
221
- if (isTimeout && !stderr.includes("ETIMEDOUT")) {
222
- stderr = `Command execution timed out after ${timeout}ms (ETIMEDOUT)${stderr ? "\n" + stderr : ""}`;
247
+ if (isTimeout) {
248
+ // Node's own message for this is `spawnSync sh ETIMEDOUT`, which already
249
+ // contains the token the old guard tested for — so the explanation was
250
+ // skipped exactly when it was needed, and the user was left with five
251
+ // words that name neither the limit nor the way to raise it.
252
+ stderr =
253
+ `Command execution timed out after ${timeout}ms (ETIMEDOUT). ` +
254
+ `The command was killed, not failed: raise verify.timeout_ms in ` +
255
+ `.agent/config.yml if this suite legitimately takes longer.` +
256
+ (stderr ? "\n" + stderr : "");
223
257
  }
224
258
  if (isNobufs && !stderr.includes("ENOBUFS")) {
225
259
  stderr = `Command output buffer exceeded limit of ${maxBuffer} bytes (ENOBUFS)${stderr ? "\n" + stderr : ""}`;