Release 0.8.6 - #164
Merged
Merged
Conversation
…ce (sable-cs8) global-setup.ts already builds dist/ once before the (parallel) vitest suite, but brief/completion/ci-init/github-action each re-ran `pnpm run build` unconditionally in beforeAll. With fileParallelism enabled, those concurrent tsc runs rewrite dist/ while other workers spawn `node dist/index.js` — so a spawn landing mid-rewrite loads a half-written module and dies with e.g. "SyntaxError: ... does not provide an export named createUpdateBetterleaksCommand" (observed in CI on tests/brief.test.ts > exits 1 for unknown topic). Guard each redundant build on !existsSync(dist/index.js), matching the pattern agent-commands/agent-init-hermes/scan-exclude-paths already use. dist is guaranteed present from globalSetup, so the guards skip on every normal run and nothing rewrites dist during parallel execution. Verified: the 4 files + a 5th CLI-spawning file run green together (223 tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…le-6d6/784/83b/t0q) - sable-6d6: test_all_21_patterns_present asserted ==21 but the HashiCorp Vault token (#159) brought it to 22 — a pre-existing red test. Renamed to test_all_patterns_present with a floor (>=22) + uniqueness check so adding a pattern no longer breaks it. Node has no count assertion (verified). - sable-784: platform-integration Gemini tests flaked with ENOENT on settings.json. Root cause: a full `agent init` install runs ~8-24s, but runCli's spawnSync timeout was 15s — under concurrent suite load it tipped over, returned status:null, and a downstream readFileSync hit a file the killed child never wrote. Raised the default timeout to 60s (covers every init call) and added runCliOk() which asserts exit 0 before reading, turning an opaque ENOENT into "CLI failed: <stderr>" and catching real regressions. - sable-83b: notifications.test.ts + audit-logger-lifecycle.test.ts used arbitrary 50-100ms setTimeout sleeps for fire-and-forget webhook dispatch, flaky under CI load. Positive assertions now poll via vi.waitFor; negative assertions use a deterministic setImmediate microtask flush (DNS is mocked, so there are no real timers/IO to wait on). - sable-t0q (CWE-367): betterleaks.ts built a temp report path from a predictable Date.now() name in the shared tmpdir. Replaced with fs.mkdtempSync (private 0700 dir, atomic), report.json inside it, best-effort rmSync cleanup in finally (also fixes prior temp-file leaks on throw paths). Brings Node to parity with Python's existing TemporaryDirectory. rafter-reviewed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(sable-qli/bnl/59s/29w)
Adds a runtime off-switch for the PreToolUse hook, honored ONLY from trusted,
machine-owner-owned sources — the RAFTER_DISABLE_HOOKS / RAFTER_DISABLE_SECRET_SCAN
/ RAFTER_DISABLE_COMMAND_POLICY env vars and the global ~/.rafter/config.json
`agent.hooks.{enabled,secretScan,commandPolicy}` keys. NEVER from project-local
.rafter.yml: a rafter-secure-design trust-boundary pass established that honoring a
disable flag from a repo-shipped file would let a hostile clone silently disable a
victim's secret scanning + command interception. Enforced structurally — the
resolver reads ConfigManager.load() (global only), not loadWithPolicy(), and `hooks`
is absent from the PolicyFile schema.
- New shared resolver: node/src/core/hook-control.ts + python/.../hook_control.py.
Env overrides global (D5); default enabled; corrupt config / unrecognized value
fails safe to enabled (D2). Granular: whole-hook, secret-scan-only, command-
policy-only. The git-commit staged-secret scan (inside evaluateBash) stays gated
by secretScan, command interception by commandPolicy.
- `rafter agent status` (+ --json `hook_control`) reports effective state + source
(default | global-config | env), so "why didn't the hook fire?" is answerable (D4).
- Node + Python parity (D6), with cross-runtime tests incl. the security negative
(a project-local .rafter.yml/.rafter/config.yml disable attempt is ignored).
- shared-docs/CONFIG.md: consolidated, code-verified config reference (global vs
project layers, trust boundary, toggle matrix mapping each switch to the code
that enforces it). CLI_SPEC + CHANGELOG updated.
Audit (sable-59s) also surfaced that agent.outputFiltering.redactSecrets/blockPatterns
are validated but never enforced (PostToolUse always redacts) — filed sable-y2z,
documented as a known gap in CONFIG.md.
rafter-secure-design gated; rafter-code-review CLEAN (trust boundary, fail-safe,
parity, granularity all verified). 39 new tests green both runtimes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hook off-switch (sable-bnl) + betterleaks TOCTOU fix (sable-t0q) + config audit & reference (sable-59s/29w). Versions bumped in node/package.json, python/pyproject.toml, and both rafter-security-skill.md frontmatter; CHANGELOG [0.8.6] dated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ssion) The off-switch change gave _evaluate_bash a required `control` param but only updated one of three test files that call it directly — test_hook.py and test_hook_integration.py (~20 call sites) broke with a TypeError, caught by the full Python CI suite. Default `control=None` to a fully-enabled HookControl: the production dispatch always passes a resolved control explicitly, so the default only affects direct test callers and is fail-safe (enabled). 101 hook tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Raftersecurity
approved these changes
Jun 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promote 0.8.6 to prod.
Highlights
RAFTER_DISABLE_*env vars or global~/.rafter/config.jsonagent.hooks.*. Honored only from trusted, machine-owner-owned sources — never project-local.rafter.yml(rafter-secure-design trust-boundary decision; rafter-code-review CLEAN).rafter agent statusreports state + source. Node + Python parity, incl. a security negative test.fs.mkdtempSyncprivate dir replaces a predictableDate.now()temp path; parity with Python.shared-docs/CONFIG.md(sable-59s/29w) — code-verified reference for the global vs project config layers, the trust boundary, and a toggle matrix mapping every switch to the code that enforces it. Audit surfaced a dead toggle (outputFiltering.redactSecrets/blockPatterns, sable-y2z — documented, not yet wired).Release mechanics
node/package.json,python/pyproject.toml, and bothrafter-security-skill.mdfrontmatter.[0.8.6]dated 2026-06-13.🤖 Generated with Claude Code