Files
gbrain/test/durability-cron.test.ts
T
7ea92d602c v0.42.48.0 feat(durability): auto-harden brain repos for git durability on PAT+URL (#2241)
* feat(git): divergence-safe pull, push-probe, default-branch detection for brain durability

Add GIT_ENV_AUTH + divergenceSafePull (skip-on-dirty, conflict-abort-clean,
never-mid-rebase), detectDefaultBranch, pushProbe, and an env-gated
GBRAIN_GIT_ALLOW_FILE_TRANSPORT escape hatch. Export GIT_ENV. pullRepo's
--ff-only contract is unchanged.

* feat(durability): brain-repo hardening core (hook, helper, cron, PAT, AGENTS rules)

hardenBrainRepo/unhardenBrainRepo: local untracked post-commit hook + committed
brain-commit-push.sh (one shared push-retry template), repo-scoped credential
with existing-helper reuse, push-probe verify, active-resolver-file rules with
taxonomy from _brain-filing-rules.json, minimal DB-free pull cron. PAT redaction
via redactSecretsInText.

* feat(sources): harden/pull/unharden commands + auto-harden on add --url

sources harden/pull/unharden subcommands; --pat-file/--no-harden on add;
auto-harden managed clones on add; unharden-before-remove. cli.ts pre-connect
early-exit for DB-free 'sources pull --path' (the cron entry, never opens PGLite).

* test(durability): unit + integration coverage for brain-repo durability

git helpers, core harden/unharden, hook+helper E2E (real background push),
cron generators. 41 tests across 4 files.

* chore: bump version and changelog (v0.42.48.0)

Brain-repo git durability: auto-harden a brain's working tree (local auto-push
hook, committed commit-push helper, always-on agent rules, DB-free pull cron,
repo-scoped credential, push-probe verify) the moment gbrain gets a PAT + URL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(sources): route harden exit code through setCliExitVerdict

A raw process.exitCode write is zeroed by the owned-verdict flush-exit
(#2084 PGLite-Emscripten pollution defense); cli-exit-verdict-pin guard
caught it. Use setCliExitVerdict(3) so 'sources harden' actually reports
needs-attention to cron/automation.

* docs: document brain-repo durability (KEY_FILES + multi-source guide)

KEY_FILES: extend git-remote.ts entry (divergenceSafePull, pushProbe,
detectDefaultBranch, GIT_ENV_AUTH, GBRAIN_GIT_ALLOW_FILE_TRANSPORT) + add
brain-repo-durability.ts/sources-harden.ts entry. multi-source-brains.md:
add a Durability (auto-harden) how-to covering sources harden/pull/unharden,
--pat-file, the guarantees, and the security posture.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 06:41:37 -07:00

45 lines
1.9 KiB
TypeScript

/**
* Durability cron generators (v0.42.44, D2 + D12): pure-string renderers.
* Asserts the cron is DB-free (gbrain sources pull --path, NOT `pull <id>`),
* secret-free, self-disabling, and that the launchd plist is periodic.
*/
import { describe, test, expect } from 'bun:test';
import { renderCronWrapper, generateBrainPullPlist } from '../src/core/brain-repo-durability.ts';
const TOKEN = 'ghp_SHOULD_NEVER_APPEAR';
describe('renderCronWrapper (D2 DB-free)', () => {
const w = renderCronWrapper('wiki', '/data/clones/wiki', 'main', '/usr/local/bin/gbrain', '/home/u/.gbrain/brain-push.log');
test('calls the DB-free path command, not the engine-opening one', () => {
expect(w).toContain("sources pull --path '/data/clones/wiki'");
expect(w).toContain("--branch 'main'");
expect(w).not.toMatch(/sources pull '?wiki'?(\s|$)/); // never `sources pull wiki`
});
test('self-disables when the captured checkout is gone', () => {
expect(w).toContain("if [ ! -d '/data/clones/wiki/.git' ]");
expect(w).toContain('path gone, skipping');
});
test('sources the shell profile (secret-free) and never bakes a token', () => {
expect(w).toContain('source ~/.zshenv');
expect(w.includes(TOKEN)).toBe(false);
});
});
describe('generateBrainPullPlist (D12 launchd)', () => {
const plist = generateBrainPullPlist('com.gbrain.brain-pull.wiki', '/home/u/.gbrain/brain-pull-wiki.sh', '/home/u', 1800);
test('is periodic (StartInterval), not a KeepAlive daemon', () => {
expect(plist).toContain('<key>StartInterval</key><integer>1800</integer>');
expect(plist).not.toContain('<key>KeepAlive</key>');
});
test('carries the per-source label and the wrapper path only (no secret)', () => {
expect(plist).toContain('<string>com.gbrain.brain-pull.wiki</string>');
expect(plist).toContain('/home/u/.gbrain/brain-pull-wiki.sh');
expect(plist.includes(TOKEN)).toBe(false);
});
});