mirror of
https://github.com/garrytan/gbrain.git
synced 2026-08-14 08:53:22 +00:00
b966d2682c93ba8bde1f8b5dc5fdde1ccdf5e73c
899
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b966d2682c |
test(openclaw): inspect runtime in plugin loader E2E (#3742)
Wave-assembled from PR #3742 by @arisgysel-design. Co-Authored-By: arisgysel-design <arisgysel-design@users.noreply.github.com> |
||
|
|
636628fdb2 |
fix(sync,import): global sync.* anchors only move for the brain repo they describe (#2114) (#3735)
Wave-assembled from PR #3735 by @smdesai27. Co-Authored-By: smdesai27 <sanilmdesai@gmail.com> |
||
|
|
cb07cfda8d |
fix(budgeting): add Sonnet 5, Fable 5, and Opus 4.8 to the synthesize context map and brainstorm output caps (#3727)
Wave-assembled from PR #3727 by @p3ob7o. Co-Authored-By: Paolo Belcastro <p3ob7o@users.noreply.github.com> |
||
|
|
9b6db85d39 |
fix(skills): advertise usable tools on stdio transport (#3725)
Wave-assembled from PR #3725 by @gregario. Co-Authored-By: Greg Jackson <gregj64@gmail.com> |
||
|
|
8d5bdfe125 |
fix(mcp): reject sources_add path on remote transport (#3724)
Wave-assembled from PR #3724 by @gregario. Co-Authored-By: Greg Jackson <gregj64@gmail.com> |
||
|
|
15ecc65b24 |
v0.45.7.0 feat(mcp,context): ambient recall — context_pack + delta frozen verbs + boundary runtime (#1) (#4028)
* feat(mcp,context): ambient recall — context_pack + delta frozen verbs + boundary runtime (#1) Two new frozen MEMORY_VERBS (context_pack, delta) on the pull surface + a Claude Code hook boundary runtime on the push surface, sharing one stateless assembler core (assembleTurnContext mode: turn|pack|delta) and a keyset session cursor (migration v126). World-only by default; include_private gated fail-closed to trusted-local. protocol_version stays 1 (additive 5→7 verbs). Survived three adversarial review waves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.45.7.0 feat(mcp,context): ambient recall — context_pack + delta frozen verbs + boundary runtime (#1) Two new frozen MEMORY_VERBS (context_pack, delta) grow the frozen set 5→7 without a wire bump — all seven stamp protocol_version: 1. context_pack assembles a deterministic, zero-LLM, budget-packed bundle (entity cards + open threads + hot facts) for a set of standing entities; delta returns only what changed since a timestamp for cheap heartbeats, with a per-session keyset cursor for at-least-once delivery. A boundary runtime wires these into Claude Code lifecycle hooks (SessionStart warm pack, PreCompact entity banking for post-compaction rehydration); Codex and any MCP host pull the same verbs at their own boundaries. World-only by default on all arms; include_private widens only for local trusted callers. Migration v126 adds session_context_state (additive). Includes the coverage close-out wave (~55 tests): real-serve compact→ session-start round trip over the live socket, --surface verbs stdio session pinning exactly 7 tools fail-closed, HTTP-transport verb calls with per-token cursor isolation, Postgres engine-parity for keyset pagination + the session-cursor table, migration v126 shape + rewind test, sub-second latency gates, CLI-level invocations, rendered-protocol boundary assertions, and a live-Codex boundary-call check. The wave caught and fixed three real bugs: the delta CLI wedging on first wake (floating GC promise racing engine teardown), the compact hook probing the PGLite socket on a Postgres config with a leftover database_path, and the verbs-surface banner hardcoding a stale verb count. Also the /document-release sweep: stale "five verbs" → seven across the protocol doc, README, INSTALL, DEPLOY, the Claude Code MCP guide, and the query skill; deferred scope filed in TODOS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(release): bump openclaw.plugin.json to 0.45.7.0 — the sixth version location The #4033 merge auto-resolved the OpenClaw plugin manifest at master's version while the trio moved to 0.45.7.0, failing the manifest drift test on CI shard 4. Register the file in CLAUDE.md's version-locations table (five → six) so every future ship and merge re-bumps it with the trio. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.45.7.0 |
||
|
|
3f22f51e5d |
v0.45.6.0 feat(skills): giftable-skills import wave — 17 new production skills + 8 upgrades (#4033)
* feat(skills): check-skill-refs gate + per-commit skills gate runner Three integrity checks over skills/ markdown: dangling refs (backtick paths, composes: frontmatter, RESOLVER dispatcher slugs), donor-remnant paths behind a ratchet allowlist, and warn-only CLI-reference drift against the live tool surface. Wired as check:skill-refs in verify and bundled into scripts/skills-commit-gate.sh (conformance+resolver tests, check-resolvable --strict, lock regen+freshness, refs, optional per-file privacy lint) for any commit touching the skills pack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skillpack): machine-readable plugin curation record + membership assertions skills/plugin-exclusions.json records why each manifest skill is not bundled into openclaw.plugin.json (Tier B host-only decisions, seeded from the pre-existing split). test/openclaw-plugin-manifest.test.ts now asserts manifest = bundled + exclusions with no overlap, no orphans, no stale exclusions, and a sorted skills array (two pre-existing sort inversions fixed). Bundling decisions are now review-visible: moving a skill between tiers requires touching both files in one commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): skillify reconciled with the evolved upstream line (v2.0.0) Adopts eval contracts, the NO-REGRESSION LAW, idempotency guidance, and the 14-item checklist from the upstream skillify line while preserving this repo's divergent content. bulk-ingestion (later in this wave) delegates its codify phase to this checklist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): book-mirror 0.5.0 backport Table-misalignment root-cause fix, anti-repetition hard constraints, and the resonant-stance rewrite from the upstream line, generalized onto native gbrain surfaces (no external storage libs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(conventions): test-before-bulk gains the progressive ramp + output-existence checks 10 -> 100 -> 500 -> full ramp with per-round stop gates, the count-before/count-after output-existence check (catches silent zero-row writes), the <2% error-rate gate, the copy-paste verification checklist, cost estimation, and pointers to native pace mode + progress flags so nobody rebuilds throttling in bash. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(conventions): regex-discipline — judgment vs heuristic epistemics The one-question test (deterministic-and-rote vs judgment), the earned- regex sequence, red flags/green lights, never-regex-what-an-attacker-can- imitate, and two genericized cautionary tales. Pairs with test-before-bulk: look before you compress, compress only the rote. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(conventions): path-discipline — display strings are not path strings Never pass a display-formatted link/URL to a file tool; a "successful" write to a poisoned path lies. Poisoned-path tells, verify-before-done, retry discipline, junk-tree recovery. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(conventions): exec-output — buffer to file, read bounded slices Empty exec results usually mean truncation, not a broken shell. The buffer-then-slice rule, shell patterns, and the diagnostic ladder, generalized to any harness. Conventions half of the durable-execution doctrine (the minion-orchestrator half lands separately). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): concept-synthesis 0.2.0 — curation cull phase Adds the keep/delete/merge muscle the synthesis loop lacked: minimum substance gates (style cannot rescue hollow ideas), weighted verdict bands with hard delete triggers, grounding labels wired to the citation conventions, a reputational-risk gate, cluster budgets, and reversible merge-with-backlinks with merge-count as an emergent-salience signal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): measure-before-you-fix — measure-first ops triage Stopwatch-verify before raising timeouts; act-line vs alert-line. Trigger vocabulary maps onto the shipped doctor/autopilot/sync alert classes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): data-loss-gate — confirmation gate for irreversible operations Recoverability checklist + alternatives + explicit-yes before any bulk delete, forget sweep, source/mount removal, or history rewrite. No category-level exception classes: disposability is a property of the inspected target. Routing-convention honesty: the native operation- boundary confirm remains a filed follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): fact-check — pre-publication claim verification Breadth gate: extract every verifiable claim, verify each against live sources with a confidence scale. Data-derived claims section carries the two laws: producer is never verifier (independent re-derivation via a different query path) and affiliation is not authorship (typed edges, not co-occurrence). Hard-block on unsupported claims. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): resolve-before-asking — exhaust the brain before interrupting the human Query, timeline, and every mounted source before asking; when asking is justified, ask WITH a hypothesis (pairs with ask-user). Includes the no-placeholders-at-ingest rule. Owns WHETHER asking is justified; query owns the lookup verb, brain-ops the read-write cycle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): brain-ingest-gate — ingest entry gate (no raw copies, entity resolution, dedup) No-raw-copies entry rule; the Named-Entity Resolution Gate (registry- first + alias expansion — a vector score is a floor for prose, never a gate for named things); the qualitative dedup decision tree (open and read the top hit before writing). The gate keeps full dedup responsibility: native import skips only matching frontmatter IDs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): correction-pipeline — trace corrections to the contamination source When the user corrects a fact: root-cause it (7-class taxonomy), fix the source surface (facts table via recall/forget/remember; pages via edit + re-sync; identity files via bootstrap templates), then run the propagation check. Never just note it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): company-brainify — personal-to-team brain sanitization Strip/keep tables, triage grep pipelines, verification greps, and the history-purge procedure for publishing a sanitized team brain from a personal one. Scan scope covers meetings, dailies, facts, and takes, not just people pages. History rewrite is gated through data-loss-gate with a mirror-clone backup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): citation-graph-ingest — typed inter-document citation edges Extract every inter-document reference from an ingested corpus and classify the edge type (overrules/distinguishes/relies-on/extends...), written via native link ops and queryable via graph-query. States plainly that natural-language relational retrieval walks a fixed edge- type set today; wiring these types in is a filed follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): two-tier-extraction — triage/deep-read model routing for archives Cheap-model triage pass, gate, then strong-model deep read for archive ingestion, expressed through the model-routing conventions. Deterministic privacy wall: named-entity and sensitive-pattern classes are stripped before any model call; ambiguous content fails closed to human review. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): brain-link-discipline — verified links in every deliverable Composite: same-message link rule, path derivation via git ls-files, push-before-link ordering, subagent-relay path rewrite, bulk-list formatting. Fallback chain: verified hosted-remote URL, then repo- relative path with a scope note, then publish output offered as an attachable artifact (never promised as a URL). Extends _output-rules.md with the verified-deliverable-link canon and the in-page (relative) vs in-message (absolute) scope split. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): draft-in-voice — memory-grounded ghostwriting from validated profiles Load a validated voice profile, obey its hard rules, produce 2-3 drafts, run the voice-fidelity self-check, never auto-post. Ships the build-a- profile appendix (corpus threshold, fingerprint schema, profile page schema with provenance/consent/versioning) so users without a donor profile aren't dead-ended. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): bulk-ingestion — the disciplined bulk pipeline lifecycle + manifest substrate Composite: the schema-trial-evaluate-codify-bulk-monitor lifecycle spine plus the manifest.json durable-state substrate (stable source-derived ids, derived-not-asserted status, idempotent workers, per-worker progress-file merge, never trust a subagent's 'done' — verify on disk). Codify delegates to skillify; execution to jobs/minions; ramping to test-before-bulk. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scripts): check-skill-refs reads cliHints from operations source --tools-json does not serialize every op's cliHints, so real commands (get, timeline, backlinks, graph, link) warned as unknown. The checker now also derives the known-command set from operations.ts cliHints names + aliases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): meeting-ingestion 2.0.0 — recorder-agnostic pipeline backport Normalized transcript record as the any-recorder contract, multi-meeting split detection, cross-recorder dedup, evidence-based speaker resolution (never guess; ASR-variant search-before-create), required page sections with explicit-reason-for-empty, claim-verification table, and the brain- contradiction consistency check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): meeting-ingestion 2.1.0 — verify-before-done phase Hard gate before reporting ingested: required sections have substance, every people/companies slug has a page + timeline backlink, speaker map resolved, every quote grounded verbatim in the transcript (deterministic sidecar checks where transcripts are retained), fabricated-attendee sanity checks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): meeting-ingestion 2.2.0 — sequence verify (chronology checks) Deterministic order lint before a meeting page ships: phase-inversion and impossible-transit checks, day-timeline gaps corroborated against `gbrain day`, with a pass/block/explicit-waive contract (waives logged as acknowledged, not resolved). Adds routing-eval fixtures for the upgraded skill. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): reports 1.1.0 — Actionability Gate Four link failure modes (Broken/Dead/Indirect/Missing) with the rule "a missing link is honest, an indirect link is a broken promise", the blocked-vs-warning table, retry loop as agent-run checks, and the prompt gate snippet. Canon lives in _output-rules.md; this section carries the taxonomy delta. Routing fixtures added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): briefing 1.3.0 — pre-briefing context pulls Salience, anomalies, personal recall, and hot-memory pulse pulls before composing the briefing, wired to the real CLI flags (anomalies uses --lookback-days, not the upstream --since form). Routing fixtures added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): daily-task-manager 2.0.0 — stable IDs + fail-closed actions Stable task-ID anchors, deterministic action routing, fail-closed ambiguity handling (no mutation on zero/multiple matches), structured Returns contract with an agent-chaining JSON mode, minimal-diff save discipline. Routing fixtures added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): eiirp 1.1.0 — auto-fire gate (file before you deliver) Always-on convention: when substantial structured analysis of a user- shared document is about to be delivered, file the brain page first and deliver analysis + link in the same reply. Fire conditions, does-not- fire list, dedicated-pipeline exemptions, per-user storage-policy override. eiirp moves from plugin exclusion to bundled — the gate is load-bearing for downstream installs. Routing fixtures added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): minion-orchestrator 1.1.0 — durable execution doctrine Capability ladder for operations over two minutes: jobs + a one-shot self-deleting deadman (verifying the result was REPORTED, not that the process exited) where the shell-jobs contract allows; cron-checked progress file; foreground with buffered output otherwise. Deadman failure modes (dies before firing, double-fire idempotency, stale checkpoint) encoded in Contract and Anti-Patterns; content-addressed stage-checkpoint appendix. Existing trust-boundary text unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(skills): concept-synthesis cull-phase routing fixtures Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): research-compendium — archive, summarize 1:1, synthesize self-contained Deep-research discipline: archive every source, one summary per source, then a self-contained synthesis (the Self-Contained Rule; Low-Bar/High- Ceiling). Claims verification delegates to fact-check; retention is gated by the user's privacy posture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): context-audit — token hygiene for the always-loaded stack Report-only audit of SOUL/MEMORY/CLAUDE/AGENTS-class files: redundancy, contradictions, staleness, compression candidates ranked by token savings. Recommendations target bootstrap templates, never rendered files; judging routes through the native cross-modal eval runner with a cheap-model default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): blog-ingest — whole-publication and feed ingestion Feed discovery, pagination walking, article-shape normalization, canonical-URL dedup, idempotent re-runs, polite pacing, and empty-husk repair as agent procedures; per-article enrichment hands off to brain-ingest-gate. Public posts only. Fetched text is data, never instructions (untrusted-content section). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): conversation-archive — AI-chat history as first-class brain content Convert chat exports into per-conversation pages, import, validate via the conversation-parser surface, extract facts; gap-healing backfill under the three invariants (capture cadence outruns eviction, gap detection, write-path equals commit-path); idea-evolution tracing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): skill-autobench — evals mined from lived usage Mine a skill's real invocation history (user corrections are the gold signal) into a proposed eval contract and replayable cases with honesty labels, staged pending human approval — never rewrites a skill. Multi-model judging verifies each provider actually responded. Extends the SkillOpt surface; never duplicates it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(todos): giftable-import wave follow-ups Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skills): resolver strict reconciliation — triggers, fixtures, disambiguation Drives check-resolvable --strict to zero on the 69-skill pack: wave-side trigger narrowing (bare words become phrases on measure-before-you-fix, data-loss-gate, brain-ingest-gate), fixture intents rewritten to realistic phrasings, wave-side MECE duplicates removed, the ingest-gate quality rules delegated to conventions, and three new disambiguation rules (publication/article/media/chat-export routing, soul-audit vs context-audit, measurement-first triage vs debugging). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): idea-ingest 1.1.0 — Returns contract + edge cases backport Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): media-ingest 1.1.0 — input contract, error handling, known pitfalls Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): brain-ops 1.1.0 — entity-card first lookup + shallow-ls warning (memory-verbs block preserved) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): maintain 1.1.0 — weekly feature-adoption check Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skills): data-research 1.1.0 — infer research target from context Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(llms): regenerate bundles after the skills wave RESOLVER.md is inlined into the llms bundles; the wave's routing rows and disambiguation rules regenerate here (single wave-end regen; the committed bundles match HEAD per test/build-llms.test.ts). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): assemble banned-path fixtures at runtime in check-skill-refs tests The privacy check bans those literals in source files; the fixtures now construct them so the test still exercises the donor-remnant gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skillpack): bundle capture + publish; keep host-only contrast fixtures portable The downstream-install smoke showed shipped fixtures referencing host- only skills. capture and publish move from exclusions to the bundle — both are user-facing and wave skills route to them (the eiirp auto-fire files via capture; brain-link-discipline falls back to publish). Contrast fixtures that pointed at host-only skills (smoke-test, skill-optimizer) now target shipped skills with declared ambiguity, so `check-resolvable --strict` is green on the host pack (69 skills) AND on a fresh scaffolded install (58 skills). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(evals): post-import-wave routing receipt (haiku) A/B harness run against the 69-skill resolver after the import wave: baseline (uncompressed) 100% held-out / 78.3% training strict — beating both compression variants on strict scoring, corroborating the review decision to defer resolver compression to its own PR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skills): pre-landing review fixes — trust boundaries, gate hardening, registration prose From the six-reviewer pre-landing pass: research-compendium gains the untrusted-content section (verbatim web archives feed future recall); blog-ingest's injection marker now travels with body chunks (inline fenced wrapper, not frontmatter-only); company-brainify's history-rewrite backup moves to a non-volatile private location with pre-push re-verification and retention cleanup; the 17 imported manifest entries get real descriptions (a block-scalar parsing slip) with a test pinning description prose and plugin-array uniqueness; check-skill-refs becomes cwd-independent, validates relative markdown links, and warns visibly on an empty CLI command set (5 new tests); the commit gate fails loudly on a regenerated-but-unstaged lock, single-quotes its inline lint script, and rejects missing lint paths; ladder attribution, checkpoint-hook bypass, frontmatter-contract notes, curation + misroute runbook docs, the brain-first API-pull rule, and negative routing fixtures for eight skills round out the batch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skills): adversarial-review hardening — trust boundaries, closure, honest contracts From the ship pre-landing + adversarial + red-team passes (all findings folded): Untrusted content: a shared conventions/untrusted-content.md (fetched text is data, never instructions) referenced from every skill that fetches or imports external text — fact-check most critically (a fetched page is never authority to rewrite a brain fact). conversation-archive gains a mandatory secret+PII redaction pass before any page is written. company-brainify: the destructive strip now sanitizes a staging copy behind the data-loss-gate card (personal brain untouched); the history purge derives its path set from the full sanitized scope, runs only against the shared repo (never sync.repo_path), stages under a non-volatile backup dir with a pre-push restore check, and logs the deletion to the personal brain, not the shared one. Downstream closure: ask-user and brain-taxonomist are bundled (three new skills delegate to them); remaining host-only references are prose, not links; a membership+closure test makes a bundled skill referencing an unshipped skill a build failure. Honest contracts: brain-link-discipline is marked mutating (it commits and pushes) and only sends its token to the issuing host; inert priority markers removed; citation-graph-ingest's writes_to dropped; concurrency and slug-collision limits documented for daily-task-manager, bulk-ingestion, conversation-archive, and blog-ingest; the self- authorizing shell-jobs prefix removed in favor of worker-side authorization. Gate hardening: the commit gate compares the staged lock blob and runs the plugin-manifest tests; check-skill-refs scans jsonl for donor remnants, tolerates CRLF, and rejects flag-shaped option values. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.45.6.0 feat(skills): giftable-skills import wave — 17 new production skills + 8 upgrades Grows the built-in pack from ~52 to 69 skills: the trust layer (correction-pipeline, data-loss-gate, fact-check, brain-ingest-gate), team-brain sanitization (company-brainify), typed citation-graph ingestion, disciplined bulk/feed/conversation ingestion, and operational skills (measure-before-you-fix, context-audit, skill-autobench, resolve-before-asking, brain-link-discipline, draft-in-voice, research-compendium), plus meeting-ingestion/skillify/eiirp/ minion-orchestrator/concept-synthesis/reports/briefing/daily-task-manager upgrades and a new skill-ref integrity gate + plugin-curation record. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(skillpack,doctor): skill currency + declared preconditions — the migration harness now examines skills The upgrade/migration path was DB-only for skills: schema migrations never touched them, and skill sync was manual/advisory with new skills hidden from the post-upgrade sweep. This makes skill currency and per-skill setup first-class. Currency (capability A): src/core/skillpack/skill-currency.ts classifies every bundled skill against the install as new / drifted / current, by each skill's OWN files (shared conventions on disk no longer misclassify a new skill as drifted). `gbrain skillpack status` reports it; `gbrain skillpack sync` scaffolds only the new ones and never overwrites local edits; the post-upgrade sweep surfaces new skills with the sync command; `gbrain doctor` gains a `skill_currency` check (WARN when new skills are available, drift stays advisory). Preconditions (capability B): a `requires:` frontmatter field (source / dir:<path> / config:<key> / pages:<n>), a pure DI-seam checker in src/core/skillpack/preconditions.ts, `gbrain skillpack setup <skill>` for the static list + hints, and a `skill_preconditions` doctor check that verifies them live against the connected brain. Annotated only the two skills with genuine preconditions (citation-graph-ingest needs a corpus; skill-autobench needs the conversations/ archive). Semantic skill search (capability C) is deliberately deferred as its own subsystem (tenancy + source isolation + embedding backfill) — filed in TODOS, not crammed in unreviewed. Tests: skill-currency, skill-preconditions, skillpack-status (31 new assertions). doctor + skillpack stay engine-safe (currency is filesystem- only; live precondition checks skip cleanly without a brain). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(doctor,skillpack): categorize new skill checks, refresh flag registry, sweep test + noise gate CI on the pushed feature surfaced three real in-branch failures: - doctor-categories: register skill_currency + skill_preconditions under SKILL_CHECK_NAMES (every doctor check must be categorized). - flag registry: doctor's import of findGbrainRoot pulls bundle.ts (which runs git rev-parse --verify) into its flag scan; regenerated the known-flags allowlist to match (harmless over-inclusion, per the generator's design). - upgrade sweep: header renamed to 'Skillpack sweep (post-upgrade)'; new-skill surfacing gated on the host having scaffolded at least one skill, so a zero-skill (opted-out) host stays silent instead of getting 'N new skills' on every upgrade. Tests updated + a case added locking the surfacing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.45.6.0 |
||
|
|
99dd1a083c |
v0.45.5.0 feat(autopilot): brain currency — honest daemon health, migrate quiesce fence, keyless scheduler chain (#4030)
* docs(designs): agent-bootstrap plan + design docs (normative, review-absorbed) The scrubbed, in-repo sources of truth for the gbrain bootstrap wave: AGENT_BOOTSTRAP_DESIGN.md (product scope/sequencing) and AGENT_BOOTSTRAP_PLAN.md (implementation; all review-finding IDs inlined). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): format spec, question bank, identity templates, bundled assets agent.json manifest (format_version 1, initialized sentinel) + machine-local install receipt [CX2-1, CX2-12]; 12-question/6-required interview bank with consent keys and a persist:false sink for the optional provider key [CX2-13]; ten {{TOKEN}} identity templates (generic, adapted to gbrain ops — gates call recall/query/put_page, write-through-ops rule, keyless agent-authored facts, silence contract); assets embedded compiled-binary-safe via file-type imports [ENG-6]. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): runbook, README paste block, bootstrap guide, TODOS entries BOOTSTRAP_FOR_AGENTS.md (agent-driven install runbook: CLI phase list is the source of truth, never-invent rules, Codex approvals preflight, keyless posture, failure-modes table, version stamp for the skew check); README gains the full-agent paste block pinned to latest-stable inside the Claude Code/Codex quick start (memory-only tier stays); docs/guides/bootstrap.md carries the full install/security/consent/degradation/uninstall contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(designs): spike instrument for the bootstrap wave (build order 0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): interview + render engines Interview gate with read-back confirm-hash (any later answer change clears the confirmation — the hostile single-batch case is structurally impossible), per-answer provenance, caps + escaping at set time, config-sink routing for the provider key; renderer with hard-fail token sweep, subordinate fencing of principal input, never-clobber + backups, deterministic minimal mode for the template repo, scaled byte floors. 58 unit tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): private-repo lifecycle — repo create, attach, uninstall, run lock gh-gated private repo creation with API-verified privacy (rate-limit distinct from public), refuse-foreign-origin with attach as the sanctioned path, atomic bootstrap mutex (pid liveness + age + token), receipt-keyed uninstall that never wholesale-deletes the gbrain home and only offers --delete-brain for a brain it created; read-only PGLite lock probe (never opens the engine). 54 unit tests, injectable exec seam throughout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(release): latest-stable ref, template-repo publish job, bootstrap CI guards release.yml advances the latest-stable tag only after assets publish (the paste block's permanent ref — copies in the wild never rot) and gains a PAT-gated publish-template job verified against the vendored tree; two skip-graceful guards (sanctioned-ref + runbook stamp; template/token bijection + placeholder assertion + generator byte-diff) wired into verify; README + runbook re-admitted to the CI cache hash; vendored deterministic template tree generated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(context): IPC v2 turn_context + 8KB assembly + visibility resolver + session identity Discriminated-union IPC with handler map, protocol echo (stale-serve detection), shared-secret gate, server-side source binding, per-kind budgets; turn-context assembly (reflex pointers + volunteered pages + world-only hot facts) under a data-not-instructions envelope trimmed to the harness's 10KB hook-output cap; facts.default_visibility resolved through one helper at all four sites (explicit caller wins, typos fail closed); typed sessionId threads _meta.session_id into the hot-memory cache key. 50 new tests; 180 adjacent tests confirmed green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(persistence): secret-scan, gbrain sources push, durability unification Pattern secret scanner (own runtime allowlist, redacted previews, corpus-write redaction mode); sources push runs the whole scan→stage→commit→pull→push sequence under one cross-platform lock (mkdir-atomic, pid+age+token) with a deny-glob backstop, commit-first divergence-safe pull, refuse-unverifiable visibility, and push-status telemetry; gbrain-home choke point unifies GBRAIN_HOME semantics with config (0700); durability is parent-repo-aware and rotates its push log at 0600. 35 new tests; 200 existing green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sources): harden/pull gates accept sources inside a parent git repo The bootstrap workspace registers brain/ (a subdirectory) as the source; the durability core already resolves the repo root, so the command gates now check inside-a-repo rather than .git-right-here [CX2-3]. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(serve): resident maintenance sweep + keyless capability probe The lock-owning serve process now closes the persistence loop: startup (3s post-connect, best-effort, unref'd) and idle (10-min quiet intervals through the injectable timer seam) sweeps run facts-fence reconciliation, deterministic link/timeline extraction over recent workspace pages, and spend-gated corpus ingest (skipped keyless — agent-authored fences cover it). gbrain sweep --once is the trusted CLI seam bootstrap verify uses. Capability probe renders the honest keyless/keyed report. Full reuse of the cycle extractor + extract cores; 26 new tests, neighbors green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(hooks): engine-free gbrain hook command, settings writers, transcript parser Four hook events (session-start digest + crashed-session recovery push, user-prompt turn-context injection under an 800ms deadline and the 10KB cap, stop buffers, session-end corpus write with redaction/retention/dedup + best-effort push); structural JSON settings merger keyed by a _gbrain marker (foreign hooks and permissions survive); dated host-spec registry; Claude Code .jsonl parser as a spec-target with a scrubbed 7-shape fixture. Heartbeat is counters-only by construction. 59 tests; zero engine modules in the import graph. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(bootstrap): cross-link the full-agent path from the connection docs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): dispatcher, verify, status — the command assembled gbrain bootstrap {status,interview,render,repo,hooks,verify,uninstall,attach}: engine-free except verify (owns its engine, in-process sweep — no live-serve conflict); phase list is the TS source of truth with install.jsonl telemetry and the support blob; verify's fail-soft check suite covers the real write path (put_page → write-through file → sweep → graph floor → recall), passes keyless, persists snapshots, and ends with the first-run tour. cli.ts wired per the three-touchpoint rule; doctor gains the bootstrap check group (silent on machines with no bootstrap state). 28 new tests; 353 adjacent green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: KEY_FILES bootstrap cluster + CLAUDE.md dispatcher row (+ build:llms) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bootstrap): e2e pins — hook-under-live-serve, attach, degraded modes, compiled binary, Docker harness The permanent pins: a real serve holds the PGLite lock while the engine-free hook completes (and a direct engine open provably throws LiveServeLockError); stale-socket fail-open; machine-2 attach with marker-keyed hook repair; decline-everything installs verify green with every degradation named; the compiled binary renders bundled templates in an empty cwd. Offline Docker harness (networkless, read-only) gated into heavy-tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(bootstrap): register doctor check categories + system-of-record allow comments The six bootstrap doctor checks join OPS_CHECK_NAMES; the sweep's batch link/ timeline inserts carry the explicit extract-path allow comments (the sweep IS the extraction path for workspace pages). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): shard wedge cap tracks suite growth (1500s -> 1800s) At ~9000 tests a healthy shard finished at 1466s and two progressing shards were false-killed at the old cap; 1800s restores ~25% headroom over the slowest observed healthy shard. Real hangs still hit it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(ci): cache-hash policy — README + runbook edits must invalidate [C2] The old deny-list assertion predates the paste block; README.md and BOOTSTRAP_FOR_AGENTS.md are policy-doc re-admissions now, so their edits must change the hash (a paste-block edit shipping under a cached green was the C2 hole). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): classify post-suite exit-hangs as warn-pass; file the leak forensics A shard killed by the wedge watchdog with every assigned file started and zero fail markers did all its work and leaked a handle at exit — pre-existing and master-reproducible (P1 TODO carries the full bisect forensics). Bun's per-test timeout turns a hung test into a (fail), so the classifier cannot mask one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): shard cap 2400s — the count-balanced heavy shard needs it under contention Observed: the heavy shard still progressing 22s before an 1800s kill while siblings finish at 1150-1550s (split balances file count, not weight). Filed the load-sensitive WAL-repair flake (pre-existing, master's v0.42.75.0 wave). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bootstrap): quarantine env-mutating suites to the serial lane check-test-isolation R1: six new files mutate GBRAIN_HOME/env at module scope — the serial lane (one process per file) is the guard's prescribed home for them. All 114 tests pass post-rename. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(readme): per-harness install sections — Codex, Claude Code, then OpenClaw/Hermes Each harness gets its own complete paste-block section (desktop app first, terminal noted — Claude Code CLI is the identical harness; Codex CLI works pull-based today); the OpenClaw/Hermes platform path keeps equal weight with its one-click deploys and INSTALL_FOR_AGENTS block intact; memory-only and remote-connect tiers consolidated under 'Lighter ways in'. Supersedes the review's D5 ordering by user direction; stale heading references updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(readme): Codex as the recommended first step; OpenClaw/Hermes framed as-intended, high-cost The install section now routes newcomers explicitly: Codex first (subscription-priced, nothing to deploy), OpenClaw/Hermes as GBrain used the way it was designed — always on, at real server + API cost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: doc-audit code chasers — broken recovery hints, stale op description, auto_link key Four small code fixes surfaced by the markdown accuracy audit: - doctor's auto-RLS recovery hint pointed at `apply-migrations --force-retry 35`, which cannot work (--force-retry targets the vX.Y.Z orchestrator registry, not the numeric schema MIGRATIONS array). Hint now points at the recreate SQL in docs/guides/rls-and-you.md; test pins against regression. - v0_11_0 migration printed the same broken-mechanism class of hint (`config set minion_mode` writes DB config nothing reads); now names `apply-migrations --mode` + preferences.json, the real setter. - submit_job's op description hardcoded a stale handler list; now points at registerBuiltinHandlers as the source plus the --follow discovery trick. - `auto_link` added to KNOWN_CONFIG_KEYS: read by link-extraction, reconcile-links, and sweep, and documented as the off-switch in brain-ops/maintain, but the allowlist rejected `config set auto_link false`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: repo-wide accuracy + MECE reform from the 9-bucket markdown audit A code-grounded audit of every markdown file (root, architecture, guides, mcp, tutorials, docs-root, operations/eval/designs, skills, recipes) followed by a fix wave with per-bucket ownership. Four classes of change: Accuracy — every documented command/flag verified against src/ before writing: dead commands replaced with working ones (pages purge-deleted, jobs watch --follow, gbrain restore, import-based Obsidian flow, space-separated --scopes, real thin-client recipes, working isolation verification, real supervisor restart procedure, curl-based ngrok health check, real minion_mode setter); count drift fixed with rot-proof phrasing (100+ ops, 50+ bundled skills via skills/manifest.json, 140+ engine methods, KNOBS_HASH_VERSION pointer instead of hardcoded versions); stale claims corrected (search-mode defaults, RETRIEVAL pipeline order incl. autocut, sentinel rules, refusal-list mechanism, engine snapshot, shard cap 2400s + EXIT-HANG classifier in TESTING.md, latest-stable + publish-template documented in RELEASING.md as release.yml promises). MECE — one home per concept, pointers elsewhere: test isolation → TESTING.md; OAuth registration + --bind/--public-url lore → DEPLOY.md; mode bundles → guides/search-modes.md (the home the CLAUDE.md dispatcher always promised); merge contract → schema-packs.md; WAL ladder → ENGINES.md; quiet-hours → quiet-hours.md; capture taxonomy → entity-detection.md; person-page taxonomy → compiled-truth.md; brain-first protocol → brain-first-lookup.md; refresh semantics → refresh-algorithm.md; KEY_FILES.md deduplicated (58 extension entries merged, one entry per file); infra-layer.md rewritten as a pointer page. Privacy — placeholder sweep across guides, docs, skills, and recipes per the iron rule; per-release narration stripped from reference docs (current-state prose only). Bootstrap coverage — AGENTS.md pointer, RESOLVER routing row, INSTALL.md path, tutorial cross-links, keyless-mode sections in spend-controls/headless-install. skills.lock.json regenerated; llms.txt/llms-full.txt rebuilt. Gates: verify 36/36, typecheck clean, doctor 96/96, skills-integrity + resolver + build-llms + config-set + migrations all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(readme): refresh production-brain stats to current brain-repo counts 155,795 pages / 24,589 people / 5,340 companies, counted from the brain repo's current HEAD; the "100K-page brain" framing moves to 150K to match. Cron-fleet count unchanged (its store lives on the deployment host, not in the repos available for verification). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scan,push): modern OpenAI/Voyage key patterns, scan staged blobs not disk - secret-scan matches sk-proj-/sk-svcacct-/sk-None- and pa- Voyage keys (the bare sk- pattern missed every current OpenAI key format). - workspacePush stages first, then scans the staged index blobs via git cat-file, closing the scan-then-stage TOCTOU where a file changed between snapshot and commit shipped unscanned. - shared binary-sniff helper, memoized glob regexes, atomic push-status write, and tests for pull_conflict + gitignored deny-match paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): regenerate flag registry for new commands, harden shard classifier + release token - cli-flag-registry.generated.ts regenerated: bootstrap/hook/sweep and sources push --message/--allow-unverified-remote were missing, so the strict #2185 validator rejected real invocations and skipped the new commands entirely. - EXIT-HANG shard classifier now requires every assigned file to have started before warn-passing a watchdog kill (was fail-open). - release.yml passes TEMPLATE_REPO_PAT via http.extraheader, off the argv. - compiled-binary e2e fails loud in CI instead of a silent permanent skip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(hook,sweep,ipc): non-blocking hook pushes, bounded sweep + cache, source-bound resolve - session-start/session-end no longer run synchronous git + inline push inside their self-deadline; a detached child does the push and the hook returns immediately (blocked Claude Code startup for minutes on a dirty tree before). - serve sweep drops the unbounded listAllPageRefs, resolves only candidate targets, claims corpus files atomically (no double-LLM-spend race), and caps the fence LIKE scan; heartbeat writes are O_APPEND with rare compaction. - hot-memory cache evicts expired entries and bounds entry count (the key is caller-controlled via _meta.session_id). - v1 resolve IPC honors boundSourceId like turn_context; turn-context runs its arms concurrently. doctor reads push/heartbeat thresholds from hook.ts. - new tests: doctor bootstrap checks, hook push-gate + deadline, concurrent sweep claims, cache eviction, bound-source resolve. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(bootstrap): origin-ownership gate, world visibility, collision-safe source id, consent + templates - repo adoption requires an exact receipt repo_url match or authed-owner check (undefined repo_url was a wildcard); create verifies privacy BEFORE the first push. - verify sets facts.default_visibility=world if unset, so agent-authored facts surface in per-turn context (they defaulted private before). - source_id derives a path-hash suffix when 'workspace' is taken by another checkout; every consumer reads manifest.source_id. - skipped HOOKS_CONSENT now declines (was falling through to default yes); --minimal refuses on an initialized manifest; tilde fences escaped. - MCP registration pins --surface full; status hard-fails a public origin (template door); receipt writers guard against newer/corrupt receipts; uninstall only claims brain-deleted after a real rm. - templates ship jobs disabled + provider-consent + support-relay lines; soul-audit re-runs over the shared interview bank. TODOS: 11 follow-ups. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(security): close adversarial-review findings — scan fails closed, whole-PEM redaction, bound repo push Cross-model adversarial pass (Claude + Codex) on the bootstrap wave: - secret scan fails CLOSED: an unreadable, oversized, or binary staged blob now blocks the push (blocked_unscannable, exit 5) instead of committing unscanned; only a confirmed staged deletion is skipped. This was the headline "block secrets before they leave the machine" property failing open. - private-key redaction spans the whole PEM block (header+body+footer), not just the header line — the base64 body no longer survives into the corpus the sweep sends to an extraction provider. - bootstrap repo commits the workspace (secret-scan-gated) before the first push and verifies the remote actually received it, so a push-fail retry can't adopt an empty remote as success. - privacy verify is re-bound to origin immediately before push (a concurrent origin rewrite between verify and push is refused). - session-end corpus write is atomic and clears the stale ingested/in-progress sidecars so a resumed session's appended transcript is re-ingested. - public-origin refusal enforced at render (not only status); MCP "already registered" is verified to target this workspace, not blessed blindly; verify probe cleanup scopes deletes to its own slugs, not a token substring; allowlist fingerprint floor raised 8→16 hex. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.45.0.0 feat(bootstrap): paste-in personal-agent install for Codex + Claude Code Turns a Codex or Claude Code session into a persistent personal agent: interview-rendered identity files, a local PGLite brain, per-turn context via serve IPC (Claude Code hooks / Codex pull protocol), session-triggered persistence, and a private GitHub repo as the agent's portable body. Keyless- first (the harness model is the LLM; one optional key adds embeddings + extraction). New `gbrain bootstrap` command family + `gbrain hook` + `gbrain sweep`; doctor bootstrap health checks; latest-stable distribution ref + template-repo publish job. Opt-in, additive — existing installs untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: regenerate flag registry for security-fix flags; drop fabricated gbrain capabilities doc ref CI caught two real failures under the merged state: - the flag registry lagged the blocked_unscannable/exit-5 flags the security round added, tripping the #2185 freshness guard. - headless-install.md described the keyless capability report as a `gbrain capabilities` command, which the #3502 doc-command resolver rejects — reworded to prose (the real surface is bootstrap verify's report). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: sync KEY_FILES + bootstrap plan to security-fix behavior Cross-referenced the security-fix round against the reference docs and corrected the drift those commits introduced: - workspace-push.ts entry: stage-FIRST-then-scan order (the TOCTOU fix), fail-closed blocked_unscannable, and the sources-push status -> exit-code map. - hooks.ts entry: MCP registration pins `serve --surface full`. - hook.ts entry: session-start/session-end pushes run in a detached child (non-blocking); atomic corpus write clears stale sidecars. - bootstrap.ts entry: render hard-refuses a public origin (template door). - verify.ts entry: source_id collision resolution (workspace-<path-hash>). - AGENT_BOOTSTRAP_PLAN as-shipped delta note for the scan/stage reorder. llms bundle unchanged (KEY_FILES is link-only); build:llms and test/build-llms.test.ts green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): silence SC2016 on the intentional askpass literal in release.yml The one-shot GIT_ASKPASS script must contain literal $1 and $TEMPLATE_REPO_PAT so they expand when /bin/sh runs it at git's credential prompt, not when the outer shell writes the file — single quotes are correct. Add a scoped shellcheck disable so actionlint passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skills): declare 'bootstrap my data' trigger in cold-start frontmatter The doc reform added 'bootstrap my data' to cold-start's RESOLVER.md row (to disambiguate data-bootstrap from agent-bootstrap) but not to the skill's own frontmatter triggers, tripping the RESOLVER↔frontmatter round-trip contract (resolver.test.ts). Declare it; regenerate skills.lock.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): default per-turn hooks + search mode ON without a prompt Installing gbrain for your coding agent IS the consent for the behaviors that make it work, so stop re-litigating them with install-time questions whose "no" defeats the product: - Per-turn hooks (Claude Code) install ON by default — no prompt. Off-ramps: `--no-hooks` at install, `GBRAIN_HOOKS=0` at runtime, `bootstrap uninstall`. The "hooks installed" line now surfaces the kill switch so default-on is never silent. A persisted HOOKS_CONSENT=no (interview --skip) still declines. - Search mode defaults to `balanced` silently (nobody knows the modes at install; `gbrain search modes` changes it any time). - MCP scope stays the ONE deliberate prompt — project vs user is a real cross-repo privacy choice, not friction. Marks the two consents `silent: true` in the question bank (new QuestionSpec field), rewrites the runbook phases so the agent no longer asks them, adds the `--no-hooks` flag (+ registry regen), and adds default-on / opt-out tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bootstrap): real end-to-end coverage — cross-session recall, per-turn content, Codex door, realistic corpus Closes the seven e2e gaps a coverage audit surfaced: the plumbing was well-unit-tested but the product claims ("Codex works, context shows up every turn with real content, it remembers across restarts, machine two recovers, Postgres works") were unproven end to end. Test-only wave — zero src changes. - Hermetic synthetic corpus (test/fixtures/bootstrap-corpus/ + a loader helper): 12 interlinked pages (52 edges, timelines), 12 world/private beliefs, 8 gold queries — curated from the gbrain-evals synthetic corpora, 100% placeholder names, so recall is asserted on a real multi-entity brain instead of a 2-node self-planted probe. - GAP1 magic moment: author a fact via the real write path, disconnect the engine, reopen against the same DB, recall it — a real session boundary, not verify.ts's same-connection SQL read-back. Plus a source-isolation assertion. - GAP2 per-turn content: hook-under-serve Pin 1 now seeds a known fact and asserts its text lands in the injected block AND private beliefs never do (was: empty brain, empty_block accepted as a pass). - GAP3 Codex door: assert the rendered AGENTS.md carries the Gate-3 brain-first pull protocol; make the fake codex shim implement `mcp get` so the [FIX7] target-verification can actually fail; the Docker cold-machine harness now exercises the hooks/MCP registration step instead of skipping it. - GAP4 corpus recall: turn-context + verify graph-floor/qrels run on the real multi-entity brain with real edges. - GAP5 attach: machine-two now re-ingests the cloned brain/ into a fresh DB and recalls a fact authored only on machine one — the multi-device payoff. - GAP6 keyed + Postgres (env-gated): real embeddings prove semantic recall a paraphrase query can reach but keyless BM25 cannot; bootstrap verify drives a real Postgres engine (skipIf DATABASE_URL/keys absent). - GAP7 persistence: session-end runs the REAL push (not the mocked seam) to a local bare remote and the remote receives the content; a planted secret is blocked at the gate; the 15-min cron installs and fires a scan-gated push. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bootstrap): real-agent e2e — drive the actual claude + codex binaries end to end Closes the audit's biggest gap ("no real harness ever drives a turn"). Adapts gstack's PTY/headless agent harness to prove the bootstrap install + smoke work against the REAL binaries, not PATH shims. Test/CI/docs only — zero src changes. - test/helpers/agent-harness.ts: hermetic clean-room child env (ported from gstack; drops CONDUCTOR_/CLAUDE_/GSTACK_/MCP_/GBRAIN_, promotes GSTACK_ANTHROPIC_API_KEY→ANTHROPIC_API_KEY), real-binary resolvers + auth probes, headless `claude -p --output-format stream-json` and `codex exec --json` turn runners, a gbrain stdio MCP-config writer, and a keyless brain seeder. + a fixture-parse unit test (no binary needed). - test/e2e/bootstrap-real-claude.serial.test.ts: real `gbrain bootstrap` install → REAL `claude mcp add` (verified via `claude mcp get`) → verify exit 0 → a real `claude -p --mcp-config --strict-mcp-config` turn that invokes mcp__gbrain__search and answers from the brain (proven: toolCalls include mcp__gbrain__search, final text carries the seeded fact). - test/e2e/bootstrap-real-codex.serial.test.ts: same install with REAL `codex mcp add` into a real ~/.codex/config.toml + Gate-3 pull-protocol assertion, then a real `codex exec --json` turn surfacing the fact (MCP or the pull- protocol shell path). Bounded retry absorbs codex's occasional MCP-call cancellation without softening the fact-requiring assertion. - Everything hermetic (temp HOME/CLAUDE_CONFIG_DIR/CODEX_HOME/GBRAIN_HOME; real ~/.codex auth copied read-only) and skipIf-gated so it self-skips cleanly where the binaries/auth are absent. - heavy-tests.yml: gated `real-agent-e2e` job (nightly/label, never the PR shard; no-op on a runner without authed binaries). - TODOS: compiled `gbrain` binary can't serve a PGLite brain (bun compile omits the WASM/extension payloads); harness falls back to `bun run` serve. Verified against live claude 4.6 + codex 0.147.0: 15 pass / 0 fail; verify 36/36; typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): real-agent-e2e job — bash array + --timeout (actionlint SC2086 + bun-test-timeout guard) The real-agent-e2e job's file loop used an unquoted $FILES (SC2086) and ran `bun test` without --timeout (check-bun-test-timeout guard). Switch to a bash array and add --timeout=600000 (real-agent turns are slow; the tests self-skip without authed binaries so it's a no-op elsewhere). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pglite): embed WASM + extension assets so the compiled binary can serve A `bun build --compile` gbrain binary could not `serve` a PGLite brain: the compile bundles JS but not PGLite's runtime payload (pglite.wasm, initdb.wasm, pglite.data, vector/pg_trgm tarballs), so `serve` on PGLite died with a bunfs/ENOENT. Now the assets ride inside the binary. - src/core/pglite-embedded-assets.ts: embeds the five assets via `import … with { type: 'file' }` (the ENG-6 idiom) and exposes getEmbeddedPgliteOptions() → { pgliteWasmModule, initdbWasmModule, fsBundle, extensions:{vector,pg_trgm} }. WASM/fsBundle are consumed as bytes; the two extension tarballs are materialized to a content-addressed temp file (atomic, size-verified reuse) because PGLite reads them via fs.createReadStream, which cannot read a /$bunfs path. Unconditional (works in bun-run and compiled), so no fragile mode branch. - src/core/pglite-engine.ts: static-import getEmbeddedPgliteOptions (engine path stays static per the engine-dynamic-import invariant); spread into both PGlite.create sites (initial + WAL-repair retry). The bunfs classifier stays as a backstop but no longer fires for a correct binary. - scripts/check-pglite-embedded.sh (+ smoketest): compiles a focused binary and asserts it boots PGLite, CREATE EXTENSION vector/pg_trgm, and round-trips a page — wired into `bun run verify` (now 37 checks), check:all, and check:pglite-embedded. Fail-soft only when compile is unavailable. - agent-harness.ts probeCompiledPglite now passes → the real-agent e2e uses the fast compiled MCP server. TODOS: the P2 "can't serve PGLite" item is closed. Verified: fresh compiled binary ran `search`/`query` against a PGLite brain and returned the seeded row (no bunfs/ENOENT); verify 37/37; pglite-engine 120/0 source-mode; typecheck clean; engine-dynamic-import + parity guards pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(source-health): wall-clock ceiling for content-relative staleness A source whose stored content timestamp trailed its last sync reported zero lag forever — the shape that let a dead sync daemon stay invisible for 71 days. lagFromContentMs now ramps toward stale past a ceiling (default 72h, GBRAIN_STALENESS_CEILING_HOURS / GBRAIN_SYNC_FRESHNESS_FAIL_HOURS) instead of returning a flat 0; ramping rather than stepping keeps the warn tier firing before the fail tier across sync_freshness, federation_health, and gbrain status. The ceiling resolves once per report (hoisted out of the per-source loops), and the numeric env plumbing moves to a shared warn-once resolver (src/core/env-number.ts) so doctor and source-health share one memo instead of warning twice. Doctor's wedged-lock alarm prints minutes below one hour so a fractional ceiling override cannot produce a "held the lock for 0h" message. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(autopilot): heartbeat-truth status, self-disable guard, migrate quiesce fence Status becomes honest and engine-free: a pure classifier maps heartbeat age, markers, and install detection to not_installed/disabled/paused/ never_run/stale/fresh with exit codes 0/1/2, dispatched in cli.ts before connectEngine so a live PGLite daemon's exclusive lock or a DB outage cannot take down the alarm. Tolerance is six intervals (the adaptive scheduler sleeps two between ticks on healthy brains; three flapped false alarms), NaN-proof at both parse sites, and a cron'd status monitor line no longer reads as an install. The generated wrapper self-disables for real when its repo vanishes — marker first, then supervisor-specific stop (launchctl bootout / systemctl disable) since a bare exit 0 under KeepAlive is just a quieter respawn loop — and requires three consecutive misses so a slow-mounting volume cannot permanently kill a healthy install. Install and uninstall clear both markers; wrapper artifacts resolve through gbrainHomePath with GBRAIN_HOME baked in at install time. gbrain migrate quiesces the whole write plane for its copy window: an unconditional atomic pause-marker claim doubles as the migration mutex (a live holder, a foreign hold, or an unwritable marker all abort; orphan adoption is renameSync compare-and-claim), in-flight writers drain via the DB lock table plus active-job count (supervisor presence locks excluded) before enumeration, minion workers stop claiming queued jobs while the marker is parked, cleanup registers at claim time, the daemon self-clears a dead migrate's orphan, and after a clean config flip the daemon detects the engine identity change and relaunches onto the new engine instead of syncing into the abandoned one indefinitely. Malformed connection URLs classify as operator config verdicts rather than crashes, and torn config reads cannot spuriously restart the daemon. Marker paths, the launchd label seam, and the pid-ownership primitives live in the new leaf module src/core/autopilot-paths.ts so migrate and the worker never import the autopilot command surface (flag-registry pooling); the regenerated registry reflects the new one-level imports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(embed,sync): keyless stale embed refuses cleanly; typechange statuses; test-home isolation The documented agent-scheduler chain (sync, then a stale embed) exited 1 on every keyless brain because EmbeddingDisabledError fell through to a blanket exit. A bare stale run now refuses cleanly via the pure isKeylessStaleRefusal predicate (stderr hint, zero-failure result); explicit asks — a slug, a slugs list, the all flag — still exit 1, mirroring the dispatch precedence where a slugs list wins over stale. buildSyncManifest handles the T/U/C git statuses instead of silently dropping them (typechange routes to modified; the file-to-symlink staleness residual is documented and filed). Status reports gain hours_since_last_sync so the ceiling-ramped staleness_hours stops doubling as the human-facing wall-clock number. The sync-failures ledger honors GBRAIN_SYNC_FAILURES_DIR and the test preload (bunfig) points it at a scratch dir, so test runs stop writing failure rows into the developer's real brain home. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(e2e): real-launchd lifecycle + agent-scheduler contract harness tier Two serial-lane e2e files that assert behavior, not artifacts: a launchd lifecycle suite (shimmed supervisor on every platform; REAL launchd on darwin under a per-run unique label, own-label cleanup only) driving install, self-disable via three strikes, status exit codes, reinstall, and uninstall through the actual CLI; and an agent-scheduler contract suite that runs the documented sync-and-embed shell chain end-to-end against a keyless brain, including the pull-failure case that must break the chain and the heartbeat that must advance on quiet re-runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: KEY_FILES current-state for the brain-currency wave; BRAIN_CURRENCY design doc Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(migrate,minions): ownership-conditional pause release + post-claim fence re-check Round-4 adversarial hardening on the quiesce protocol. Orphan adoption verifies identity AFTER its atomic rename — a racer that completed the whole adopt-and-rewrite in the read-to-rename gap gets its live marker restored no-clobber instead of stolen. Marker release compares content before unlinking, so a claim that changed hands can never delete the new owner's fence mid-copy. On the worker side, a job whose claim committed after migrate's drain probe is released back to the queue un-run (delayed one minute, conditional on our lock token) instead of executed into the doomed source engine. The cross-host residual — a worker on another machine never sees a local marker file — is filed as the DB-visible-pause follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(autopilot): migrate the crontab wrapper-detection pin to the pure seam The inline crontab.includes check this static pin matched became crontabIndicatesAutopilotInstall; the pin now exercises the function directly (wrapper line still detected) and asserts the detect path is wired through it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.46.0.0 feat(autopilot): brain currency — honest daemon health, migrate quiesce fence, keyless scheduler chain VERSION + CHANGELOG + TODOS for the brain-currency PR-A wave, plus the version-stamped bootstrap runbook and vendored template regen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: sync documentation for v0.46.0.0 The brain-currency wave shipped new user-facing surface the guides did not carry: the trustworthy autopilot --status exit codes (0/1/2 + paused), the keyless-safe sync-and-embed chain, and the migrate quiesce fence. - docs/guides/live-sync.md: keyless clean-refusal note on the embed --stale primitive; new "gate on the daemon's heartbeat" verify item with the exit-code contract. - docs/guides/cron-schedule.md: the recommended autopilot health-gate cron line (the status classifier already special-cases this monitor line so it never reads as an install). - docs/ENGINES.md: migrate --to now documents the pause-marker mutex, worker job-pickup fence, DB-lock drain (GBRAIN_MIGRATE_QUIESCE_SECONDS, default 300), crash-safe release, and daemon relaunch after the flip. - docs/operations/headless-install.md: keyless mode's "refuse cleanly" claim made concrete — the documented chain exits 0 on a bare stale embed. - INSTALL_FOR_AGENTS.md Step 7: health-gate bullet + keyless-safe chain note. - skills/maintain/SKILL.md: autopilot check now teaches the exit-code semantics and the --json report fields. - docs/TESTING.md: inventory entries for the two new harness-tier serial suites (real-launchd lifecycle, agent-scheduler shell-chain contract). - llms-full.txt regenerated (TESTING.md is inlined in the bundle). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: cross-model doc-review fixes for v0.46.0.0 Round two from the post-ship documentation review, checked against the code: - CHANGELOG.md: two shipped fixes were missing from the 0.46.0.0 entry (git typechange/unmerged statuses no longer dropped by sync; a wedged lock holder past the staleness ceiling now fails doctor with the break-lock remedy), and the env-resolver bullet over-claimed its coverage — narrowed to the doctor + staleness-threshold vars it actually serves. Additive + accuracy only; no entries replaced. - docs/guides/live-sync.md: Tricky Spots item for the staleness wall-clock ceiling (GBRAIN_STALENESS_CEILING_HOURS, default 72h, ramp-not-step) and the hours_since_last_sync status field. - docs/ENGINES.md: the quiesce paragraph over-stated marker release — now describes the pid-checked orphan adoption, the daemon self-heal poll, and the confirm-the-pid manual step for daemonless hosts. - docs/guides/minions-fix.md: the verify block's bare autopilot --status now notes the exit code is the verdict (and the set -e caveat). - llms-full.txt regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.45.5.0 chore(release): re-version 0.46.0.0 -> 0.45.5.0 Same release content; the version slots into the 0.45.x line per the natural-next-off-master convention. VERSION, package.json, CHANGELOG header, bootstrap runbook stamp, vendored template regen, lockfile, and llms bundles all move together (trio audit green; stamp + template + llms freshness guards re-run green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(skills): refresh skills.lock version stamp for 0.45.5.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.45.5.0 |
||
|
|
6fae2c10ff |
v0.45.3.0 fix(bootstrap): never offer MCP project scope on Codex — consent-flow honesty (#4029)
* fix(bootstrap): codex scope-note guard + scope-aware wire hint
runHooks: on --harness codex, print a stderr note when an explicit,
non-skipped MCP_SCOPE=project answer is persisted (raw readInterviewState
read, not the project-defaulting consentAnswer resolver) — reachable via
attach from a Claude Code machine or a pre-fix install. consentAnswer is
now shape-tolerant: a hand-edited non-string answer value falls through to
the bank default instead of throwing at a toLowerCase call site.
status.ts: the wire-phase resume_hint states the scope rule (Claude Code
consent, phase 3; Codex always user-global — no scope flag).
Tests: 7-case branch matrix on the note guard + wire-hint pin.
* fix(bootstrap): harness-scope the MCP consent prose (never offer project scope on Codex)
Runbook: the scope consent moves to phase 3 (Claude Code only, recorded with
interview --set BEFORE the read-back so the confirmation covers it — a
wire-time set clears the A8 confirm and regresses status); phase 6's Codex
bullet gains the counter-signal: Do NOT offer an MCP scope choice — codex
mcp add has no scope flag, registrations are always user-global.
Bank: MCP_SCOPE.question gets a read-aloud-safe "(Claude Code only. ...)"
prefix and phase moves wire -> interview to match.
Templates: ACCESS_POLICY's scope section becomes two static harness
paragraphs; CLAUDE.md stops conflating directory-based identity loading
with MCP scope. Vendored template-repo regenerated.
* chore(ci): pin the harness-scoping counter-signals (check-bootstrap-templates §e)
Three SKIP-GRACEFUL tripwires: the runbook must carry "Do NOT offer an MCP
scope choice" and "Claude Code only"; questions.json's MCP_SCOPE.question
must start with "(Claude Code only". Guard-test fixtures gain a compliant
MCP_SCOPE entry + pass/fail cases for each pin.
* docs: Codex user-global scope caveats + A8 consent-semantics follow-up TODO
Install table and KEY_FILES hooks entry now state the split: Claude Code
takes --scope (project default); Codex has no scope flag. TODOS gains the
deferred structural question (consent keys vs the A8 confirm gate).
* fix: pre-landing + red-team review hardening
Guard §(e): a valid-JSON bank missing its questions object now FAILS (it
silently passed both §a and §e); the pin also asserts MCP_SCOPE.phase is
'interview' so the schema half of the fix can't silently revert. Fixtures
for both + entry-vanished. consentAnswer says so on stderr when it discards
a malformed answer value (a silent fall-through could flip a damaged opt-out
to the permissive default) — pinned by a claude-code matrix case asserting
the receipt. Off-ramp texts gain the narrow `codex mcp remove gbrain`
alongside full uninstall (note, runbook, ACCESS_POLICY + vendored copy).
Cross-refs de-ordinaled (phase names, not numbers — master renumbers steps).
TODOS A8 entry gains the healing half (status can't distinguish consent-key
invalidation from tampering).
* fix: adversarial-review hardening — fail-closed consents + honest scope prose
consentAnswer: a present-but-unusable answer (non-string, empty, bare {})
now fails CLOSED to 'no' with a stderr note — a bank-default fall-through
could flip a damaged opt-out into consent (cross-model finding); 'no' is
every consent key's safe reading (no hooks, no cron, project scope). Pinned
by a malformed-HOOKS_CONSENT matrix case asserting hooks are declined.
Codex scope note: names the safe remediation (--skip MCP_SCOPE + reconfirm;
never --set user — the answer git-syncs to paired Claude Code machines and
would widen their scope) [codex review P2]. Prose states the user-global
registration's read+write reach honestly (runbook, ACCESS_POLICY + vendored).
Interview resume_hint carries the consent-recording step (the CLI channel a
resumed install actually reads). Guard §(e): unparseable questions.json now
FAILS instead of passing silently. TODOS: P1 filed for stdio dispatch scope
parity (pre-existing, surfaced by the adversarial pass).
* chore: bump version and changelog (v0.45.3.0)
Runbook stamp + vendored template-repo regenerated to match (the two
version gates check:bootstrap-tag and check:bootstrap-templates enforce).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: update project documentation for v0.45.3.0
KEY_FILES.md: bootstrap.ts entry carries the fail-closed consent
resolution + Codex stale-scope-answer note; CI-guards entry adds the
check-bootstrap-templates §(e) harness-scoping counter-signal pins.
bootstrap.md: degradation matrix Codex row states the no-scope-flag
reality (registrations are user-global). llms bundles regenerated
(byte-identical — these docs are linked, not inlined).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: cross-model doc-review fixes for v0.45.3.0
CHANGELOG: Codex off-ramps are removal commands, not narrowing; the
refresh recipe now covers both harnesses and names the re-confirm step
for pre-fix installs whose confirmation was invalidated. Runbook: the
user-scope tradeoff says read and write, matching ACCESS_POLICY.
bootstrap.md: degradation-matrix Codex row rephrased (lose the ability
to confine reach, not the reach itself). KEY_FILES: the section-(e)
description no longer implies placement pinning.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
v0.45.3.0
|
||
|
|
a996e42856 |
v0.45.2.0 fix(bootstrap): create-repo-first repo adoption + hardening (#4024)
* fix(bootstrap): harden create-repo-first repo adoption `gbrain bootstrap repo` adopts an empty, private, personally-owned GitHub repo the human created (create-repo-first), instead of only ever creating one. This hardens the existing adoption branch: - Empty-only adoption + pending_repo_url proof: a non-empty origin is refused (ORIGIN_NOT_EMPTY) unless it matches this workspace's pending marker (our own interrupted push). Never adopts a user's existing project from a git-ancestry guess, and never silently no-ops without pushing. - Repo-local git identity is set on the adopt path too (fresh-machine commits). - repo_url is recorded only AFTER a successful push (pending marker before); a failed push no longer looks "done" to `bootstrap status`. - Pre-push secret scan also covers an already-committed tree; ls-files failure fails closed. - assertOriginMatches binds BOTH the fetch URL and a configured push URL to the verified-private repo, so a foreign pushurl can't leak the workspace. - disposition: 'created' | 'adopted' | 'reused' replaces the overloaded flag. - Hook push-gate: the no-daemon session-end / recovery push is deferred until the repo phase records repo_url AND the current origin still matches it, so nothing is published to an unverified or redirected remote. Adds ORIGIN_NOT_EMPTY / REMOTE_CHECK_FAILED error codes. * docs(bootstrap): lead with the repo, document create-repo-first README (Claude Code + Codex) now opens with "the folder you open becomes your agent's private repo" and adds a "prefer to make the repo yourself?" callout for the create-repo-first path (empty, personal-account repo). Updates the bootstrap guide, the Claude Code MCP note, and the KEY_FILES / AGENT_BOOTSTRAP_PLAN invariants to describe adoption instead of "foreign origins refused". * v0.45.1.0 fix(bootstrap): create-repo-first repo adoption + hardening Bumps VERSION/package.json to 0.45.1.0, adds the CHANGELOG entry, refreshes the runbook + template-repo version stamps, and regenerates the llms bundle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(todos): file P2 follow-up — index-blob secret scan for bootstrap pushes * ci(gitleaks): run the free CLI instead of the license-gated v2 action gitleaks-action@v2 now enforces a paid GITLEAKS_LICENSE and fails the job ("missing gitleaks license") for accounts it can't validate over the API — blocking every PR's merge gate. Replace it with the open-source gitleaks CLI (pinned 8.30.1, checksum-verified against the release's own checksums file), scanning the PR/push commit range with the committed .gitleaks.toml allowlist. Same secret-scan coverage, no license wall. * v0.45.2.0 chore(release): re-bump 0.45.1.0 -> 0.45.2.0 Re-target the release version at the user's request. Updates VERSION, package.json, the CHANGELOG header + self-repair block, the runbook + template-repo version stamps, the TODOS follow-up reference, and the llms bundle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(perf): raise entity-card ratio ceiling 50x -> 100x (CI flake) The RATIO GUARD asserted entity p99 <= 50x max(getPage p50, 1ms). On a fast runner getPage p50 floors to 1ms and a normal entity p99 (~50ms) reads as ~52x, tripping the gate even though absolute p99 (52ms) is well under the 100ms budget — a p99 tail divided by a sub-ms median. At the 1ms floor, 50x also made the ratio STRICTER than the test's own 100ms absolute budget. Raise the ceiling to 100x: still far below the >=200x O(N)-regression signal the guard exists to catch, and consistent with (never stricter than) the absolute budget. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>v0.45.2.0 |
||
|
|
c2cc8b0207 |
v0.45.1.0 feat(context): hook-lane feedback loop + cross-turn dedupe for the harness hooks (#4025)
* refactor(context): extract pure gateVolunteeredPointers (no behavior change) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(context): volunteer-shaped resolve IPC — channel/cwd/volunteer fields, handler-result widening, server-side gate + validated source + delivery-point logging; listener on both engines Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(cli): gbrain volunteer-hook — one-shot harness hook entry (IPC-first; Postgres-only fallback; startup-hook-exempt) T0 evidence: a UserPromptSubmit hook's additionalContext is recorded in the session transcript as a structured hook_additional_context attachment and transcript_path is stable across turns — cross-turn dedupe reads ONLY those attachments (never raw tail substrings). Live smoke: seeded brain + running serve → pointer injected via IPC at 0.43s wall, turn 2 suppressed, one event banked channel=claude-code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(recipes): claude-code-reflex + codex-reflex (experimental) hook adapters Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(doctor): volunteer_channels per-adapter visibility Engine-aware sibling of retrieval_reflex_health: groups context_volunteer_events by channel (7d) so operators can see which push adapters fire; message separates installed-but-unregistered from registered-but-quiet; pre-v117 tolerant; info-only status. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(context): hook-lane feedback loop + cross-turn dedupe — converge cathedral-3 onto the #3975 harness hooks The #3975 hook lane volunteered pages invisibly (nothing logged to context_volunteer_events) and re-volunteered the same page every turn an entity was mentioned (no priorContextText). This convergence ports the cathedral-3 branch's two gap-fills into the blessed lane and retires its parallel surface: - transcripts: parseTranscript extracts injectedContextBlocks (structured hook_additional_context attachments — the T0-verified recorded shape of our own injections; never raw-turn substring matching) - hook user-prompt: feeds those blocks as priorContextText (volunteer once per session, not once per mention) + sends a feedback-loop channel (--harness <claude-code|codex>, default claude-code); hook joins STARTUP_HOOK_SKIP_COMMANDS (per-prompt check-update spawn class, grep-pinned) - turn-context: result exposes post-trim volunteered survivors - resolve-ipc: additive TurnContextRequest.channel + onTurnContextDelivered seam (fires post-write only — abandoned blocks are never counted) - serve: delivery callback logs volunteered pages + pointers per channel - doctor: volunteer_channels check (per-channel 7d visibility; separates installed-but-unregistered from registered-but-quiet; pre-v117 tolerant) Retired (never released): gbrain volunteer-hook, hook-adapters.ts, claude-code-reflex + codex-reflex recipes, the volunteer-shaped resolve wire extension. Kept: gateVolunteeredPointers extraction, VolunteerChannel claude-code/codex values, the real hook-transcript fixture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: regenerate llms bundles for the hook-lane doc updates Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: coverage for the hook-lane delivery wiring Extract logTurnContextDeliveryFireAndForget into volunteer-events.ts so the shipped serve callback is unit-testable (channel fallback, sessionId clamp, pointer+page rows in ONE synchronous registration — the double dynamic import could register the pending write too late to survive process exit). Direct units for isVolunteerChannel, explicit-channel logDeliveredReflexPointers, and the pure gate (idempotency pin; stale parity claim in its doc corrected). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: pre-landing review fixes — 7-reviewer batch Multi-confirmed: volunteer_channels now runs on the LOCAL doctor path too (buildChecks; docs pointed there); transient DB errors no longer misreported as pre-v117; priorContextText deduped + 32KB-capped hook-side. Red team: delivery counts reconciled against the hook heartbeat (CAUTION when mostly degraded — write-accept isn't injection); IPC clamp drops advisory priorContextText BEFORE any window turn; foreign hook_additional_context blocks (no gbrain marker) excluded from dedupe input; engine-aware quiet guidance (Postgres brains aren't told to chase hook registration); 2MB dedupe horizon documented. Security: wire channel claims restricted to harness channels. DRY: VOLUNTEER_CHANNELS single source, shared reflexPointerRationale, SESSION_ID_MAX_LEN shared with the op clamp; dead channel param dropped from logDeliveredReflexPointers. Tests: budget-trim volunteered invariant under real trim pressure, poll/ordering-based delivery-seam tests (no fixed sleeps), doctor branch + engine-parity-row tests, wiring source pins, cap pins; fixture pruned to load-bearing lines (environment inventory removed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: adversarial-review fixes — cross-model P1 + Claude adversarial batch P1 (codex structured review + Claude adversarial, cross-model): the remote run_doctor path now threads the caller's source scope into volunteer_channels — a source-bound token can no longer see other sources' activity counts/timestamps (must-never-violate source-isolation rule); local trusted doctor stays brain-wide. Claude adversarial: IPC connection handler gains a one-request re-entrancy guard (trailing bytes mid-await double-processed the same line → duplicate handler work + duplicate delivery logging); partial client-side trims now heartbeat as reason 'trimmed' (delivery-count drift visibility); heartbeat reconciliation uses the same 7-day window as the counts + a 5-sample floor; one oversized dedupe block no longer evicts all smaller ones (continue, not break); quiet-channel guidance no longer claims old serves attribute hook traffic to reflex (they log nothing); 'transient' claim softened to evidence; marker-filter + unknown-channel-default comments state their honest limits. Pins: scoped-query SQL/params, re-entrancy single-fire, updated wiring grep. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump version and changelog (v0.45.1.0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: restamp bootstrap runbook + regenerate vendored template repo for v0.45.1.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: update project documentation for v0.45.1.0 Post-ship /document-release pass: fold the post-doc review fixes into the reference docs (KEY_FILES: source-scoped remote volunteer_channels + heartbeat reconciliation, 32KB-capped dedupe input, marker filter, IPC one-request guard + clamp order), fix BRAINBENCH's now-stale memoryless-hook rationale, and mark the doctor-check half of the push-context TODO shipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: cross-model doc-review fixes for v0.45.1.0 Codex doc review against the shipped diff: push-context channel table gains the harness-hook row (three-channels claim was stale), the guide states the PGLite-socket topology limit and the bounded dedupe horizon, delivery-point claims are hedged to match the code (write-accept isn't proof of injection; the doctor reconciles against the hook heartbeat, which can't inspect registration), BRAINBENCH's future-tense hooks-PR sentence updated now the hook has landed, and the adapter-flip + resolver-channel TODOS reflect the current lane count. llms-full.txt regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(hook): document --harness in the user-prompt usage text Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.45.1.0 |
||
|
|
d35c9c9e44 |
v0.45.0.0 feat(bootstrap): paste-in personal-agent install for Codex + Claude Code (#3975)
* docs(designs): agent-bootstrap plan + design docs (normative, review-absorbed) The scrubbed, in-repo sources of truth for the gbrain bootstrap wave: AGENT_BOOTSTRAP_DESIGN.md (product scope/sequencing) and AGENT_BOOTSTRAP_PLAN.md (implementation; all review-finding IDs inlined). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): format spec, question bank, identity templates, bundled assets agent.json manifest (format_version 1, initialized sentinel) + machine-local install receipt [CX2-1, CX2-12]; 12-question/6-required interview bank with consent keys and a persist:false sink for the optional provider key [CX2-13]; ten {{TOKEN}} identity templates (generic, adapted to gbrain ops — gates call recall/query/put_page, write-through-ops rule, keyless agent-authored facts, silence contract); assets embedded compiled-binary-safe via file-type imports [ENG-6]. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): runbook, README paste block, bootstrap guide, TODOS entries BOOTSTRAP_FOR_AGENTS.md (agent-driven install runbook: CLI phase list is the source of truth, never-invent rules, Codex approvals preflight, keyless posture, failure-modes table, version stamp for the skew check); README gains the full-agent paste block pinned to latest-stable inside the Claude Code/Codex quick start (memory-only tier stays); docs/guides/bootstrap.md carries the full install/security/consent/degradation/uninstall contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(designs): spike instrument for the bootstrap wave (build order 0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): interview + render engines Interview gate with read-back confirm-hash (any later answer change clears the confirmation — the hostile single-batch case is structurally impossible), per-answer provenance, caps + escaping at set time, config-sink routing for the provider key; renderer with hard-fail token sweep, subordinate fencing of principal input, never-clobber + backups, deterministic minimal mode for the template repo, scaled byte floors. 58 unit tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): private-repo lifecycle — repo create, attach, uninstall, run lock gh-gated private repo creation with API-verified privacy (rate-limit distinct from public), refuse-foreign-origin with attach as the sanctioned path, atomic bootstrap mutex (pid liveness + age + token), receipt-keyed uninstall that never wholesale-deletes the gbrain home and only offers --delete-brain for a brain it created; read-only PGLite lock probe (never opens the engine). 54 unit tests, injectable exec seam throughout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(release): latest-stable ref, template-repo publish job, bootstrap CI guards release.yml advances the latest-stable tag only after assets publish (the paste block's permanent ref — copies in the wild never rot) and gains a PAT-gated publish-template job verified against the vendored tree; two skip-graceful guards (sanctioned-ref + runbook stamp; template/token bijection + placeholder assertion + generator byte-diff) wired into verify; README + runbook re-admitted to the CI cache hash; vendored deterministic template tree generated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(context): IPC v2 turn_context + 8KB assembly + visibility resolver + session identity Discriminated-union IPC with handler map, protocol echo (stale-serve detection), shared-secret gate, server-side source binding, per-kind budgets; turn-context assembly (reflex pointers + volunteered pages + world-only hot facts) under a data-not-instructions envelope trimmed to the harness's 10KB hook-output cap; facts.default_visibility resolved through one helper at all four sites (explicit caller wins, typos fail closed); typed sessionId threads _meta.session_id into the hot-memory cache key. 50 new tests; 180 adjacent tests confirmed green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(persistence): secret-scan, gbrain sources push, durability unification Pattern secret scanner (own runtime allowlist, redacted previews, corpus-write redaction mode); sources push runs the whole scan→stage→commit→pull→push sequence under one cross-platform lock (mkdir-atomic, pid+age+token) with a deny-glob backstop, commit-first divergence-safe pull, refuse-unverifiable visibility, and push-status telemetry; gbrain-home choke point unifies GBRAIN_HOME semantics with config (0700); durability is parent-repo-aware and rotates its push log at 0600. 35 new tests; 200 existing green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(sources): harden/pull gates accept sources inside a parent git repo The bootstrap workspace registers brain/ (a subdirectory) as the source; the durability core already resolves the repo root, so the command gates now check inside-a-repo rather than .git-right-here [CX2-3]. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(serve): resident maintenance sweep + keyless capability probe The lock-owning serve process now closes the persistence loop: startup (3s post-connect, best-effort, unref'd) and idle (10-min quiet intervals through the injectable timer seam) sweeps run facts-fence reconciliation, deterministic link/timeline extraction over recent workspace pages, and spend-gated corpus ingest (skipped keyless — agent-authored fences cover it). gbrain sweep --once is the trusted CLI seam bootstrap verify uses. Capability probe renders the honest keyless/keyed report. Full reuse of the cycle extractor + extract cores; 26 new tests, neighbors green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(hooks): engine-free gbrain hook command, settings writers, transcript parser Four hook events (session-start digest + crashed-session recovery push, user-prompt turn-context injection under an 800ms deadline and the 10KB cap, stop buffers, session-end corpus write with redaction/retention/dedup + best-effort push); structural JSON settings merger keyed by a _gbrain marker (foreign hooks and permissions survive); dated host-spec registry; Claude Code .jsonl parser as a spec-target with a scrubbed 7-shape fixture. Heartbeat is counters-only by construction. 59 tests; zero engine modules in the import graph. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(bootstrap): cross-link the full-agent path from the connection docs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): dispatcher, verify, status — the command assembled gbrain bootstrap {status,interview,render,repo,hooks,verify,uninstall,attach}: engine-free except verify (owns its engine, in-process sweep — no live-serve conflict); phase list is the TS source of truth with install.jsonl telemetry and the support blob; verify's fail-soft check suite covers the real write path (put_page → write-through file → sweep → graph floor → recall), passes keyless, persists snapshots, and ends with the first-run tour. cli.ts wired per the three-touchpoint rule; doctor gains the bootstrap check group (silent on machines with no bootstrap state). 28 new tests; 353 adjacent green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: KEY_FILES bootstrap cluster + CLAUDE.md dispatcher row (+ build:llms) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bootstrap): e2e pins — hook-under-live-serve, attach, degraded modes, compiled binary, Docker harness The permanent pins: a real serve holds the PGLite lock while the engine-free hook completes (and a direct engine open provably throws LiveServeLockError); stale-socket fail-open; machine-2 attach with marker-keyed hook repair; decline-everything installs verify green with every degradation named; the compiled binary renders bundled templates in an empty cwd. Offline Docker harness (networkless, read-only) gated into heavy-tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(bootstrap): register doctor check categories + system-of-record allow comments The six bootstrap doctor checks join OPS_CHECK_NAMES; the sweep's batch link/ timeline inserts carry the explicit extract-path allow comments (the sweep IS the extraction path for workspace pages). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): shard wedge cap tracks suite growth (1500s -> 1800s) At ~9000 tests a healthy shard finished at 1466s and two progressing shards were false-killed at the old cap; 1800s restores ~25% headroom over the slowest observed healthy shard. Real hangs still hit it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(ci): cache-hash policy — README + runbook edits must invalidate [C2] The old deny-list assertion predates the paste block; README.md and BOOTSTRAP_FOR_AGENTS.md are policy-doc re-admissions now, so their edits must change the hash (a paste-block edit shipping under a cached green was the C2 hole). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): classify post-suite exit-hangs as warn-pass; file the leak forensics A shard killed by the wedge watchdog with every assigned file started and zero fail markers did all its work and leaked a handle at exit — pre-existing and master-reproducible (P1 TODO carries the full bisect forensics). Bun's per-test timeout turns a hung test into a (fail), so the classifier cannot mask one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): shard cap 2400s — the count-balanced heavy shard needs it under contention Observed: the heavy shard still progressing 22s before an 1800s kill while siblings finish at 1150-1550s (split balances file count, not weight). Filed the load-sensitive WAL-repair flake (pre-existing, master's v0.42.75.0 wave). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bootstrap): quarantine env-mutating suites to the serial lane check-test-isolation R1: six new files mutate GBRAIN_HOME/env at module scope — the serial lane (one process per file) is the guard's prescribed home for them. All 114 tests pass post-rename. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(readme): per-harness install sections — Codex, Claude Code, then OpenClaw/Hermes Each harness gets its own complete paste-block section (desktop app first, terminal noted — Claude Code CLI is the identical harness; Codex CLI works pull-based today); the OpenClaw/Hermes platform path keeps equal weight with its one-click deploys and INSTALL_FOR_AGENTS block intact; memory-only and remote-connect tiers consolidated under 'Lighter ways in'. Supersedes the review's D5 ordering by user direction; stale heading references updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(readme): Codex as the recommended first step; OpenClaw/Hermes framed as-intended, high-cost The install section now routes newcomers explicitly: Codex first (subscription-priced, nothing to deploy), OpenClaw/Hermes as GBrain used the way it was designed — always on, at real server + API cost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: doc-audit code chasers — broken recovery hints, stale op description, auto_link key Four small code fixes surfaced by the markdown accuracy audit: - doctor's auto-RLS recovery hint pointed at `apply-migrations --force-retry 35`, which cannot work (--force-retry targets the vX.Y.Z orchestrator registry, not the numeric schema MIGRATIONS array). Hint now points at the recreate SQL in docs/guides/rls-and-you.md; test pins against regression. - v0_11_0 migration printed the same broken-mechanism class of hint (`config set minion_mode` writes DB config nothing reads); now names `apply-migrations --mode` + preferences.json, the real setter. - submit_job's op description hardcoded a stale handler list; now points at registerBuiltinHandlers as the source plus the --follow discovery trick. - `auto_link` added to KNOWN_CONFIG_KEYS: read by link-extraction, reconcile-links, and sweep, and documented as the off-switch in brain-ops/maintain, but the allowlist rejected `config set auto_link false`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: repo-wide accuracy + MECE reform from the 9-bucket markdown audit A code-grounded audit of every markdown file (root, architecture, guides, mcp, tutorials, docs-root, operations/eval/designs, skills, recipes) followed by a fix wave with per-bucket ownership. Four classes of change: Accuracy — every documented command/flag verified against src/ before writing: dead commands replaced with working ones (pages purge-deleted, jobs watch --follow, gbrain restore, import-based Obsidian flow, space-separated --scopes, real thin-client recipes, working isolation verification, real supervisor restart procedure, curl-based ngrok health check, real minion_mode setter); count drift fixed with rot-proof phrasing (100+ ops, 50+ bundled skills via skills/manifest.json, 140+ engine methods, KNOBS_HASH_VERSION pointer instead of hardcoded versions); stale claims corrected (search-mode defaults, RETRIEVAL pipeline order incl. autocut, sentinel rules, refusal-list mechanism, engine snapshot, shard cap 2400s + EXIT-HANG classifier in TESTING.md, latest-stable + publish-template documented in RELEASING.md as release.yml promises). MECE — one home per concept, pointers elsewhere: test isolation → TESTING.md; OAuth registration + --bind/--public-url lore → DEPLOY.md; mode bundles → guides/search-modes.md (the home the CLAUDE.md dispatcher always promised); merge contract → schema-packs.md; WAL ladder → ENGINES.md; quiet-hours → quiet-hours.md; capture taxonomy → entity-detection.md; person-page taxonomy → compiled-truth.md; brain-first protocol → brain-first-lookup.md; refresh semantics → refresh-algorithm.md; KEY_FILES.md deduplicated (58 extension entries merged, one entry per file); infra-layer.md rewritten as a pointer page. Privacy — placeholder sweep across guides, docs, skills, and recipes per the iron rule; per-release narration stripped from reference docs (current-state prose only). Bootstrap coverage — AGENTS.md pointer, RESOLVER routing row, INSTALL.md path, tutorial cross-links, keyless-mode sections in spend-controls/headless-install. skills.lock.json regenerated; llms.txt/llms-full.txt rebuilt. Gates: verify 36/36, typecheck clean, doctor 96/96, skills-integrity + resolver + build-llms + config-set + migrations all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(readme): refresh production-brain stats to current brain-repo counts 155,795 pages / 24,589 people / 5,340 companies, counted from the brain repo's current HEAD; the "100K-page brain" framing moves to 150K to match. Cron-fleet count unchanged (its store lives on the deployment host, not in the repos available for verification). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scan,push): modern OpenAI/Voyage key patterns, scan staged blobs not disk - secret-scan matches sk-proj-/sk-svcacct-/sk-None- and pa- Voyage keys (the bare sk- pattern missed every current OpenAI key format). - workspacePush stages first, then scans the staged index blobs via git cat-file, closing the scan-then-stage TOCTOU where a file changed between snapshot and commit shipped unscanned. - shared binary-sniff helper, memoized glob regexes, atomic push-status write, and tests for pull_conflict + gitignored deny-match paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): regenerate flag registry for new commands, harden shard classifier + release token - cli-flag-registry.generated.ts regenerated: bootstrap/hook/sweep and sources push --message/--allow-unverified-remote were missing, so the strict #2185 validator rejected real invocations and skipped the new commands entirely. - EXIT-HANG shard classifier now requires every assigned file to have started before warn-passing a watchdog kill (was fail-open). - release.yml passes TEMPLATE_REPO_PAT via http.extraheader, off the argv. - compiled-binary e2e fails loud in CI instead of a silent permanent skip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(hook,sweep,ipc): non-blocking hook pushes, bounded sweep + cache, source-bound resolve - session-start/session-end no longer run synchronous git + inline push inside their self-deadline; a detached child does the push and the hook returns immediately (blocked Claude Code startup for minutes on a dirty tree before). - serve sweep drops the unbounded listAllPageRefs, resolves only candidate targets, claims corpus files atomically (no double-LLM-spend race), and caps the fence LIKE scan; heartbeat writes are O_APPEND with rare compaction. - hot-memory cache evicts expired entries and bounds entry count (the key is caller-controlled via _meta.session_id). - v1 resolve IPC honors boundSourceId like turn_context; turn-context runs its arms concurrently. doctor reads push/heartbeat thresholds from hook.ts. - new tests: doctor bootstrap checks, hook push-gate + deadline, concurrent sweep claims, cache eviction, bound-source resolve. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(bootstrap): origin-ownership gate, world visibility, collision-safe source id, consent + templates - repo adoption requires an exact receipt repo_url match or authed-owner check (undefined repo_url was a wildcard); create verifies privacy BEFORE the first push. - verify sets facts.default_visibility=world if unset, so agent-authored facts surface in per-turn context (they defaulted private before). - source_id derives a path-hash suffix when 'workspace' is taken by another checkout; every consumer reads manifest.source_id. - skipped HOOKS_CONSENT now declines (was falling through to default yes); --minimal refuses on an initialized manifest; tilde fences escaped. - MCP registration pins --surface full; status hard-fails a public origin (template door); receipt writers guard against newer/corrupt receipts; uninstall only claims brain-deleted after a real rm. - templates ship jobs disabled + provider-consent + support-relay lines; soul-audit re-runs over the shared interview bank. TODOS: 11 follow-ups. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(security): close adversarial-review findings — scan fails closed, whole-PEM redaction, bound repo push Cross-model adversarial pass (Claude + Codex) on the bootstrap wave: - secret scan fails CLOSED: an unreadable, oversized, or binary staged blob now blocks the push (blocked_unscannable, exit 5) instead of committing unscanned; only a confirmed staged deletion is skipped. This was the headline "block secrets before they leave the machine" property failing open. - private-key redaction spans the whole PEM block (header+body+footer), not just the header line — the base64 body no longer survives into the corpus the sweep sends to an extraction provider. - bootstrap repo commits the workspace (secret-scan-gated) before the first push and verifies the remote actually received it, so a push-fail retry can't adopt an empty remote as success. - privacy verify is re-bound to origin immediately before push (a concurrent origin rewrite between verify and push is refused). - session-end corpus write is atomic and clears the stale ingested/in-progress sidecars so a resumed session's appended transcript is re-ingested. - public-origin refusal enforced at render (not only status); MCP "already registered" is verified to target this workspace, not blessed blindly; verify probe cleanup scopes deletes to its own slugs, not a token substring; allowlist fingerprint floor raised 8→16 hex. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.45.0.0 feat(bootstrap): paste-in personal-agent install for Codex + Claude Code Turns a Codex or Claude Code session into a persistent personal agent: interview-rendered identity files, a local PGLite brain, per-turn context via serve IPC (Claude Code hooks / Codex pull protocol), session-triggered persistence, and a private GitHub repo as the agent's portable body. Keyless- first (the harness model is the LLM; one optional key adds embeddings + extraction). New `gbrain bootstrap` command family + `gbrain hook` + `gbrain sweep`; doctor bootstrap health checks; latest-stable distribution ref + template-repo publish job. Opt-in, additive — existing installs untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: regenerate flag registry for security-fix flags; drop fabricated gbrain capabilities doc ref CI caught two real failures under the merged state: - the flag registry lagged the blocked_unscannable/exit-5 flags the security round added, tripping the #2185 freshness guard. - headless-install.md described the keyless capability report as a `gbrain capabilities` command, which the #3502 doc-command resolver rejects — reworded to prose (the real surface is bootstrap verify's report). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: sync KEY_FILES + bootstrap plan to security-fix behavior Cross-referenced the security-fix round against the reference docs and corrected the drift those commits introduced: - workspace-push.ts entry: stage-FIRST-then-scan order (the TOCTOU fix), fail-closed blocked_unscannable, and the sources-push status -> exit-code map. - hooks.ts entry: MCP registration pins `serve --surface full`. - hook.ts entry: session-start/session-end pushes run in a detached child (non-blocking); atomic corpus write clears stale sidecars. - bootstrap.ts entry: render hard-refuses a public origin (template door). - verify.ts entry: source_id collision resolution (workspace-<path-hash>). - AGENT_BOOTSTRAP_PLAN as-shipped delta note for the scan/stage reorder. llms bundle unchanged (KEY_FILES is link-only); build:llms and test/build-llms.test.ts green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): silence SC2016 on the intentional askpass literal in release.yml The one-shot GIT_ASKPASS script must contain literal $1 and $TEMPLATE_REPO_PAT so they expand when /bin/sh runs it at git's credential prompt, not when the outer shell writes the file — single quotes are correct. Add a scoped shellcheck disable so actionlint passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(skills): declare 'bootstrap my data' trigger in cold-start frontmatter The doc reform added 'bootstrap my data' to cold-start's RESOLVER.md row (to disambiguate data-bootstrap from agent-bootstrap) but not to the skill's own frontmatter triggers, tripping the RESOLVER↔frontmatter round-trip contract (resolver.test.ts). Declare it; regenerate skills.lock.json. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(bootstrap): default per-turn hooks + search mode ON without a prompt Installing gbrain for your coding agent IS the consent for the behaviors that make it work, so stop re-litigating them with install-time questions whose "no" defeats the product: - Per-turn hooks (Claude Code) install ON by default — no prompt. Off-ramps: `--no-hooks` at install, `GBRAIN_HOOKS=0` at runtime, `bootstrap uninstall`. The "hooks installed" line now surfaces the kill switch so default-on is never silent. A persisted HOOKS_CONSENT=no (interview --skip) still declines. - Search mode defaults to `balanced` silently (nobody knows the modes at install; `gbrain search modes` changes it any time). - MCP scope stays the ONE deliberate prompt — project vs user is a real cross-repo privacy choice, not friction. Marks the two consents `silent: true` in the question bank (new QuestionSpec field), rewrites the runbook phases so the agent no longer asks them, adds the `--no-hooks` flag (+ registry regen), and adds default-on / opt-out tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bootstrap): real end-to-end coverage — cross-session recall, per-turn content, Codex door, realistic corpus Closes the seven e2e gaps a coverage audit surfaced: the plumbing was well-unit-tested but the product claims ("Codex works, context shows up every turn with real content, it remembers across restarts, machine two recovers, Postgres works") were unproven end to end. Test-only wave — zero src changes. - Hermetic synthetic corpus (test/fixtures/bootstrap-corpus/ + a loader helper): 12 interlinked pages (52 edges, timelines), 12 world/private beliefs, 8 gold queries — curated from the gbrain-evals synthetic corpora, 100% placeholder names, so recall is asserted on a real multi-entity brain instead of a 2-node self-planted probe. - GAP1 magic moment: author a fact via the real write path, disconnect the engine, reopen against the same DB, recall it — a real session boundary, not verify.ts's same-connection SQL read-back. Plus a source-isolation assertion. - GAP2 per-turn content: hook-under-serve Pin 1 now seeds a known fact and asserts its text lands in the injected block AND private beliefs never do (was: empty brain, empty_block accepted as a pass). - GAP3 Codex door: assert the rendered AGENTS.md carries the Gate-3 brain-first pull protocol; make the fake codex shim implement `mcp get` so the [FIX7] target-verification can actually fail; the Docker cold-machine harness now exercises the hooks/MCP registration step instead of skipping it. - GAP4 corpus recall: turn-context + verify graph-floor/qrels run on the real multi-entity brain with real edges. - GAP5 attach: machine-two now re-ingests the cloned brain/ into a fresh DB and recalls a fact authored only on machine one — the multi-device payoff. - GAP6 keyed + Postgres (env-gated): real embeddings prove semantic recall a paraphrase query can reach but keyless BM25 cannot; bootstrap verify drives a real Postgres engine (skipIf DATABASE_URL/keys absent). - GAP7 persistence: session-end runs the REAL push (not the mocked seam) to a local bare remote and the remote receives the content; a planted secret is blocked at the gate; the 15-min cron installs and fires a scan-gated push. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(bootstrap): real-agent e2e — drive the actual claude + codex binaries end to end Closes the audit's biggest gap ("no real harness ever drives a turn"). Adapts gstack's PTY/headless agent harness to prove the bootstrap install + smoke work against the REAL binaries, not PATH shims. Test/CI/docs only — zero src changes. - test/helpers/agent-harness.ts: hermetic clean-room child env (ported from gstack; drops CONDUCTOR_/CLAUDE_/GSTACK_/MCP_/GBRAIN_, promotes GSTACK_ANTHROPIC_API_KEY→ANTHROPIC_API_KEY), real-binary resolvers + auth probes, headless `claude -p --output-format stream-json` and `codex exec --json` turn runners, a gbrain stdio MCP-config writer, and a keyless brain seeder. + a fixture-parse unit test (no binary needed). - test/e2e/bootstrap-real-claude.serial.test.ts: real `gbrain bootstrap` install → REAL `claude mcp add` (verified via `claude mcp get`) → verify exit 0 → a real `claude -p --mcp-config --strict-mcp-config` turn that invokes mcp__gbrain__search and answers from the brain (proven: toolCalls include mcp__gbrain__search, final text carries the seeded fact). - test/e2e/bootstrap-real-codex.serial.test.ts: same install with REAL `codex mcp add` into a real ~/.codex/config.toml + Gate-3 pull-protocol assertion, then a real `codex exec --json` turn surfacing the fact (MCP or the pull- protocol shell path). Bounded retry absorbs codex's occasional MCP-call cancellation without softening the fact-requiring assertion. - Everything hermetic (temp HOME/CLAUDE_CONFIG_DIR/CODEX_HOME/GBRAIN_HOME; real ~/.codex auth copied read-only) and skipIf-gated so it self-skips cleanly where the binaries/auth are absent. - heavy-tests.yml: gated `real-agent-e2e` job (nightly/label, never the PR shard; no-op on a runner without authed binaries). - TODOS: compiled `gbrain` binary can't serve a PGLite brain (bun compile omits the WASM/extension payloads); harness falls back to `bun run` serve. Verified against live claude 4.6 + codex 0.147.0: 15 pass / 0 fail; verify 36/36; typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): real-agent-e2e job — bash array + --timeout (actionlint SC2086 + bun-test-timeout guard) The real-agent-e2e job's file loop used an unquoted $FILES (SC2086) and ran `bun test` without --timeout (check-bun-test-timeout guard). Switch to a bash array and add --timeout=600000 (real-agent turns are slow; the tests self-skip without authed binaries so it's a no-op elsewhere). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pglite): embed WASM + extension assets so the compiled binary can serve A `bun build --compile` gbrain binary could not `serve` a PGLite brain: the compile bundles JS but not PGLite's runtime payload (pglite.wasm, initdb.wasm, pglite.data, vector/pg_trgm tarballs), so `serve` on PGLite died with a bunfs/ENOENT. Now the assets ride inside the binary. - src/core/pglite-embedded-assets.ts: embeds the five assets via `import … with { type: 'file' }` (the ENG-6 idiom) and exposes getEmbeddedPgliteOptions() → { pgliteWasmModule, initdbWasmModule, fsBundle, extensions:{vector,pg_trgm} }. WASM/fsBundle are consumed as bytes; the two extension tarballs are materialized to a content-addressed temp file (atomic, size-verified reuse) because PGLite reads them via fs.createReadStream, which cannot read a /$bunfs path. Unconditional (works in bun-run and compiled), so no fragile mode branch. - src/core/pglite-engine.ts: static-import getEmbeddedPgliteOptions (engine path stays static per the engine-dynamic-import invariant); spread into both PGlite.create sites (initial + WAL-repair retry). The bunfs classifier stays as a backstop but no longer fires for a correct binary. - scripts/check-pglite-embedded.sh (+ smoketest): compiles a focused binary and asserts it boots PGLite, CREATE EXTENSION vector/pg_trgm, and round-trips a page — wired into `bun run verify` (now 37 checks), check:all, and check:pglite-embedded. Fail-soft only when compile is unavailable. - agent-harness.ts probeCompiledPglite now passes → the real-agent e2e uses the fast compiled MCP server. TODOS: the P2 "can't serve PGLite" item is closed. Verified: fresh compiled binary ran `search`/`query` against a PGLite brain and returned the seeded row (no bunfs/ENOENT); verify 37/37; pglite-engine 120/0 source-mode; typecheck clean; engine-dynamic-import + parity guards pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.45.0.0 |
||
|
|
fc310db3ea |
v0.44.1.0 feat(ai): recipes resolve any model — remove the native-recipe runtime allowlist (#4014)
* feat(ai): recipes resolve any model — remove the native-recipe runtime allowlist Frontier models ship weekly; a curated models: array can never stay current. assertTouchpoint now checks only the provider's touchpoint capability (anthropic has no embeddings, voyage has no chat) and never gates on the model id. Any id the user names goes to the provider, which is the real authority on what exists — a nonexistent model surfaces as the provider's own model_not_found at call time, and gbrain models doctor live-probes the configured models for a pre-flight check. With the gate gone, the entire extendedModels bypass machinery is dead and deleted: the _extendedModels registry, registerExtendedModel, registerConfigSelectedChatModel (+ its one caller in the contextual-reindex handler), both registration loops, and the tier-resolution loop that existed only to feed them. This also structurally closes the per-task-key gap where models.think / models.dream.* / facts.extraction_model selections were rejected while identical models.default selections worked. Recipe models: arrays remain informational — models[0] default selection for --model <provider> shorthand, guard-test fixtures pinning the repo's own hardcoded defaults, and gbrain providers list display. gateway.rerank() keeps its own model-list check deliberately: each listed reranker id maps to a known request/response wire shape. unknown_model still fires for providers lacking the touchpoint, so every probe reason stays reachable; tests that pinned the allowlist rejection now pin the pass-through contract (or the missing-touchpoint trigger). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.44.1.0 feat(ai): recipes resolve any model — remove the native-recipe runtime allowlist Frontier models ship weekly; a curated models: array can never stay current. assertTouchpoint now checks only the provider's touchpoint capability and never gates on the model id — any id the user names goes to the provider, and a nonexistent one surfaces as the provider's own model_not_found at call time (gbrain models doctor stays the token-free pre-flight). The extendedModels bypass machinery is deleted end to end, which also closes the per-task-key gap: models.think / models.dream.* / facts.extraction_model selections now behave exactly like models.default. think's graceful sentinel surfaces the thrown AIConfigError's own message + fix instead of generic key advice, so a provider 4xx is never key-blamed. gateway.rerank() keeps its own model-list check (each listed id maps to a known wire shape). Ship chores: VERSION/package.json → 0.44.1.0, CHANGELOG entry, gitleaks action pin refreshed to current v2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.44.1.0 |
||
|
|
52306ed438 |
fix(auth): accept comma-separated --scopes and reject zero-token input (#3990)
`gbrain auth register-client --scopes` only split on whitespace, so the comma-joined form the CLI's own registration hint recommends (`--scopes read,write,admin`, printed by init.ts) fell through as a single unrecognized token and was rejected with `Unknown scope "read,write,admin"` -- self-contradicting the hint. parseRegisterClientArgs now normalizes comma- and/or whitespace-separated --scopes input to the canonical space-joined form before it reaches registerClientManual's assertAllowedScopes gate. The shared parseScopeString (RFC 6749 space-delimited OAuth wire format, also used for untrusted DCR/refresh/request-scope parsing) is left untouched. Input that normalizes to zero tokens (comma-only, whitespace-only, or empty) is rejected at the parser boundary with a clear usage error, rather than silently registering a client with no usable scopes -- closing a hole that a comma-only first pass at this fix left open (caught by review). Tests cover comma/space/mixed forms parsing to the identical scope set, a genuinely unknown scope still being rejected, and zero-token input being rejected. |
||
|
|
a2521d0a32 |
fix(enrich): keep entity-name candidates on a single line (#3991)
* fix(enrich): keep entity-name candidates on a single line extractEntities()'s namePattern used \s+ between capitalized words, which matches across line breaks. A capitalized word ending one paragraph and a capitalized word starting the next were spliced into one bogus multi-word candidate whose name embedded the line break(s). Scope the pattern to same-line whitespace: [^\S\r\n ]+ (spaces, tabs, NBSP, other Unicode space separators, but not \n, \r, or the Unicode line/paragraph separators U+2028/U+2029). Same-line multi-word names and NBSP-separated names still match; any line break between two capitalized words now stops the match, same as it already does for punctuation. Surfaced during single-user dogfooding on a ~100-page install (v0.44.0.0): extractEntities produced ~1,620 raw candidates, and these paragraph-spanning splices were the largest single source of noise. * fix(enrich): exclude vertical tab and form feed from name-splice guard The line-scoping fix in the prior commit excluded CR, LF, and the Unicode line/paragraph separators (U+2028/U+2029) from the same-line whitespace class, but still treated VT (U+000B) and FF (U+000C) as same-line whitespace since \s matches them and they weren't in the excluded set. Winters<VT>Reyes and Winters<FF>Reyes were still spliced into one bogus candidate, contradicting the "any line break stops the match" intent. Add \v and \f to the excluded set, so the pattern now excludes every vertical whitespace code point (CR, LF, VT, FF, U+2028, U+2029) while still retaining horizontal whitespace (spaces, tabs, NBSP, other Unicode space separators) between same-line words. |
||
|
|
75fae742d5 |
v0.44.0.0 feat(eval): BrainBench — cross-harness memory conformance suite (Cathedral 2) (#2158)
Public, reproducible, cross-harness benchmark for the four ways agent memory fails (know-to-ask, push precision/recall, write-back fidelity, cross-session continuity). Hermetic PGLite, sealed gold, deterministic corpus, CI gate vs master's committed baseline, published JSON-schema foreign-runner contract. See docs/eval/BRAINBENCH.md.v0.44.0.0 |
||
|
|
a13ecb1f0a |
fix(cycle): BudgetMeter prices from the derived Anthropic view, so the cap is off for canonically-priced models (#3951)
* fix(cycle): price BudgetMeter through the canonical table, not the derived Anthropic view BudgetMeter estimated submit cost with `estimateMaxCostUsd`, which reads ANTHROPIC_PRICING. CLAUDE.md defines that table as a DERIVED view of the one canonical chat-pricing table, so any non-Anthropic model was unpriceable here even when CANONICAL_PRICING carries its rates — and an unpriceable model does not merely lose accuracy, it takes the `cost === null` branch and returns `allowed: true` with cost 0. The cap is off for that model. Measured on |
||
|
|
e2ff128e35 |
fix(config): register sources.default as a known key + validate it at set time (#3941)
* fix(config): register sources.default as a known key + validate it at set time
`gbrain config set sources.default <id>` printed
WARN: writing unknown key "sources.default" with --force.
Nothing in gbrain reads this.
That last sentence is false. `source-resolver.ts` tier 5 reads
`getConfig('sources.default')` on every unqualified call, and
`gbrain sources default <id>` writes exactly this key. The key was
simply missing from KNOWN_CONFIG_KEYS.
The message matters more than the missing entry: pinning
`sources.default` is the documented way to stop tier 5.5
(sole-non-default-source auto-routing) from silently moving where
unqualified writes land. An operator who hits that and reaches for
this knob is told the knob does nothing.
- Register the exact key, not a `sources.` prefix. A prefix would
bless arbitrary unread `sources.*` keys and weaken the unknown-key
guard this list exists to provide.
- Validate at set time, mirroring `runDefault()`: reject a malformed
id, and reject one that is not registered. Without this, registering
the key would turn `config set` into a way around the check
`sources default` already performs — and tier 5 calls
assertSourceExists, so a typo would surface later as a throw on
unrelated commands rather than at the point of the mistake.
Tests: membership, the absence of a blanket `sources.` prefix, and the
three set-time paths (malformed / unregistered / accepted).
* fix(config): keep flag literals out of the new error text + refresh flag registry
CI caught this: the #2185 freshness guard failed because the generated
flag registry no longer matched a fresh generator run.
Two separate causes, only one of them mine:
1. Mine. The "source is not registered" message named `--path` while
suggesting `gbrain sources add`. The generator scans command sources
for flag tokens, so a flag named in prose silently gets granted to
`gbrain config` — exactly the prose-bleed class #3902 closed for
safety flags. Reworded to carry no flag literals. (The first attempt
at a warning comment reproduced the bug by quoting a placeholder
flag in the comment itself; that is gone too.)
2. Pre-existing. Regenerating also adds `--federated-read`, which comes
from `src/commands/auth.ts` and appears in no line this branch
touches. The committed registry on master is stale with respect to
its own sources, so the guard fails for any branch cut from master
until it is refreshed. Committed here because the guard's contract is
"committed registry == fresh generator run"; flagged in the PR body
so it is not mistaken for part of this change.
`bun test test/cli-flag-validation.test.ts` — 24 pass / 0 fail.
* fix(config): propagate lookup failures, describe the real id grammar
Round-2 review findings:
- Dropped `.catch(() => null)` around `fetchSource`. It turned a
connection failure / permission error / SQL regression into
"source is not registered", sending the operator after a
registration problem that doesn't exist while the real fault was
swallowed. `fetchSource` already absorbs the one expected
legacy-column case; everything else should surface as itself.
Test added: `executeRaw` throws → the error propagates and nothing
is written.
- The malformed-id message quoted `[a-z0-9-]{1,32}`, which is looser
than `SOURCE_ID_RE` (`-wiki` reads as legal under the printed rule
but is rejected). Now states the actual grammar.
- Tightened the accept-path assertion from `toContainEqual` to an
exact `setCalls` equality.
Also corrects the previous commit's explanation of the flag-registry
drift: `--federated-read` is NOT pre-existing master drift. It enters
`config`'s flag set because this change adds
`await import('../core/source-id.ts')`, and that module's comment
mentions the flag — the generator follows imports, so prose-bleed
crosses module boundaries. Master is not stale; this branch pulled it
in. The regenerated entry is deterministic and harmless (`config` is
flag-validation-exempt), and stays in this PR because the guard's
contract is "committed registry == fresh generator run".
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
69472c24e7 |
fix(jobs): embed --dry-run --background embedded for real (#3594 class) (#3956)
* fix(jobs): embed --dry-run --background embedded for real
`runEmbed` serializes the flag into the job payload
(`dryRun: cleanArgs.includes('--dry-run')`), but the registered `embed` worker
handler never read it back — it forwards slug/slugs/all/stale/sourceId/pace and
onProgress, and nothing else. So `runEmbedCore` ran without `dryRun`: a
backgrounded preview called the embedding provider and wrote vectors. API spend
and NULL->vector mutation from an invocation whose entire purpose was neither.
One line: the handler now passes `dryRun: !!job.data.dryRun`.
Fourth instance of the class in #3594, which lists three (jobs prune, unify-types,
sync) and says "filing the class, because fixing them one at a time will not stop
the fourth". The shape here is a fourth variant: the guard is neither late nor
defaulted wrong — it simply is not wired to the flag the CLI already sends.
The test mirrors test/jobs-unify-types-default-dryrun.test.ts (the #1575 fix in
that same issue) and asserts the side effect rather than the return value, which
is #3594's stated reason this class escapes tests. It seeds real stale chunks
first and asserts countStaleChunks > 0, so the dry-run path cannot short-circuit
and pass the write assertion vacuously.
Two independent signals, so it bites in either environment. Without embedding
credentials a real run throws EmbeddingCredentialError from the preflight, so
reaching the assertion at all proves the dry-run branch was taken — a preview
has no business needing an API key. With credentials the run would succeed and
write, and the chunk count catches it.
Verification on
|
||
|
|
ea17f5911c |
fix(cli): --help required a configured brain for five self-help commands (#3954)
* fix(cli): --help required a configured brain for five commands `gbrain models --help` on a machine that has not run `gbrain init` exits 1 with "No brain configured. Run: gbrain init". So do watch, skillopt, maintain and extract-conversation-facts. That is the state a reader is most likely to be in when they reach for --help. Mechanism: cli.ts's per-command --help block prints the generic usage stub only for CLI_ONLY commands NOT in CLI_ONLY_SELF_HELP. Members of that set are meant to print their own help, so they fall through to the normal dispatch -- which connects the engine before the handler runs. Each of these five already opens with a --help branch (models honours it first by explicit comment, watch and skillopt on line 1 of the handler); the connect gate made that branch unreachable. Dispatched pre-connect now, through a small table keyed by command. The engine is never read on the help path, so a placeholder is passed rather than widening five signatures -- skillopt already declares `BrainEngine | null` for this reason. Measured on |
||
|
|
d050969da6 |
fix(frontmatter): drop five Apple Notes directory rules that cannot change an outcome (#3952)
* fix(frontmatter): drop five Apple Notes rules that cannot change an outcome DIRECTORY_RULES carried five subfolder rules whose every field equals what the generic 'apple notes/' rule already produces. The generic rule assigns type/source/date/title and tags the first path segment lowercased and hyphenated, which is exactly what these five spelled out by hand. They read as deliberate overrides and are not. Measured across prefix x body x filename (45 cases): title, type, date, source and tags are identical with and without them. The only field that moves is `matchedRule`, the debug label naming which rule fired -- and it must move, because the rule that fires is now the generic one. Nothing persists that field: serializeFrontmatter writes title/type/date/source/tags only, and the one behavioural consumer (frontmatter.ts:475) compares it against the literal '(default)', which neither value is. Removing them exposed an existing assertion as vacuous: the ordering test named 'apple notes/yc/' and asserted its index is below the generic rule's. findIndex returns -1 for an absent prefix, and -1 is below any index, so the test kept passing while testing nothing. Rewritten to require every subfolder rule to precede the generic one, and to require that the set is non-empty. Adds a guard that recomputes the equivalence rather than listing prefixes, so a rule that re-derives the generic output cannot be added back silently. Verification on |
||
|
|
7c65bcee9b |
fix(embed): dry-run announced one page of progress for any stale count (#3955)
`gbrain embed --stale --dry-run` emitted a synthetic `onProgress(1, 1, 0)`.
The CLI latches its `embed.pages` total from the first callback, so the
progress stream announced a single page next to a summary naming every stale
chunk:
{"event":"start","phase":"embed.pages","total":1}
{"event":"tick","phase":"embed.pages","done":1,"total":1,"pct":100}
[dry-run] Would embed 60 stale chunks
Measured on a fresh PGLite brain with 60 unembedded pages, v0.42.76.0.
docs/progress-events.md defines `total` as "the total item count if known at
start" and says it is omitted when the work has no total up front. Omission is
provided for; asserting a wrong value is not. The number is also internally
inconsistent with this same path's own result: the existing test asserts
`pages_processed === 0` for a dry run, with the comment "we don't enumerate
pages in dry-run (cheaper pre-flight)".
The synthetic call is dropped, so a dry run emits no `embed.pages` phase --
matching the zero pages it processes. No consumer requires the phase to appear:
progress-tail.ts names it only as an example, and the minion path
(jobs.ts:1553) passes its own callback from the real embed loop.
Adjacent but distinct from #1459, which is about the denominator of a REAL
`--stale` run being latched from the first callback while pages keep arriving
(numerator exceeds it). Same phase, different cause; this change does not
address that one.
Verification on
|
||
|
|
9afbf4a09a |
docs(search): telemetry comments promised an exit flush the code deliberately removed (#3950)
* docs(search): telemetry header still promised an exit flush that was removed The module header for the search-telemetry rollup writer describes the buffer as "flushed periodically (60s OR 100 calls, whichever first) AND on process exit via beforeExit/SIGINT/SIGTERM with a 2-second timeout cap." `ensureExitHook` two hundred lines below says the opposite, and is the current behaviour: "Lossy by design: skip the buffered drain entirely on process exit", with the signal handlers dropped too. The beforeExit drain was removed because it kept short-lived CLI processes alive waiting on a DB write, which hung test/e2e/claw-test.test.ts. So the file has documented an exit flush it does not perform. A reader who stops at the header concludes every recorded search reaches search_telemetry. The header now states current behaviour and names the consequence, pointing at the existing ensureExitHook rationale rather than restating it. Comment only; no code path changes. Verification on |
||
|
|
e11e407752 |
fix(cli): global help advertises list -n N, a flag list does not accept (#3953)
`gbrain --help` documents `list [--type T] [--tag T] [-n N]`. The command does
not read `-n`; the limit flag is `--limit`, which `gbrain list --help` already
documents. Nothing in the tree parses `-n` for this command (the only `-n` in
src/ is integrations.ts's alias for --dry-run).
Measured on a fresh PGLite brain with 60 pages:
list -n 5 -> 50 rows (the default)
list -n 300 -> 50 rows
list --limit 5 -> 5 rows
list --limit 300 -> 60 rows
Not silent, but not usable either. The run prints `output truncated at 50 rows
(default 50). Pass an explicit limit`, and a reader who reached for -n because
the help said so has already done exactly what that message asks.
Help text only; the flag registry regenerates unchanged.
Verification on
|
||
|
|
c5952b8714 |
v0.43.0.0 feat(mcp,search): MEMORY_VERBS v1 five-verb protocol + concept-question query steering (cathedral 1, #2416) (#2172)
* feat(verbs): MEMORY_VERBS v1 core — five frozen verbs over existing machinery recall extended in place (G1B additive superset: protocol_version + string fact_id/provenance on every response; query arm via cheap-hybrid with keyword-only degradation when no embedding provider; op-layer budget packing facts-first via the new generic packToBudget). remember/entity/synthesize/ forget as first-class Operations in src/core/verbs.ts (runtime leaf — TDZ-safe both import directions) with MEMORY VERB descriptions, ToolAnnotations, populated suggestions on every error (verbError + protocol ErrorCodes + additive detail/protocol_version on OperationError.toJSON). writeSingleFact (facts/write-single.ts): zero-LLM single-fact seam reusing the pipeline's dedup; X1 deterministic supersession (same entity+kind, similar, text differs -> supersedeId / fence expire+link); validUntil threaded through FenceInputFact (was silently dropped). buildEntityCard (verbs/entity-card.ts): reflex resolution arms + parallel depth-1 reads, per-arm degradation on pre-page_aliases brains, near-miss suggestions with create_safety. runThink gains additive usage aggregation; synthesize converts the no-LLM graceful result to the unavailable protocol error and prices the cost block via canonical model-pricing. RESPONSE_SCHEMAS registry + ERROR_SCHEMA for the conformance kit. Cathedral 1 stage 1/4 (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(mcp): MEMORY_VERBS v1 surface mode + fail-closed dispatch + usage sidecar gbrain serve --surface verbs|full (config mcp_surface registered in the merge allowlist; default full — existing installs unchanged). Enforcement is two-layer and fail-closed: filtered ListTools AND dispatchToolCall's new allowedOps set, applied on stdio + BOTH HTTP paths (serve-http.ts OAuth and the second http-transport.ts). ToolAnnotations passthrough in buildToolDefs + the HTTP inline mapper, emitted only when an op defines them (byte-stability for existing tools). Verb validation/internal errors speak the protocol envelope (suggestion + protocol_version) at the dispatch layer. E4 observability: per-verb JSONL sidecar (usage-log.ts — local only, never uploaded; O_APPEND line-atomic, lock-free 10MB rotation) written from dispatch so param-validation failures count; brain_id via gbrainPath; doctor gains the memory_verbs_usage check. CLI: human-readable rendering for remember/entity/ synthesize with trailing --json escape; gbrain recall gains --query + --budget-tokens routed through the recall OP (same arm MCP exercises) with the budget footer; the forget verb carries no cliHint (CLI_ONLY forget dispatches first and would shadow it). Cathedral 1 stage 2/4 (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(protocol): gbrain protocol — schemas, conformance kit, stats + TTHW gbrain protocol [--json] emits the live verb input schemas + the hand-authored RESPONSE_SCHEMAS + error contract + versioning policy. gbrain protocol conformance certifies ANY MCP endpoint (default: self-spawned stdio; --target http://...[--token] or a stdio command) against the embedded fixture set — shape/enum/behavior/round-trip only, deterministic; entity-page cases seed via put_page when available and skip honestly on verbs-only targets; synthesize is cost-gated behind --synthesize (asserts the clean unavailable error on key-less CI). Fixtures double as BrainBench seeds (test/fixtures/memory-verbs/cases.json mirror). Self-certification passes 23/23 live. gbrain protocol stats [--days N]: per-verb calls/error-rate/latency, budget drops, entity hit rate, and measured TTHW (init stamps protocol_installed_at; delta to the sidecar's first verb call) — local JSONL only, never uploaded. init epilogues print the 3-command quickstart (mcp add --surface verbs + key-free resurrection demo) with the visibility consent line. entity-card fix: raw namespaced-slug input now matches exactly (slugify flattens slashes). Cathedral 1 stage 3/4 (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test+docs(protocol): conformance suite, p99 gate, MEMORY_VERBS_v1 doc, funnel test/memory-verbs-conformance.test.ts: 26 tests through dispatchToolCall — G1B superset regression (legacy fields byte-equal, additions allowed), budget math incl. budget<first-item, keyword degradation (never an error), remember provenance/ttl-trap/null-echo, remote round-trip via the world default + private facts hidden, entity card schema-validated with the chat gateway rigged to throw (zero-LLM guard) + all arms + fence test, synthesize [EXPENSIVE + annotations + clean unavailable, forget idempotency/not_found, writeSingleFact X1 supersession via the embed-transport seam + degraded dedup, the F3 negative self-test (the runner FAILS lying servers: dropped fields, bad enums, re-typed ids), fixture-mirror drift guard. test/mcp-surface.test.ts: verbs=exactly 5, full=identity, dispatch-layer fail-closed, strict flag parsing. test/entity-card-perf.slow.test.ts: 20K pages/100K links/30K aliases/40K facts — measured p99 22.9ms vs the 100ms contract + a recalibrated 50x ratio guard; wired into the CI perf job along with stdio self-certification (23/23 CONFORMANT live). tool-defs byte- stability test now pins per-op: ops WITHOUT annotations keep the exact pre-v1 shape. docs/protocol/MEMORY_VERBS_v1.md: the frozen contract — envelopes, error codes, additive-forever policy, ttl forms (P30D rejected), status-not- status_text, surface modes + why default full, per-harness installs, consent line, conformance pass criteria + copyable --target examples, local-only stats privacy, 200K manual bench recipe. README quickstart + docs/mcp pages moved to --surface verbs with the claude-not-found recovery; llms-config registered + bundles rebuilt; KEY_FILES entries for the 7 new modules. Cathedral 1 stage 4/4 (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(verbs): verify-gate compliance — test seam, allow-comment placement, import hygiene usage-log gains __setUsageLogPathForTests so the verb tests redirect the sidecar without mutating process.env.GBRAIN_HOME (test-isolation lint); the gbrain-allow-direct-insert rationale moves onto the insertFact call line (system-of-record guard greps per-line); protocol.ts drops its unused `operations` import (operations-filter-bypass guard — operationsByName only). The synthesize unit test now drives the chat-transport seam (deterministic success envelope + priced cost block, zero real API spend); the keyless `unavailable` conversion stays covered end-to-end by the CI conformance step, which runs credential-free. Cathedral 1 verify pass (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(verbs): close 3 ship-review P1s — forget cross-source, entity backlink leak, conformance exit code Pre-landing review (codex, on the committed diff) caught three P1s the plan-stage reviews flagged only in the abstract: P1.1 trust boundary — forget expired by GLOBAL fact id with no source/visibility scope: a remote caller in source A could expire facts in source B (incl. private) by guessing ids. forgetFactInFence gains sourceId + worldOnly opts (scope-check before any state inspection; out-of-scope → not_found, no existence leak); the forget verb passes ctx.sourceId + worldOnly=remote. P1.2 source isolation — entity card incoming edges + backlink_count leaked across sources: getBacklinks(slug,{sourceId}) scopes only the TARGET page and getBacklinkCounts has no source param, so a foreign-source page's slug surfaced in a same-named entity's card. Replaced both with a both-sides-scoped query (f.source_id = t.source_id = entity source, mentions excluded) inside the card module — no engine-interface change. P1.3 CLI exit code — protocol conformance/unknown-subcommand set process.exitCode directly, which the PGLite/WASM force-exit seam ignores (it reads the gbrain-owned setCliExitVerdict). A NOT CONFORMANT target printed the banner but exited 0 — CI would green-light a non-conformant server. Now routes through setCliExitVerdict; proven: unknown subcommand exits 1. Regression tests: cross-source forget → not_found + foreign fact stays active; remote can't forget a private fact; entity card omits foreign-source backlinks from both edges and the count. Cathedral 1 ship-review fixes (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.43.0.0 feat(mcp): MEMORY_VERBS v1 — frozen five-verb memory protocol + conformance kit (cathedral 1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: surface --surface verbs in local-stdio quickstart paths for v0.43.0.0 The MEMORY_VERBS v1 quickstart flag (gbrain serve --surface verbs) was added to the README quickstart, CLAUDE_CODE.md, and CODEX.md during the build but three other docs that show the local-stdio wire-up still presented bare `gbrain serve`: the connect-coding-agent Path B walkthrough (the doc the README links to), INSTALL.md, and DEPLOY.md's Local stdio path. Brought them in line so the five-verb surface is consistent across every local-quickstart entry point. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(doctor): assign memory_verbs_usage check to the SKILL category set The new memory_verbs_usage doctor check (Cathedral 1, E4) was added without a category, tripping the doctor-categories drift guard (every check name must belong to exactly one category set). It's an agent-capability adoption signal, a sibling of retrieval_reflex_health — assigned to SKILL_CHECK_NAMES. Cathedral 1 ship-review fix (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(verbs): propagate MEMORY_VERBS v1 to existing harnesses on upgrade The verbs shipped reachable + self-describing, but an EXISTING install that upgrades to 0.43 and keeps its skills had no active push toward them: no post-upgrade notice (default surface stays 'full'), and no skill/guidance routing — agents kept calling search/query/get_page/extract_facts. Two additive propagation fixes: - Post-upgrade feature pitch: src/commands/migrations/v0_43_0.ts (pitch-only, no-op idempotent orchestrator — the verbs ride existing tables, no schema/ data migration). Registered in the migrations index so and the self-upgrade NOTIFY channel announce the five verbs + + on any upgrade past 0.43. Verified live: the pitch fires for an install upgrading from 0.42.43.0. - Skill routing: skills/query (memory reads) and skills/brain-ops (read/write cycle) now teach the verbs — recall/entity/synthesize vs search/get_page/ query, remember vs extract_facts, forget — with a fall-back-to-classic-ops note for older brains / full surface. The in-MCP tool descriptions already steered tool-list readers; this adds the narrative layer the audit found missing across all 29 skills. apply-migrations future-list fixtures updated for the new 0.43.0.0 registry entry. No DB migration; no contract change; verbs unaffected. Cathedral 1 propagation (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(verbs): green the 3 failing CI shards — 3-seg migration version, hermetic synthesize test, fresh llms Three real CI-shard failures from the propagation commit + master merges: - migrations-registry (shard 10): v0_43_0 used a 4-segment version 0.43.0.0, but the registry enforces 3-segment semver (/^\d+\.\d+\.\d+$/). Changed the migration version to 0.43.0 (the RELEASE stays 0.43.0.0; migrations have always used 3-seg, e.g. 0.32.2). apply-migrations future-list fixtures updated to match. - synthesize conformance test (shard 5): asserted a successful answer via the chat seam, but runThink builds its client through a real-key check (not the seam), so CI (credential-free) took the NO_ANTHROPIC_API_KEY path → the verb's unavailable conversion → isError. Now wraps the call in withEnv with a fake key AND the chat seam, so it's deterministic regardless of ambient credentials. - build-llms (shard 8): the v0.42.44.0 master merge changed an inlined doc; regenerated llms.txt/llms-full.txt. Cathedral 1 CI fixes (#cathedral-1) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(search): steer concept/landscape questions to query — descriptions, docs, lookup chain, CLI nudge (#2416) The search/query routing guidance shipped a stale and backwards model: SEARCH_DESCRIPTION still led with "Keyword search using full-text search" (search has been cheap-hybrid — vector + keyword + RRF, expansion off — since D4/D15), and the mandatory lookup chain in brain-first.md ordered search-first with query only "if search is thin". For concept/landscape questions ("all the X that do Y"), keyword+vector recall always returns something, so the fallback never fires and expansion-recoverable synonym-phrased matches drop silently. - operations-descriptions.ts: fix the stale SEARCH lead (cheap-hybrid, no LLM expansion); both constants now route concept/synonym/landscape questions to query, cap the claim honestly (both are top-K — exhaustive enumeration belongs to list_pages), and keep every pinned redirect. - brain-first.md: lookup chain is intent-driven (exact token → search; concept/landscape → query FIRST), cost claims corrected, and the anti-pattern is explicit: a nonzero search count is not a completeness signal. Same sweep in brain-ops/SKILL.md and query/SKILL.md, which duplicated the backwards order. - search-modes.md: decision tree + quick-ref updated to the cheap-hybrid reality, new Tricky Spot #6 (count ≠ completeness), example names moved to placeholder people per the privacy convention. RETRIEVAL.md gets the matching one-liner. - query-intent.ts: looksConceptShaped + conceptNudge — deterministic concept-shape classifier in the existing classifier home, tuned to favor false-negatives; cues owned by other routers (find_experts, salience ops) deliberately excluded. cli.ts prints the hint-only stderr nudge on BOTH result paths (local engine + thin-client routed), --quiet-gated; search stays the cheap hot path (never auto-reroutes). - salience-llm-routing e2e: new concept-phrasings block asserts the descriptions actually route concept questions to query (verified live: 20/20 — all 12 personal-query regressions still route to salience ops, all 8 concept phrasings route to query). - KEY_FILES.md: query-intent entry updated to current state (the old entry pointed at a renamed file); llms bundle regenerated (no diff — entry is link-only). - TODOS.md: MCP-envelope hint follow-up filed (remote agents are the higher-value half; needs a return-contract change kept out of this commit). Verification: typecheck clean; 70 targeted unit tests + 20 live routing evals green; all 24 check:all guards green; failing full-suite shards triaged — think-pipeline.serial.test.ts:434 fails identically on the clean tree (pre-existing on this branch, filed separately), the rest are PGLite parallel-load contention that pass in isolation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): pin think's stub-path usage at null per the E2 accounting contract The MEMORY_VERBS [E2] usage-accounting change deliberately moved "no LLM ran" from `undefined` to `null` (the frozen cost-block spelling), but the older #1698 stub-path test still pinned `undefined` and failed on every run of this branch. The protected semantic is unchanged: no fake usage or cost on the stub path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(changelog): cover the #2416 query-steering wave under v0.43.0.0 The 0.43.0.0 entry described only MEMORY_VERBS; the #2416 retrieval-routing wave rides the same release. Adds its user-facing summary and refreshes the release date to the actual ship date. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(protocol): reword so the #3502 docs-lint doesn't read a phantom command Master's docs-lint (landed after this branch forked) scans for `gbrain <verb>` references; the MEMORY_VERBS diagram's "gbrain reference implementation" phrasing parsed as a nonexistent `gbrain reference` command. Post-merge-only failure; both sides were green alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: update project documentation for v0.43.0.0 Post-ship /document-release drift pass over the MEMORY_VERBS v1 + #2416 query-steering waves: - docs/tutorials/connect-coding-agent.md: the pasteable brain-first protocol still taught the pre-#2416 "Search first" rule and named only full-surface tools; step 1 now routes by question shape (exact tokens -> search, concept/landscape -> query first) and both steps map the five-verb surface names (recall/synthesize/remember). find_experts and query callouts marked with their surface. - README.md + tutorial: "90-tool wall" count refreshed to 110 (the actual operation catalog). - CLAUDE.md: contract-first op count ~90 -> ~110 with the v0.43.0.0 MEMORY_VERBS clause; reference-map row for the memory-verbs protocol / --surface / conformance docs. - TODOS.md: blank line before the v0.42.74.0 section header. - llms-full.txt regenerated (build:llms; freshness test green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: apply cross-model doc-review fixes for v0.43.0.0 Verified findings from the /document-release Codex pass: - MEMORY_VERBS_v1.md: disclose that conformance WRITES (marker-suffixed seed page + remember/forget facts, write-scope creds, artifacts not auto-deleted); quickstart header says 4 commands because it has 4. - CHANGELOG v0.43.0.0: cost claim scoped per verb (budget/latency/cost block); remember "updates in place" -> supersedes-with-history (what write-single.ts actually does). - DEPLOY.md: legacy bearer fallback works on BOTH engines now (both schemas carry access_tokens per http-transport.ts) — was Postgres-only. - CLAUDE_CODE.md: list_skills/core-tools verify note marked full-surface (on --surface verbs those tools aren't on the surface). - skills/query/SKILL.md: --surface full INCLUDES the verbs; fallback is for pre-0.43 servers only. - skills/brain-ops/SKILL.md: search/query tool labels updated to the #2416 reality (cheap hybrid vs hybrid + LLM expansion). - search-modes.md: stale "keyword search returns chunks" label. - RETRIEVAL.md: note the query op defaults expand:true per call. - Counts: 43 skills -> 52 (README x2, INSTALL), 30 skills -> 52 (CLAUDE.md), "30+ tools over MCP" -> 110 (README). - TODOS.md: filed 2 code-vs-spec follow-ups (recall filter composition + limit cap; synthesize unavailable-mapping breadth) instead of silently editing the frozen contract. - llms-full.txt + skills.lock.json regenerated; freshness guards green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): regenerate the flag registry for the cathedral-1 command set Master's #2185 strict flag validation ships a committed, generated CLI_FLAG_REGISTRY with drift + freshness guards keyed off CLI_ONLY. The merge added this branch's `protocol` command to CLI_ONLY, but the committed registry was generated on master without it, failing CI shard 7's drift guard. Regenerated via `bun run build:flag-registry` on the merged tree (95 commands; picks up `protocol` and the `--surface` help-text mentions from the verbs work). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.43.0.0 |
||
|
|
130d321d23 |
v0.42.76.0 fix(upgrade,security,cli): strict flag validation, bootstrap-wedge class kill, federated chunk scope (#3902)
* fix(schema): bootstrap timeline_entries.event_page_id forward reference — un-wedge pre-v121 upgrades (#2626 #2594 #2579 #2537 #2536) v0.42.56.0 (Chronicle, migration v121) added timeline_entries.event_page_id and two partial indexes in the embedded schema blobs without extending applyForwardReferenceBootstrap — any brain whose timeline_entries predates v121 wedged initSchema at blob replay ("column event_page_id does not exist") before runMigrations could apply v121, with no in-band recovery. - Add the timeline_entries.event_page_id probe + column-only ALTER to applyForwardReferenceBootstrap in BOTH engines; FK + partial indexes land via the idempotent v121 / blob replay afterwards. Stays in the always-run bootstrap (never a migration hook — those skip oddly-stamped brains). - REQUIRED_BOOTSTRAP_COVERAGE entry + strip blocks in both runtime tests. - e2e: pre-v121 rewind → full initSchema converges (indexes re-created); wedged-brain recovery — a brain that already FAILED the upgrade attempt converges on retry with full final shape (column + FK + both partial indexes) and no ledger residue. Absorbs PR #2548 (@chetan-guevara) and the e2e test from PR #2623 (@colinagent) — thank you both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(schema): coverage guard cross-references migration-added columns — close the scan hole that shipped the v121 wedge The A2 static check treated a column as covered when the current CREATE TABLE body declared it. But a column that is BOTH in the blob's CREATE TABLE AND added by a migration is a forward reference by definition — on pre-existing tables CREATE TABLE IF NOT EXISTS no-ops and the blob's CREATE INDEX crashes initSchema before runMigrations can help. That mask is exactly how timeline_entries.event_page_id passed the guard while wedging every pre-v121 brain. - buildIndexRefCoveragePredicate: migration-added columns (from extractAddedColumnsFromMigrations over the MIGRATIONS array) require a bootstrap ALTER; CREATE TABLE presence no longer counts for them. - Unit test pins the v121 regression shape red/green with synthetic inputs; the A2 test pins the incident triple directly (migration-added + blob-indexed + bootstrap-covered). - The strengthened predicate immediately surfaced two more latent wedges of the same class: minion_jobs.timeout_at + minion_jobs.idempotency_key (migration v7, blob-indexed, unprobed). Added probes in both engines + coverage entries + runtime strip blocks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(security): serve --http hides the generated admin token from non-TTY output by default (#2624) Generated admin bootstrap tokens printed into container/log-aggregator stdout on every headless start. shouldSuppressBootstrapPrint now defaults to hidden unless stderr is an interactive TTY; env-sourced tokens are never printed; --print-admin-token is the explicit escape hatch for capturing the value on a trusted non-TTY start; --suppress-bootstrap-token still overrides everything. Unit-tested across all five postures. Absorbs PR #2625 (@irresi) — thank you. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(security): legacy bearer tokens honor permissions.takes_holders over serve --http (#2529) GBrainOAuthProvider.verifyAccessToken never returned takesHoldersAllowList, so the serve --http dispatch site always fell back to ['world'] — remote MCP callers with an operator-configured takes_holders grant saw only public takes. The legacy branch now extracts permissions.takes_holders exactly like src/mcp/http-transport.ts (fail-safe ['world'] default, non-string entries dropped, malformed permissions JSON fails closed without throwing), and AuthInfo carries the field as a typed contract. OAuth-registered clients have no takes_holders storage on oauth_clients; that lane is design work tracked in TODOS (column migration + DCR/ registration surface), not part of this hotfix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(security): get_chunks honors the federated source grant + stops shipping embedding vectors (#2555, getChunks half of #2544) The get_chunks op still used the pre-#2200 scalar pattern (ctx.sourceId ? {sourceId} : {}) and engine.getChunks had no sourceIds[] support — a federated client that could read a page via get_page got [] from get_chunks. The op now routes through sourceScopeOpts (canonical ladder: federated array > scalar floor > nothing) and both engines gain the getPage-style sourceIds[] precedence branch; the unset-opts 'default' floor is preserved for local callers (importCodeFile contract). While in the function: SELECT cc.* pulled every embedding vector over the wire per chunk only for rowToChunk to discard them — replaced with the explicit non-vector column list in both engines (the getChunks half of #2544; the per-put_page getAllSlugs half is tracked separately). getChunksWithEmbeddings stays scalar-only by design (engine-internal, zero remote-reachable callers — documented at the interface). Tests: op-level federated repro + isolation + default-floor bleed guard (PGLite), engine precedence + Chunk-shape pin, and a DATABASE_URL-gated engine-parity test covering all three scope shapes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(auth): /admin/api/register-client accepts source + federatedRead bindings (#2143 enabler) The HTTP register endpoint hardcoded source_id='default' and federated_read=undefined — only the CLI could mint a client bound to a non-default source, so HTTP-registered MCP clients wrote into 'default' regardless of intent. The endpoint now accepts optional source / federatedRead body fields, validated via assertValidSourceId with a structured 400 on bad input; omitting both preserves the historical default. The admin-UI form layer is a tracked follow-up. Absorbs PR #2016 — thank you. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scope,calibration): think reads over MCP; source-scoped takes reads; calibration CLI reachability + model resolution (#2078-class #2451) - think op → scope:'read' for OAuth/MCP clients: the handler already forces save/take off for remote callers before persistence, so a read-scoped token can think without a write grant; local CLI persistence unchanged. Scope-annotation test carries an explicit remote-gated allowlist. - takes_list / takes_search / takes_scorecard / takes_calibration route through sourceScopeOpts (the #2200 class on the takes read lane) with engine support in BOTH engines + tests. - 'calibration' added to CLI_ONLY (the command was registered but unreachable — dispatch-gap class) and calibration_profile/voice-gate resolve models through the canonical gateway tier resolver instead of bare ids that parseModelId rejects. - BigInt-safe local-op output normalization (bigintToStringReplacer, postgres.js wire parity) — first half of the #2450 fix; the formatResult default case lands with the cli-output commit. Absorbs PR #2598 (@colinagent) and PR #2452 (@spinsirr) — thank you both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): output correctness — BigInt-safe rendering, search --json, files bigint, and 5 unreachable commands (#2450 #2527 #2042 #2035-class) - normalizeLocalResult wraps the local-op output round-trip with the bigint→string replacer (postgres.js wire parity; a bare stringify THROWS on BIGSERIAL keys); formatResult's default renderer gets the same replacer so nothing upstream can crash it. - search/query --json: CLI-local formatter flag threaded through the shared formatter — stdout is a parseable result array, never human text on the --json path (the #2042 residual). - file_list normalizes size_bytes (Postgres BIGINT → Number) so MCP serialization and the CLI KB math survive; null preserved. - NEW dispatch-gap guard: every handleCliOnly top-level case label must be reachable via CLI_ONLY. It immediately caught FIVE live unreachable commands: pages, backfill, reconcile-links, notability-eval (added to CLI_ONLY), and the documented 'gbrain search modes|stats|tune' dashboards (pre-fix, 'search modes' silently keyword-searched the word "modes") — now routed via a pre-dispatch subcommand gate. 'whoknows' stays on its op-alias route (collision guard); tracked with PR #2509. Absorbs PR #2494 and PR #2531 (@javieraldape) and adapts PR #472 (@vinsew) — thank you. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(db): updateSourceConfig survives mixed-array config rows + repair/doctor cover the subagent jsonb columns (#2251) The array-coercion branch called jsonb_each(elem) bare — a mixed array (e.g. '["x", {"last_full_cycle_at": ...}]') threw 'cannot call jsonb_each on a non-object' DURING row production, permanently failing every subsequent updateSourceConfig (last_full_cycle_at could never be written again). Non-object elements are now neutralized inline via a CASE-guarded jsonb_each; the row self-heals to a flat object on the next write, object elements' keys recovered. Pinned ungated on PGLite (real Postgres semantics) and via a DATABASE_URL-gated e2e on the real engine. repair-jsonb + doctor's jsonb_integrity check extend from 5 to 8 columns (subagent_messages.content_blocks, subagent_tool_executions.input/output — historical damage rows from the pre-v0.42.53.0 positional double-encode; the write paths themselves were fixed in #2375) with a to_regclass skip for brains predating those tables. Adapts the repair/doctor extension from PR #597 (@vinsew) — thank you. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): dry-run honesty — strict unknown-flag rejection CLI-wide + unify-types worker defaults to dry-run (#2185 #1575) #2185: 'gbrain init --migrate-only --dry-run' applied REAL migrations — flags are read ad hoc (args.includes) so anything a handler doesn't look for was silently ignored, including intent-bearing safety flags. The CLI now validates every flag pre-dispatch and pre-engine: - Op commands validate against the operation contract (op.params + CLI-local --json/--explain), mirroring parseOpArgs so flag values that begin with '--' are never misread. parseOpArgs also gains the --key=value inline form (previously parsed as a junk key that consumed the NEXT token). - CLI_ONLY commands validate against a GENERATED per-command registry (scripts/generate-flag-registry.ts scans each command's case block + imported modules + one level of relative imports; deliberately over-inclusive so a missed flag can't break a working invocation). Committed as src/core/cli-flag-registry.generated.ts; 'bun run build:flag-registry' regenerates. - Passthrough by construction: everything after '--', plus call / config / 'jobs submit' payloads (handler-defined params are their contract). - Guards: sweep test (every command × nonsense flag → error), acceptance tests (real flags, --no- negation, = form, -- passthrough), drift guard (every CLI_ONLY member has an entry), freshness guard (committed registry == fresh generator run), subprocess smokes incl. the literal #2185 repro failing loud with zero engine work. BREAKING: scripts passing stray flags now fail loud with "Unknown flag --x for 'gbrain <cmd>'" — that is the point. #1575: the unify-types worker registration passed apply ?? true while the handler documents 'Default false (dry-run)' — the canonical operator invocation destructively retyped 25K+ pages by default. Now ?? false with a structural test; explicit --params '{"apply":true}' is the only way to mutate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(todos): file fix-wave 1 follow-ups (OAuth takes_holders design, parseFlags end-state, whoknows routing, #2544 half, #1558 UI, #2536 diagnostics) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): memory-safe unit runner — adaptive concurrency + serial OOM rescue pass A default run (4 shards × 4 intra-shard files) holds up to 16 concurrent PGLite WASM instances (~1.5GB each). With sibling Conductor workspaces running their own suites, PGLite connect failed with 'Out of memory' across every shard at once — 369 phantom test failures on a healthy branch, indistinguishable from real breakage at a glance. Two default-on layers in scripts/run-unit-parallel.sh: 1. Memory-aware sizing: total concurrency is capped to available memory (vm_stat on macOS, MemAvailable on Linux) at GBRAIN_TEST_MEM_PER_FILE_MB (default 1536) per concurrent file, shedding shards before intra-shard width. Quiet machines are unaffected (banner: mem-ok); pressured ones degrade instead of OOMing (banner: mem-adapted AxB→CxD). 2. Serial OOM rescue: failures whose shard log carries the WASM out-of-memory signature are re-run at --max-concurrency 1 after the fan-out drains. Phantoms pass serially → run goes green with an oom_rescued note and the failure blocks marked superseded; real failures fail again and stay red. Plain assertion failures never match the signature and never enter the rescue lane (existing exit-code and failure-log contract tests unchanged). Escape hatches: GBRAIN_TEST_NO_MEM_ADAPT=1, GBRAIN_TEST_NO_OOM_FALLBACK=1. Tests: OOM-once fixture rescued to exit 0; kill-switch stays red; banner advertises the sizing verdict. Documented in docs/TESTING.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test,cli): merge-seam repairs — shard timeout for the tripled suite, init flag-error contract, think scope exemption Three post-merge repairs surfaced by the first full-suite run: - Shard timeout 1500s -> 3000s: the suite roughly tripled since the cap was sized (~3900 -> 11k+ tests; PGLite inits replay 120 migrations, was 92). Two shards were killed mid-progress at 1500s. - The #2185 pre-dispatch validator now emits the same error contract as init.ts's in-handler check it preempts: lowercase 'unknown flag' on stderr + structured {status:'error', reason:'invalid_flag'} on stdout for --json callers (pinned by test/init-migrate-only.test.ts). - test/operations-trust-boundary.test.ts gets the same documented remote-gated allowlist for think's read scope (#2598) that test/oauth.test.ts already carries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): rescue lane also covers externally-killed shards (sibling-workspace pkill / memory jetsam) Second phantom class observed on a multi-workspace Conductor machine: 3 shards SIGTERM'd + 1 SIGKILL'd at ~700s under a 3000s cap, all mid-progress — an external killer, not a wedge. The dead shards then poisoned the serial pass (lock/state residue → 18 more phantoms), and every one of the 18 passed standalone. The runner now stamps per-shard start/end epochs; a shard dying on 143/137 before 80% of SHARD_TIMEOUT is classified externally-killed and its file list joins the serial rescue queue (real wedges die AT the cap and stay red). Serial-pass failures that occur while any shard was externally killed are treated as suspect residue and rescued too. Structural tests pin the detector, threshold, and routing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(auth): keep tokenEndpointAuthMethod terminal in the register-client destructure (PKCE structural contract) The #2016 absorb appended source/federatedRead after tokenEndpointAuthMethod; test/fix-wave-structural.test.ts pins tokenEndpointAuthMethod as the final destructured field (v0.36.1.x #1077 PKCE regression contract). The added fields move into the regex's optional-middle slot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): re-apply the #2598 remote-gated scope exemption to master's oauth scope-annotation test Taking master's v0.42.74.0 oauth.test.ts (its #2529 implementation) dropped the think read-scope allowlist that PR #2598 carries; re-applied to match test/operations-trust-boundary.test.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(review): pre-landing review batch — CLI-local flag acceptance, json= coherence, re-landed getChunks trim, payload-aware jsonb repair, rescue-lane tightening Ship Step 9 findings (checklist pass + 4 specialists), all verified before fixing: - CRITICAL: the #2185 validator rejected --source/--dry-run on op commands (makeContext CLI-locals consumed outside the op contract) — 'gbrain search "x" --source y' exited 1. Exempted with parser-mirroring value consumption + unit/subprocess tests incl. global-flag acceptance. - CRITICAL: --json=<v> diverged between validator (accepted) and parseOpArgs (junk-key path consumed the NEXT token, corrupting positionals). Parser now handles --json=true|false; =-forms of bare-only CLI-locals reject loud. parseOpArgs inline-= suite added (regression rule). - CRITICAL: the master merge silently restored SELECT cc.* in both engines' getChunks while docs claimed the #2544 trim. Re-landed the explicit non-vector column list + a source-level structural pin so a merge can't silently undo it again. - repair-jsonb/doctor: the subagent columns legitimately hold jsonb string scalars (persistToolExec binds pre-serialized strings) — unconditional unwrap would abort the repair run or corrupt legit values. jsonPayloadOnly predicate (JSON-container content only) on those 3 targets, mirrored in doctor + parameterized to_regclass + behavioral test (damage flagged, legit string ignored, absent table skipped). - runner rescue-lane tightening: serial failures rescue-eligible only with their own OOM signature or after an external shard kill (residue), never because a sibling shard OOM'd — flaky serial tests stay red. Rescue passes no longer double-count into TOTAL_PASS; shard timeout scales when mem-adaptation sheds shards; negative-path tests (mixed run stays red, deterministic OOM-signature failure stays red). - fail-closed remote spelling at 2 forward sites (ctx.remote !== false per the CLAUDE.md invariant); registry generator drops template-literal flag prefixes; real-PG e2es for the v121 + minion_jobs wedge classes; stale comments corrected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: regenerate flag registry after master merge (#3864 added extract help flags) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(review): red-team batch — dispatch-order validation, real --dry-run boolean, rescue-lane timeout/isolation/cap, parseable-payload repair gate Red-team pass (post-specialist) findings, all verified before fixing: - CRITICAL: validateCommandFlags checked the op lane before CLI_ONLY while dispatch runs CLI_ONLY first — dual-lane commands (think/salience/ anomalies) were validated against the WRONG contract, rejecting documented invocations ('salience --kind entity'). Lane order now mirrors dispatch. - CRITICAL: --dry-run was blessed as legal on op commands but parseOpArgs never SET it (trailing → nothing → ctx.dryRun false → the REAL destructive action ran; leading → consumed the next token). Now a CLI-local boolean exactly like --json, with --dry-run=false support and regression tests. - CRITICAL: the rescue lane ran bun test WITHOUT --timeout=60000 (bun default 5s) — PGLite phantoms re-failed on timeout and were mislabeled 'confirmed real'. Both rescue invocations now mirror the shard flags; serial files re-run one process per file (run-serial-tests.sh isolation contract); rescue wallclock capped at 2x the shard timeout. - CRITICAL: the jsonPayloadOnly probe matched container-LOOKING invalid JSON ('[INFO] fetch complete') whose repair cast would throw and abort the run mid-loop. Predicate now gates on pg_input_is_valid (PG16+, same floor as the existing IS JSON usage) + per-target catch records and continues; doctor mirrors; behavioral test covers the lookalike row. - Registry generator bounded at handleCliOnly's closing brace (the LAST case block absorbed ~100 junk flags from the rest of cli.ts, neutering strict validation for it); uppercase flag typos reject loudly in both lanes (handlers are lowercase-sensitive); --json=true spelling gets the structured invalid_flag envelope; get_chunks __all__ narrowing filed as a Wave 3 TODO. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(engines): getChunks trimmed SELECT must carry modality (codex P1-B) The #2544 egress trim replaced SELECT cc.* with an explicit column list but omitted cc.modality — every rowToChunk field except the vector must survive the trim, or the embed round-trip (getChunks -> upsertChunks) rewrites image chunks as text. Both engines; the structural pin now iterates the full rowToChunk field list instead of spot-checking. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(cli): safety flags require consumption evidence in the flag registry (codex P1-A) upgrade.ts prints a help hint naming another command's --dry-run; that literal is depth-0 text for post-upgrade, so the generator allowlisted --dry-run there — recreating the exact #2185 repro this wave kills (post-upgrade --dry-run accepted, ignored, migrations run for real). Safety flags now need a tight-quoted standalone literal (an args read like has('--dry-run')) before the registry grants them; prose bleed embeds the flag inside a longer string and never qualifies. Regenerated registry drops --dry-run from post-upgrade, keeps genuine consumers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.42.76.0 fix(upgrade,security,cli): strict flag validation, bootstrap-wedge class kill, federated chunk scope Version bump + CHANGELOG for the fix wave: CLI-wide unknown-flag rejection with a generated per-command registry (#2185, #1575 class), minion_jobs bootstrap probes + migration-aware coverage guard (v121 wedge class), get_chunks federated scope + egress trim (#2555, half of #2544), think read-scope over MCP (#2598), register-client source bindings (#2016, #2143 enabler), repair-jsonb/doctor subagent columns with a parse-validated damage predicate, memory-safe unit-test runner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: update project documentation for v0.42.76.0 CONTRIBUTING.md: test-runner claims match the memory-safe 4-shard default (was 8-shard) and the CLI-only command recipe now includes the build:flag-registry regen step. KEY_FILES.md: repair-jsonb entry updated to the 8-column parse-gated current state; new entries for the strict flag-validation subsystem and src/core/source-id.ts; operations/engine/ serve-http entries updated for get_chunks scope ladder + trimmed SELECT and the register-client HTTP source bindings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: apply doc-review fixes for v0.42.76.0 Cross-model doc review caught stragglers: docs/TESTING.md still said 8-shard in the file taxonomy, carried a two-generations-stale shard timeout default (600s -> 3000s), and didn't name the new run-unit-parallel regression test or the remaining runner knobs; CONTRIBUTING.md's fast-loop file count predated the tripled suite (92+ -> 1000+); test-count claims unified at 3700+; the CLIENT_FENCED_WRITE_OPS comment in operations.ts still described think as scope write; KEY_FILES names the exported findUnknownFlag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): rescue lane survives CI — strip ::group:: prefixes, satisfy the bun-test-timeout guard Two CI-only breaks from the master merge: (1) under GITHUB_ACTIONS the shard wraps file sections as ::group::path.test.ts, so the rescue pass extracted literal ::group:: non-paths that matched zero test files — failing_files_in_log now strips the prefix; (2) master's new check-bun-test-timeout guard greps for bare 'bun test' and tripped on run_rescue's comment text (the invocations themselves carry --timeout=60000) — comment reworded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(test): shard-mechanics tests disable mem-adaptation — CI's 7GB runner collapsed explicit 2 shards to 1 The runner deliberately adapts even explicit --shards to available memory (GBRAIN_TEST_NO_MEM_ADAPT=1 is the escape hatch); on GitHub's ~7GB runners that collapsed the tests' 2-shard sandbox runs to 1 shard, breaking every 'shard 1/2:' expectation while passing locally. The tests pin shard MECHANICS with tiny synthetic files, so they now set the escape hatch; the one test that checks the mem banner overrides it back on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.42.76.0 |
||
|
|
485c4773ca |
fix(think): extend 16K output headroom to OpenAI reasoning models (#3916)
maxOutputTokensFor() gave the 16000-token output budget only to thinking-by-default Anthropic Claude 5 models; every other model kept the 4000 default. OpenAI reasoning models (the gpt-5 family and the numbered o-series) bill internal reasoning tokens as output tokens counting against max_tokens, so the 4000 cap starved think's actual answer the same way. Add OPENAI_REASONING_MODEL_RE alongside the existing Claude-5 regex, deliberately scoped to the gpt-5 family + numbered o-series with the non-reasoning ChatGPT -chat snapshots (gpt-5-chat-latest, gpt-5.2-chat-latest) excluded. Defaults (4000/16000) are unchanged and no config knob is added; gpt-4o, other providers, and provider-prefixed spellings outside openai:/openai/ keep 4000. Tests pin the new 16000 cases (colon + slash forms), the chat-snapshot and codex-mini-latest exclusions, and name/version boundaries (gpt-50, o3foo, bare model names, openrouter-nested ids). Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f8878be54e |
fix(cycle): price synthesize_concepts estimates from the canonical table (#3915)
The T1/T2 narrative cost estimate hardcoded Sonnet rates ($3/M in, $15/M out) regardless of which model actually answered, violating the one-canonical-chat-pricing-table invariant. On non-Sonnet models (e.g. openai:gpt-5.2) the ~2.4x over-estimate tripped the $1.50 budget gate early — degrading remaining T1/T2 groups to the deterministic template with budget still left — and persisted the inflated cost into receipts and rollups. The estimate now resolves through canonicalLookup(result.model), the model that actually answered. A canonical miss falls back to Sonnet-tier pricing (derived from the canonical table, not hand-copied), mirroring skillopt/preflight's lookupPrice policy: conservative, non-throwing, and it keeps the budget gate effective. The $1.50 gate, concept-generation logic, and model resolution are untouched (model resolution is in flight separately on the same file). Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0df56f1efa |
docs(brainstorm): correct estimateCost's cost figures to match its own formula (#3905)
The doc comment claimed brainstorm ~$0.05-0.15 and lsd ~$0.20-0.40 while
stating it anchors on Sonnet pricing. Plugging the function's own token
budgets (3000 in/cross, 250 out/idea, 350 judge-in/idea, 200 judge-out/idea)
and the profile constants (brainstorm 4x6x3, lsd 2x12x4) into Sonnet's
canonical $3/M-in $15/M-out yields ~$0.78 and ~$0.96 — roughly 5x and
3x the stated ranges. No single model in the canonical pricing table makes
both stated ranges true, and the comment, formula, and profile constants
were all introduced in the same commit (
|
||
|
|
01d9639204 | fix(facts): salvage valid extractor candidates (#3894) | ||
|
|
f15480b9d0 |
v0.42.75.0 fix(pglite): in-place WAL auto-repair for the macOS Aborted() startup crash (#2575, #223, #1670) (#3901)
* fix(pglite): in-place WAL auto-repair for the Aborted() startup crash (#223, #1670, #2575) The 'macOS 26.x WASM bug' was a misdiagnosis: an unclean shutdown (typically the OS-upgrade reboot) tears the data dir's WAL, and every subsequent open fails WAL replay inside WASM with an opaque RuntimeError: Aborted(). This ports the pg_resetwal recovery upstream rejected (electric-sql/pglite#994, by @yestheboxer) and wires it into connect() as bounded auto-repair: - src/core/pglite-resetwal.ts: pg_resetwal for PG17 NodeFS dirs, fail-closed layout validation, atomic+durable writes (tmp+fsync+rename), idempotent. - src/core/pglite-repair.ts: whole-pg_wal-dir rename backup (zero transient disk), overwrite-order restore with mtime guard, cooldown sidecar + episode-scoped backup retention (newest 3 episodes), and a never-throws engine seam. Kill-switch: GBRAIN_PGLITE_WAL_REPAIR=off. - pglite-engine.ts: verdict rename macos-26-3 -> wasm-abort, classifier now matches the real production message (it previously fell to 'unknown'), corrupt-beats-wasm precedence preserved, honest per-outcome error copy incl. the failed-not-restored arm, and repair only under a cleanly-acquired lock (new LockHandle.reaped provenance; never after reaping a holder). - gbrain pglite-repair: manual dry-run/repair command (validate-before-lock, serve/reaped refusals, no --force by design). - doctor: pglite_data_dir fs-check with recurrence escalation and backup inventory when a PGLite brain fails to connect. - reinit-pglite: embedding flags default from file-only config so the recovery ladder's rebuild rung works bare mid-outage. - stringifyPgliteInitError: message-less Emscripten ErrnoError objects no longer surface as [object Object]. Regression-tested against real brains: corrupt every WAL segment (truncate and garbage variants), reopen, auto-repair fires, original rows readable, process.exitCode stays contained (#2084). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(pglite): replace the macOS-26.x misdiagnosis with the corrupt-WAL recovery ladder README + INSTALL.md shipped (via #1671) the claim that PGLite is incompatible with macOS 26.x and that a Bun/WASM fix would restore it. The real cause is torn WAL state from the upgrade reboot, now auto-repaired in place. Rewrites those sections around the recovery ladder (auto-repair -> gbrain pglite-repair -> reinit-pglite -> engine switch; native-Postgres recipe kept, credit @roysaurav), adds the ENGINES.md troubleshooting section, updates the KEY_FILES.md entries to current truth, files the two follow-up TODOs (SIGTERM engine-close extension; pglite upgrade blocker), and regenerates the llms bundles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pglite): harden WAL auto-repair (pre-landing + adversarial review) Review-army (security/testing/maintainability/perf) + Claude & Codex adversarial passes on the WAL-repair wave. Correctness + safety hardening, no behavior change to the happy path: - Live-writer safety: repair refuses any reaped lock acquisition, a corrupt (unknowable-liveness) reap writes a cross-process quarantine marker that gates auto-repair AND the manual command for 10 min, isProcessAlive treats only ESRCH as dead (EPERM/malformed-pid read as alive), and a live postmaster.pid (native Postgres) is refused. Lock heartbeat + initial write are atomic (tmp+rename) so a torn read can't misclassify a healthy holder; an in-flight acquisition is no longer mistaken for corrupt. - resetWal verifies the stored pg_control CRC before trusting/re-signing it — a damaged control file routes to rebuild instead of laundering corrupt checkpoint counters under a fresh CRC. Atomic 'wx' writes (no symlink follow), whole-pg_wal-dir rename backup, 64MB seg-size cap. - Honest failure reporting: repairPgliteWal threads the real restore result out via WalRepairError so the 'failed-restored' vs 'failed-not-restored' message never lies; the not-restored copy names the correct restore paths. - Episode lifecycle: episodes close on the next healthy connect (not just on a verified repair), a gutted (restored) backup loses its pin, stale (>24h) episode backups aren't reused, and the cooldown also caps repaired-only crash loops. Empty backup dirs are pruned on refusal. - Command: rejects unknown flags and valueless --path (a destructive command must not silently mis-parse), confirm prompt goes to stderr (stdout stays clean for --json), embedding-flag defaults come from the config file only. - Symlink confinement extended to global/; sidecar reuse path validated (prefix + no '..' + must still hold pg_wal); sidecar writes atomic. - doctor recurrence escalation counts all attempts; data dir absolutized. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(pglite): current-state KEY_FILES + WAL-repair follow-up TODOs KEY_FILES.md pglite entries updated to the hardened truth (reap marker + quarantine, atomic writes, CRC gate, global-symlink refusal, WalRepairError, episode lifecycle). TODOS.md files the deferred judgment-call follow-ups (unclean-shutdown gate on auto-repair; non-gbrain pglite consumer boundary; mixed-version torn-lock double-read). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: bump version and changelog (v0.42.75.0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.42.75.0 |
||
|
|
0b47afbf40 | fix(doctor): resolve MSYS executable paths (#3865) | ||
|
|
9f81e1d8b2 | fix(cli): expose extract flags in help (#3864) | ||
|
|
86555e39bb |
fix(autopilot): honor source pull policy (#3861)
Fixes #3835 Use a canonical positive pull flag for sync and autopilot-cycle jobs while preserving legacy noPull payloads. Parse source config consistently so local-only sources skip pulls and PGLite remote sources still pull. Test: bun test test/job-pull-policy.test.ts test/autopilot-fanout.test.ts test/autopilot-fanout-wiring.test.ts test/sources-load.test.ts |
||
|
|
3257758492 | fix(doctor): align maintenance scope with canonical files (#3854) | ||
|
|
a948dfd6e2 |
v0.42.74.0 fix(security): honor takes_holders over serve --http + agent-voice default-deny CORS (#2529 #2477) (#3868)
* fix(auth): honor permissions.takes_holders for legacy bearer tokens over serve --http (#2529) The OAuth provider's legacy access_tokens branch parsed permissions.source_id but never read permissions.takes_holders, so the /mcp dispatch site's fail-closed default pinned every remote caller to world-only takes visibility — set-takes-holders was a silent no-op over serve --http, in both directions (grants above world never applied; restrictions below world didn't either). - src/core/legacy-token-scope.ts: new parseTakesHoldersAllowList shared by BOTH transports (the drift between the legacy HTTP transport's correct inline parse and the OAuth provider is how this bug shipped). [] preserved as explicit deny-all; non-array → undefined → consumer defaults ['world']. - src/core/operations.ts: AuthInfo.takesHoldersAllowList typed field (same ride-along as sourceId/allowedSources). - src/core/oauth-provider.ts: legacy branch threads the stored grant. OAuth-client tokens unchanged (no per-client storage — TODO filed). - src/mcp/http-transport.ts: converged on the shared helper (behavior no-op). - src/commands/serve-http.ts: sidecar cast replaced by the typed field. - src/core/facts/meta-hook.ts: hashAllowList gives [] its own cache key (cache identity only — payload filtering stays visibility-based). Tests: 7 verifyAccessToken cases (grant/absent/garbage/deny-all/mixed/ oauth-client/column-default), pure-helper describe, meta-hook cache-key pin, and a Postgres e2e (test/e2e/serve-http-takes-holders.test.ts) pinning the issue repro end-to-end over POST /mcp — the seam that had no coverage. Reported by @Derek95king. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(agent-voice): default-deny CORS + origin gate + loopback bind in reference server (#2477) The recipe reference server reflected any Origin into Access-Control-Allow-Origin on every route, including the unauthenticated side-effectful POSTs. Recipe is install_kind: copy-into-host-repo, so the reference is the source of truth for every future install. - Default-deny CORS: headers emitted only for exact matches against the new AGENT_VOICE_CORS_ORIGIN allowlist (comma-separated), with Vary: Origin; Allow-Credentials never set. Same-origin /call needs no configuration. - Origin gate on /session and /tool: CORS headers gate response reads, not request sends — a no-preflight "simple" cross-origin POST still executes. Disallowed Origins now 403 before any body read / upstream fetch / tool dispatch. No-Origin callers (curl, Twilio, native) and same-origin pages (Origin host == Host, tunnels included) pass. DNS rebinding stays a documented production-checklist item (TODO filed). - Loopback-default bind: HOST env, default 127.0.0.1 (mirrors gbrain serve --http --bind default); HOST=0.0.0.0 for containers/LAN. - Startup log prints the bind + CORS posture; recipe md + install manifest bumped to 0.1.1 with the production checklist rewritten to match. Tests: test/agent-voice-cors.serial.test.ts spawns the real server twice and pins default-deny, allowlist echo + trimming, preflight behavior, and the gate's ordering (evil-origin 403 vs no-origin reaching the handler). Reported by @sebastiondev. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: agent-voice origin gate fails closed on malformed Origin (#2477) Coverage-audit follow-up: a cross-origin POST with an unparseable Origin header must 403 (new URL() throws → originAllowed returns false), never fall through to the handler. A bypass here would defeat the gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: pin loopback-bind default + wire-level fail-closed takes default (#2529 #2477) Pre-landing review (testing specialist) flagged two revert-catching gaps: - #2477 HOST default (127.0.0.1) had no assertion — a regression to all-interfaces would pass every test. Capture the server's startup log and assert the loopback bind; add a HOST=0.0.0.0 override case. - #2529 serve-http `?? ['world']` default branch was only unit-covered. Add a 4th e2e case: a legacy token with no takes_holders key sees world-held takes but NOT brain-held ones over POST /mcp, pinning the fail-closed default end-to-end. Also assert the deny-all case returns a successful (non-error) tool result so the negatives can't pass vacuously. Verified: agent-voice 4/4, serve-http-takes-holders e2e 4/4 (real Postgres). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(auth): shared permissions decode + collision-free hot-memory cache key (#2529) Adversarial-review hardening on the #2529 fix: - The two transports shared parseTakesHoldersAllowList but still decoded the permissions column differently: the OAuth provider JSON.parse'd a string-typed value, the legacy HTTP transport didn't. On a double-encoded jsonb string scalar (#2339 class) a deny-all token would fail open to ['world'] on the HTTP transport while the provider honored it. Extract coerceLegacyPermissions into the shared module and route both through it, so "the two transports cannot drift" is literally true (shared decode + shared parse). Arrays/scalars/malformed strings → undefined (no grant). - hashAllowList used bare sentinels ('_' for undefined, '(empty)' for []), which collided with real holder values ['_'] and ['(empty)']. Encode collision-free (undefined → 'none', else JSON.stringify(sorted)) so the []-vs-undefined cache separation the #2529 change relies on holds for every holder value. Tests: coerceLegacyPermissions unit cases (object/JSON-string/malformed/ array/scalar), all existing takes-holders + meta-hook + e2e suites green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * v0.42.74.0 fix(security): honor takes_holders over serve --http + agent-voice default-deny CORS (#2529 #2477) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: document agent-voice HOST + AGENT_VOICE_CORS_ORIGIN env vars in install hint (v0.42.74.0) The post-install hint's env-var quick-start predated the #2477 hardening and listed neither the loopback-default HOST bind nor the default-deny AGENT_VOICE_CORS_ORIGIN allowlist. Add both as optional entries (safe by default) and refresh the stale startup-log line to match the server's actual loopback-bind output. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: correct CHANGELOG command guidance + TODOS filing version (#2529 #2477) Ship-stage document-release caught two wrong CLI invocations in the v0.42.74.0 CHANGELOG "To take advantage" block: `gbrain auth permissions <token>` has no read-only view form (that shape errors + exits 1 — set the scope directly with `set-takes-holders <values>`), and `integrations install agent-voice --refresh` requires `--target <host-repo>`. Also correct the follow-ups TODO header from the plan's stale v0.42.56.0 guess to the actual ship version v0.42.74.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(deps): bump js-yaml to 3.15.1 — osv GHSA-5p4m-2wfm-xmqj (#2529 #2477) osv-scan flagged js-yaml@3.15.0 (High, CVSS 7.5), fixed in 3.15.1. The transitive copy (gray-matter → js-yaml) was pinned to ^3.15.0 by the package.json `overrides` block; bump both the direct dependency and the override to ^3.15.1 so the vulnerable version is gone from bun.lock entirely (gray-matter/js-yaml now resolves to 3.15.1). Patch bump, in-range, frontmatter parsing verified (markdown + frontmatter + import + oauth suites green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(deps): bump admin nanoid to 3.3.18 — osv GHSA-2v37-7h3g-55p8 (#2529 #2477) osv-scan flagged nanoid@3.3.16 (High, CVSS 8.2) in admin/bun.lock, fixed in 3.3.17. nanoid is transitive (postcss → nanoid), so pin it in the admin overrides block; refresh resolves to 3.3.18 (latest patched 3.x). Admin SPA build verified green; both root and admin lockfiles now scan clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>v0.42.74.0 |
||
|
|
15b9863d13 |
v0.42.73.2 fix(security): fence dedup-resolved writes to the caller's own write scope (#3809)
* fix(security): fence the dedup-resolved slug under the caller's own confinement put_page's resolved-slug re-check tested `ctx.auth.boundSlugPrefixes` only. The delegated submit_agent -> subagent context carries `viaSubagent` + `allowedSlugPrefixes` but no `auth`, so a slug-bound client holding `agent` scope could delegate a write and have importFromContent's dedup pre-check redirect it onto a page outside its grant — where the disk write-through then re-rendered the victim's file with the caller's provenance. The re-check now applies whichever confinement the caller is actually under (OAuth binding and/or subagent allow-list / legacy namespace) via `slugOutsideCallerFence`, which composes the existing match rules rather than re-deriving them. Dedup returns status 'skipped' before any DB write, so the throw still rolls nothing back. The denial does not name the resolved slug (slug-enumeration oracle) and reads "your write scope", since either confinement can trigger it. Reported privately by Aleksei Razsadin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test: coverage for the OAuth in-fence redirect and the missing-subagentId guard * v0.42.73.2 fix(security): fence dedup-resolved writes to the caller's own write scope VERSION + package.json + CHANGELOG for 0.42.73.2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: state that the write fence follows a delegated write --------- Co-authored-by: Garry Tan <garrytan@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>v0.42.73.2 |
||
|
|
aecb33e795 |
v0.42.73.1 revert(ci): remove the PR gate and withdraw the v0.42.72.1 contribution requirements (#3805)
* v0.42.73.1 revert(ci): remove the PR gate — it cannot function on this repository
The gate needs an ANTHROPIC_API_KEY Actions secret for its verdict and
read-write workflow permissions to post a comment or set a label. This
repository grants neither, and both are admin-only, so it can only ever skip.
It also caused a real incident before that was understood: a read-only token
turned every API call into a 403, the code treated that as a crash, and the
check went red on an outside contributor's PR four times with no comment
explaining why. v0.42.73.0 fixed the crash, but a check that runs on every PR
and can never reach a verdict does not earn a place in the repo.
Removes the workflow, the script, its type surface, and its test file. The code
is preserved in git history at v0.42.73.0. If it is ever restored, the
mechanical half (intent/screenshot policy, title rule, red flags) should render
to the Actions job summary rather than a comment — that needs no token
permission and no API key.
CONTRIBUTING.md's intent-paragraph and screenshot requirement is unchanged and
stands as written; it is enforced by maintainers reading PRs, as before.
typecheck clean, verify 34/34, llms bundles regenerated.
* v0.42.73.1 revert(docs): withdraw the human-intent-paragraph + screenshot contribution requirement
This reverts commit
v0.42.73.1
|
||
|
|
e5dee4fb78 |
fix(deps): bump four override pins past newly-published CVEs
osv-scan passed on the last two release PRs and fails on this one, so these are newly published, not tolerated debt: 2 High + 4 Medium across fast-uri (3.1.4 → 3.1.5), hono (4.12.25 → 4.12.34), ip-address (10.1.1 → 10.3.1), and admin's postcss (8.5.10 → 8.5.23). All four are transitive and pinned through the overrides block, so the fix is the pin, not a dependency addition. Resolved: fast-uri@3.1.5, hono@4.13.0, ip-address@10.4.0, postcss@8.5.25.v0.42.73.0 |
||
|
|
3c26f2eaf2 |
fix(ci): a permission failure must never red-X a contributor's PR (live incident)
The gate's first live runs put a red X on an outside contributor's PR five times over, with no comment explaining why. This repo's GITHUB_TOKEN is read-only, so every comment and label call returned 403; the throw reached the top-level handler as exit 2. The gate is advisory. A repository permission problem is an operator condition, never a statement about the PR under review. 401/403/404 from GitHub now emit a loud operator-facing warning naming both blockers (workflow permissions and the missing ANTHROPIC_API_KEY secret) and exit 0. A genuine outage or a bug in here still fails visibly at exit 2. Pinned by four tests including the entry-handler routing; mutation-tested. |
||
|
|
615c33e5b5 |
v0.42.73.0 feat(ci): strict PR usefulness gate + five contributed correctness fixes
Release-only commit: VERSION, package.json, CHANGELOG. All code already on master. Covers the 8 gate commits (#3794, closes #3698), plus #3764 import stdout, #3759 dry-run chmod, #3726 cycle model telemetry, #3751 integrity counter, #3739 doc comment, and #2655 slug_filter. |
||
|
|
40d1d4cabc |
fix(import): make imported pages readable by gbrain's own conversation parser (#3788)
* fix(envelope importer): honor the envelope's own counts, and stop a second import destroying the first
Two ways this importer lost conversation history at exit 0 with zero stderr
bytes. Both reproduced against output from the real memvelope converter, not
hand-authored envelopes — the blind spot that let them through review.
F1. envelope-v0 makes meta.conversation_count and meta.message_count mandatory:
the envelope states its own totals. The script read neither. A file declaring
353 conversations and 9412 messages that actually held one of each imported
one, exited 0, and printed a receipt internally consistent with the reduced
output. The receipt also counted only pages, never messages, so every
message-level loss was invisible by construction — a conversation arriving with
one turn instead of forty still writes exactly one page.
Each declared count is now checked on its own, before the first write. A count
that disagrees with the contents, or that is present but is not a non-negative
integer, refuses the import (exit 2). A count that is simply absent cannot be
compared, so the envelope still imports — but stderr names the field whose half
of the check was skipped, because an unchecked import must never look like a
checked one. The receipt reports messages as well as pages, and says so out
loud when messages read exceed messages written.
F2. filesWritten was per-process and the default outDir is a fixed literal, so
a second import into the same directory clobbered the first with no read-back
and no warning. The trigger is the positional fallback conv-N, which fires
whenever c.id is not a non-empty string — and the spec is explicit that id is
string | null and that a converter MUST NOT synthesize one, so null is the
CONFORMING shape, not malformed input. The reference converter emits it for any
ChatGPT export lacking conversation_id and id. Filenames then key off array
position rather than identity, and two unrelated exports both put their first
conversation at conv-1.
Target files are now examined before anything is written. Byte-identical
content is a re-import; a page this importer wrote from the SAME conversation
id is a refreshed export legitimately updating its own page. Anything else — a
foreign file, or one of ours whose conversation id cannot be matched — refuses
the import (exit 2) and writes nothing. Refusing rather than disambiguating
keeps the importer from inventing an identity the envelope declined to state,
and the remedy is a different output directory.
Both checks run before the first write, so a refused import leaves no partial
output to be mistaken for a whole one, and does not even create the directory.
Verified against all 12 golden fixtures from the memvelope reference converter
and 7 envelopes freshly produced by running that converter over synthetic
ChatGPT and Claude exports: every one imports at exit 0 with zero stderr bytes,
and all 19 produce byte-identical pages to the previous script. The guards do
not false-positive on legitimate producer output.
Known limit, documented in the header: the target-file check is check-then-
write, not atomic. Two simultaneous imports into one directory can both pass it
— measured 19 refused / 21 raced over 40 trials, against 0 / 40 before.
17 new tests; the 13 existing tests are unchanged and still pass. No new
dependencies: package.json and bun.lock are byte-identical.
* fix(envelope importer): close the three holes the adversarial pass found in the guards
An eight-agent refutation round attacked every claim made for the previous
commit. Four claims survived untouched; three did not, and all three were in
code that commit introduced. Each is reproduced, then closed, with tests.
Identity was compared on the TRIMMED conversation id, because that is what the
filename slugs and what the frontmatter recorded. So two ids differing only by
surrounding whitespace — both copied verbatim by the reference converter, both
schema-valid — looked like one conversation to the guard, and the second import
destroyed the first at exit 0 with zero stderr bytes. The surviving page then
recorded an id that appeared nowhere in the envelope that wrote it. The
frontmatter now records the id verbatim, as the spec requires, and identity is
matched raw. The filename still slugs the trimmed form, so no page's bytes
change for any id without surrounding whitespace.
The identity scan required a page to start with exactly `---\n`. A page THIS
IMPORTER WROTE that later picked up CRLF line endings or a UTF-8 BOM — a git
checkout with core.autocrlf, a cross-platform sync, an editor save — was
therefore reclassified as foreign, and one such page refused the ENTIRE
envelope at exit 2. The previous script treated the same mutation as a harmless
overwrite, so the guard had turned a cosmetic byte change into an unrecoverable
block. Both are normalized away before the scan. The message for a file that
genuinely cannot be recognized no longer asserts "was not written by this
importer" — a claim this code cannot make, and one that was false for exactly
the pages it was being printed about.
The new message-delta warning announced "N message(s) in the envelope are not on
disk" whenever pages collided. Duplicate ids are conforming input — the spec has
merging never deduplicate — so converting an old export together with a newer
one, which is what the memvelope CLI tells users to do, fired it routinely while
every unique turn was on disk. It now states what it knows: the overwritten
pages carried N messages that are not on disk, and if those were earlier copies
the surviving page may already hold them. The raw tally stays, since hiding it
is what made message-level loss invisible to begin with.
Two header claims were false as written and are corrected: message text is
byte-verbatim for 18 of the 19 producer envelopes, not all 19 — the
lone-surrogate fixture writes U+FFFD, unchanged from before and out of scope —
and sequential coverage is no longer described as complete. Three further limits
are now stated rather than left implicit: an id-less conversation cannot be
refreshed in place (refused, not applied — the deliberate trade, since the same
ambiguity resolved the other way is the defect being fixed); a conversation
whose created_at moves orphans its earlier page rather than updating it; and a
hand-written lookalike is indistinguishable from a page this importer wrote.
Rejected after reproducing it: a "silent loss" via a duplicate top-level
conversations key in the JSON text. JSON.parse keeps the last such key, which is
language semantics, is identical on the previous script, and cannot be produced
by JSON.stringify.
36 tests pass, 0 fail. The 13 pre-existing tests remain byte-identical as the
first 306 lines of the file. All 19 real-producer envelopes still produce
byte-identical pages. package.json and bun.lock unchanged.
* fix(envelope importer): write pages gbrain's own conversation parser can read
Every page this importer wrote declared `type: conversation` — which opens the
gate to conversation-facts extraction, chronicle eligibility and the
conversation_format_coverage check — and then presented a turn header
**Assistant** (2025-11-02T14:22:51.000Z · m2):
matching none of the 17 built-in patterns in the conversation parser. The
extractor parsed zero messages, incremented `pages_skipped`, and said nothing.
Pages were stored and searchable; no fact was ever extracted from any of them.
Measured on two throwaway PGLite brains fed the same 13 conversations, one
written each way: `conversation-parser scan` goes from 13/13 `no_match` with 0
messages to 13/13 `imessage-slack` with 33; `extract-conversation-facts
--dry-run` goes from "Skipped 13 page(s)" to every page segmenting and reaching
the extractor; `doctor` conversation_format_coverage goes from warn "13/13 ...
match NO built-in pattern" to ok "13 pages: imessage-slack=13".
The turn header is now `**Me** (2025-11-02 14:22):`, the one shape that parser
reads. 24-hour, not 12-hour-with-AM/PM: both match and both were measured to
reconstruct all 24 hours exactly, so the tie is broken on the fact that 24-hour
is a substring of the envelope's own `ts` (no hour arithmetic, so the 12/0
boundary cannot be got wrong) and sorts chronologically within a day.
That header can carry a wall clock and nothing else, so per-message identity
moves to frontmatter as an array of maps:
messages:
- id: "m1"
ts: "2025-11-02T14:22:51.000Z"
An array, not a map keyed by id — a map discards order and collapses the
duplicate ids the spec permits. Consumers index it by position.
Every scalar is JSON-encoded, so every timestamp is quoted. Unquoted, js-yaml
reads an RFC 3339 scalar as a JS Date: microseconds truncate, a +05:30 offset
normalises away, and gbrain's own coerceFrontmatterString slices a Date to 10
characters — the time of day gone. It is sticky, too. The suite fails if a
timestamp is ever emitted unquoted, and carries a sentinel proving that guard
fires.
Also drops the `---` rule between turns: it is a non-blank line matching no
pattern, so the parser appended it to the preceding message and every extracted
text ended `...\n---`.
Verified over all 12 golden fixtures from the memvelope reference converter
(exit 0, zero stderr, 33/33 messages, every id and ts byte-identical after the
round trip) and over fresh envelopes built by running that converter over
synthetic vendor exports. 52 tests green, up from 36.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(envelope importer): build the offset-shifted header clock without Date.UTC
Self-review of the branch, before the refutation round. `headerClock()` is the
only place in this script that does arithmetic on a timestamp, and its offset
branch had two ways to emit a header that is off — or that does not parse at
all.
`Date.UTC` applies MakeFullYear: a year of 0..99 is read as 1900+y. The shape
regex accepts any four digits, so `0050-01-01T00:30:00+05:30` was rendered as
1949-12-31 — the page moved by nineteen hundred years, silently, at exit 0.
Built with the UTC setters instead, which do not remap.
And `getUTCFullYear()` returns `49`, not `0049`. The pattern's regex requires
`\d{4}`, so an unpadded year emits a header that matches nothing and the turn is
appended to its neighbour as a continuation — one message where there were two.
The year is now padded like every other field.
Neither is reachable from the reference converter, which emits `Z`. Both are
reachable from a conforming envelope: the spec types `ts` as any date-time
string, and RFC 3339 offsets are legal.
18 new tests pin the clock on its own: every Z / offset / designator-less form,
both day-boundary crossings, `+0530` without a colon, the sub-100 year in both
branches, and the six unusable-`ts` shapes that must fall back to the
conversation's date at midnight rather than fabricate a clock or drop a turn.
Three more hostile ids added — U+2028, U+2029 and U+0085 are line breaks in
YAML 1.1 and JSON.stringify emits all three raw, so they are the sharpest
version of the injection the quoting exists to stop. js-yaml 3.14 does not
honor them as breaks; measured, not assumed.
72 tests pass, 0 fail. Adjacent suites (conversation-parser, markdown,
extract-conversation-facts, doctor backlog): 380 pass, 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(envelope importer): name the built-in pattern count the header claims
'None of the built-in patterns' is a claim about a number the reader cannot
see. There are 17, and `gbrain conversation-parser list-builtins` is how to
count them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(envelope importer): stop the conversation title manufacturing a turn
An adversarial round against the new format. Three claims in the file header
were wrong or overstated, and one of them was a defect.
The H1 heading is the only place a third-party string reaches the BODY — every
frontmatter value is JSON-escaped, the heading was interpolated raw. That was
merely untidy while nothing parsed the body. Now that the turn headers are
legible, a title carrying a newline manufactures a TURN, and one that lands
AHEAD of every real one, so `messages[0]` in frontmatter names content the user
never sent and every id after it is off by one. Measured: a 2-message
conversation titled "Real Title\n\n**Me** (2020-01-01 09:00): INJECTED" parses
to 3 turns, the first attributed to 2020. The heading is flattened to one line;
the verbatim title, newlines and all, is still recorded in frontmatter.
The empty-title fallback also disagreed with itself — "Untitled conversation"
in frontmatter, "Conversation" in the heading, two names for the same missing
thing, and parseMarkdown prefers the body's H1 when frontmatter has no title.
One fallback now.
Corrected in the header, because a false claim there is worse than a limit:
"the frontmatter array still holds exactly the real turns, so the two can be
reconciled by count" was FALSE. parse.ts picks one pattern per page, scored on
the first 10 body lines, and only re-scores full-body under 0.3 — so a pasted
Slack or Telegram snippet inside one message only has to win that window, and
the length of the real conversation is irrelevant. With four `**[09:0N] Name:**`
lines quoted inside message 1: 40 real turns are replaced by 4 fabricated
speakers at fabricated times, at exit 0, with phase regex_match, so
pages_skipped stays 0 and doctor's conversation_format_coverage reports OK. And
at 4 real turns against 4 pasted lines the COUNTS AGREE while every speaker and
timestamp is fabricated — which is exactly the check the old wording offered as
the remedy. The table is in the header now, with what count-comparison does and
does not catch. Closing it needs a parse.ts change, not an importer change.
"every timestamp is QUOTED" was true only for conforming input. JSON quotes
strings; an envelope whose `ts` is a number emits `ts: 1762093371000` unquoted.
That is a YAML integer, not a Date, so it is lossless and carries no truncation
hazard — but the claim as written was wrong, and the behavior is now pinned by
test rather than described.
Two more limits stated: minute resolution is the parser's ceiling, not this
format's — every branch of buildIso hardcodes :00 and no built-in captures
seconds, so two turns in the same minute collide (claude-basic m1 15:02:00 and
m2 15:02:31 both parse to 15:02:00Z) and frontmatter `ts` is the only
full-resolution value on the page. And the array survives a gbrain rewrite
semantically, not textually: serializeMarkdown re-emits `id: "m1"` as `id: m1`.
81 tests pass, 0 fail. Adjacent suites: 389 pass, 0 fail. All 12 golden
fixtures still import at exit 0 with zero stderr.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(envelope importer): range-check the per-message clock, and arm two guards
Second adversarial round. The shape regex counts digits; it does not know a
calendar. `2025-99-99T99:99:00Z` was copied straight into a turn header — and
`imessage-slack` MATCHES that header, so gbrain stored an instant no calendar
contains. `2025-02-30` is the sharper one: it yields a VALID JS Date, silently
shifted to March 2. The script already validates `created_at` before it reaches
a header, with a comment explaining why; the per-message clock is the same
untrusted surface and is used far more often. It is validated now, by round
trip — a date that does not survive its own UTC round trip was never a date.
Also removes a provably unreachable `Number.isFinite` check: over the whole
space the shape regex admits, the constructed instant is always finite.
Two guards had no test that fires. Both are now pinned:
- The `created_at` validation. Dropping it let a hostile value break the turn
header it was supposed to anchor, and the whole suite stayed green.
- The determinism guard — the reason TS_SHAPE exists rather than
`new Date(string)`, which parses a designator-less date-time as LOCAL time.
The one test that touched it only caught the mutation because this box is
America/New_York; on a UTC runner it passed. TZ is now pinned explicitly and
the case runs under four zones including UTC and Pacific/Kiritimati.
94 tests pass, 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(envelope importer): correct five claims the adversarial round falsified
None of these change behavior. All five were statements in the file header that
an independent pass could not reproduce, and a false claim in a header is worse
than a limitation, because a reader has no way to tell.
"the field is read nowhere outside builtins.ts", of `time_format`, was flatly
false: src/commands/conversation-parser.ts reads it twice. Both are display —
`list-builtins` prints it — and the operative half stands, since parse.ts never
reads it and converts off the captured AM/PM group instead. Said that way now.
"turns averaging more than ~19 non-blank lines fall below the floor" was
optimistic by one line: measured line by line, 18 parses (0.0526) and 19 does
not (0.0499), because the H1 is in the denominator too.
"a fallback when `ts` is null" understated the condition. It fires for any
string this script will not read a clock out of — a date with no time, a basic-
format 20251102T142251Z, an impossible 2025-02-30 — silently preferring the
conversation's date over a value that looked like a time.
"Every emitted scalar is JSON-encoded" was false for the four keys the script
writes itself (`type:`, `origin:`, `date: null`, `messages: []`), which are
literals under its own control rather than envelope data.
"collapses the duplicate ids the spec permits" cited the wrong half of the
spec: duplicates are tolerated for CONVERSATION ids; message ids are positional
and unique within their conversation. The real argument for an array is that
order IS the join. And since a conforming envelope's message id is derivable
from its index, `ts` is the only genuinely new value the array carries — worth
saying rather than implying more is being rescued than is.
Also: the STATUS block quoted `gbrain conversation-parser scan` as producing a
13-page aggregate; it takes one slug and has no aggregate form. The numbers
were reproduced per-slug, so the receipt now shows the command that produced
them.
And two tests changed by this branch still used `page.split('---')[1]` to reach
the frontmatter — the exact idiom this branch's own helper documents as unsound,
since it cuts on the substring anywhere including inside a quoted value. Both
now go through that helper.
94 tests pass, 0 fail. Adjacent suites: 402 pass, 0 fail. All 12 golden
fixtures still exit 0 with zero stderr and 33/33 messages parsed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(envelope importer): break a duplicate id on updated_at, not array position
A merged re-export kept the STALE copy, deterministically.
The memvelope CLI's own USAGE tells users to pass every downloaded export
at once; folder expansion sorts by filename (cli/convert.mjs expandInputs)
and SPEC.md rule 8 forbids the converter from re-sorting conversations
afterwards. Every automatic duplicate-namer a browser or OS gives a second
download of `conversations.json` inserts a character that sorts below `.`
— ` (1)`, `(1)`, `-1`, ` 2` — so the RE-EXPORT lands first in
`conversations[]` and the ORIGINAL lands last. Resolving the collision by
array index therefore threw away the newer copy every time, not half the
time.
Reproduced with an envelope built by the reference converter from two
ChatGPT downloads named the way a browser names them: 4-turn re-export at
index 0, 2-turn original at index 1, and the 2-turn page reached disk at
exit 0.
`updated_at` is a required conversation key in envelope-v0, is populated
by both vendor paths of the reference converter, and was read by nothing
here. It now decides, compared as an INSTANT (a `+05:30` value sorts above
a `Z` value it precedes) at full sub-second resolution (the converter
emits milliseconds always).
The tiebreak applies only when BOTH copies carry an orderable value.
Equal, absent, non-string or unparseable on either side falls back to
array order — a missing timestamp is not evidence of being older, and
preferring the copy that has one is a guess dressed as a rule. `created_at`
is not a secondary key: it is identical in both copies of a re-export.
The collision warning is not quieter — it is louder. It now names which
copy survived, on what basis, and with both timestamps, because
"overwriting the earlier page" is false whenever the tiebreak fires. The
run summary says "discarded" rather than "overwritten" for the same
reason: the losing copy is now sometimes never written at all.
`updated_at` is still written to no page. That is a separate, tracked gap.
Receipts: 107 pass / 0 fail (94 pre-existing, unweakened; 13 new, red-first
against
|
||
|
|
d610a845a8 |
fix(ci): gate — CommonMark info-string rule, hash the real model payload, close the YAML scanner gap (blind review round 5)
Three defects a second blind reviewer found that survived round 5. All three
reproduced against
|
||
|
|
f87488ff36 |
fix(ci): gate — stop red-Xing genuine contributors; neutralize markdown embeds (blind review round 4)
A blind reviewer rejected this branch on false positives and the repro held up. The gate's intent check deleted every list and blockquote LINE before counting words, then measured what was left against an arbitrary 40. Real people write their story as bullets. Measured on the four realistic-human descriptions from the repro: description before after floor 40 floor 20 own prose written as four bullets 0 55 CLOSE PASS short non-native-English paragraph 38 38 CLOSE PASS specific first-person bug report 34 34 CLOSE PASS mostly a stack trace + a real explanation 28 27 CLOSE PASS The file's own exemption rationale already says a check that is red on every release is a check somebody disables inside a week. Red on every terse-but- genuine contribution dies the same way, and it costs real contributors on the way there — worse than the forgery risks rounds 1-3 chased. - intentWordCount strips the list/quote MARKER and keeps the author's words. Still stripped: fenced code, indented code (new — the other spelling of a fence, guarded so it never eats a list continuation), headings, HTML comments, raw HTML, bare URLs, inline code, link/image syntax, and the template's own bold prompts. An untouched pull_request_template.md still scores 0, pinned against the real file on disk. - INTENT_MIN_WORDS 40 -> 20, documented as a floor against empty/boilerplate- only descriptions, not a quality bar. CONTRIBUTING.md documents no word count at all. Empty, "fixes bug", the unfilled template, 10 words of lorem and an indented log paste all still fail. - The fix-it comment no longer tells contributors to reopen a PR that was never closed — the gate has no close call in it. It now says what actually happens: editing the description re-runs the check (the workflow triggers on `edited`), the PR stays open, a maintainer decides. - sanitizeModelText backslash-escapes `[` and `]`. Escaping HTML killed the <details> forgery, but Markdown needs no angle brackets: an image embed renders a green "APPROVED" picture and a link renders a live phishing target inside a close-lane comment. The file asserted nothing attacker-controlled reaches Markdown unescaped; that is now true. - A label-API failure during a NEUTRAL run no longer throws to exit 2 with no comment. NEUTRAL has no verdict to record, so it logs, still posts, exits 0 — and the comment stops claiming labels were cleared when they were not. The verdict path keeps failing loudly on purpose (ordering note). Four verbatim human descriptions are pinned as PASSING forever. Eight mutations (floor, list markers, quote markers, indented code, markdown escaping, the reopen copy, the NEUTRAL try/catch, the label claim) each fail at least one test. test/pr-gate-workflow.test.ts 145 -> 158 pass, 0 fail. typecheck, actionlint, verify (34/34) green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ddb39df23a |
fix(ci): gate — escape comment HTML, tighten screenshot floor, fix fence false-positive, repair label ordering (blind review round 3)
Four findings from a second independent blind review, all reproduced against HEAD before the fix and all pinned by a mutation-tested case. - sanitizeModelText escapes &, < and > after the existing comment/mention/ block-marker stripping, so raw HTML from any dynamic string (model output, mechanical flag details built from PR filenames, neutralReason, the policy-exempt note) renders as literal text. A <details open><summary>MERGE LANE — approved</summary> string is a forged verdict inside a close-lane comment; it now renders as <details. - hasScreenshot requires a markdown image whose URL looks like a URL or path, an <img> carrying a non-empty src=, or a bare paste URL, and strips HTML comments before scanning. ``, `<img alt=proof>` and an image hidden inside `<!-- -->` no longer clear it. Documented in the code as a FLOOR against zero-effort submissions, not proof. - stripCodeFences is a line scanner following the CommonMark rule that a closing fence must be the same character and at least as long as the opening one. The old backreference read ```` as a NEW opening fence and stripped to EOF, so a compliant body documenting fence syntax lost its intent paragraph and was closed. The scanner is also linear, retiring the superlinear-backtracking hazard the 16KB cap was sized against. - Labels are reconciled BEFORE the sticky comment carrying the cached state. Written the other way, one transient label-API 500 left stale/missing/ duplicate labels forever: the rerun short-circuited on the persisted state and returned success without repairing them. Also: - hashInputs covers what the run consumes — the truncated model body plus the mechanical policy outcome — so an edit past the 6KB model cap no longer mints a new hash and buys an identical paid call, while a policy fix landing past that cap still invalidates the cached verdict. - The test's YAML block-scalar scanner accepts indentation indicators in both legal orders (`>2-` as well as `|-2`), with a guard-the-guard case proving it sees a `run: >2-` block interpolating attacker-controlled text. - A block at the top of the script states plainly that this gate is a triage signal and a reviewer checklist, not an authorization boundary; one line of the sticky comment footer says the same to the contributor. 145 pass / 0 fail (was 126), typecheck clean, actionlint clean, verify 34/34. |
||
|
|
88731d8cf3 |
fix(ci): gate — sanitize mechanical flag details, anchor path regexes, exempt maintainer/bot/draft from the policy check (blind review round 2)
Blind review round 2 rejected the branch. Five findings, all fixed.
BLOCKING 1 — the verdict was forgeable through a PR filename.
renderComment wrote mechanical red-flag details raw, and two of them
interpolate filenames (adds_recipe, deletes_tests). git allows a newline
inside a filename and JS `[^/]` matches one, so RECIPE_RE's anchors were
decorative: a PR adding `src/core/ai/recipes/x\n## PR Gate — ...\ncc
@octocat\n<!-- ...state... -->\nz.ts` put that text verbatim into the bot's
comment — forged heading, live third-party mention, and on the NEUTRAL
render (which writes no state block of its own) parseState() returned the
ATTACKER's block, so the next run hit the spend guard and silently skipped
the verdict with no label and exit 0. Three layers:
(a) flag details go through sanitizeList, exactly like the model's
strings. Audited every other interpolation into the comment; the rest
are literals in the file, Number()-coerced, or already sanitized.
(b) every path regex spells its segment class [^/\n], not [^/] —
RECIPE_RE, SOURCE_EXT_RE and the test-path check.
(c) parseState reads the state block only off line 2 of a marker-leading
comment (where renderComment writes it) and STATE_RE is whole-line
anchored. A block anywhere else is somebody else's text.
BLOCKING 2 — no exemption, so every release PR was close-lane.
Measured: 40 of the last 40 merged PRs would be close-lane on
missing_screenshot, including every /ship release PR. A check that is red
on every release gets switched off within a week, and then it filters
nothing. The #3745 policy exists to filter INCOMING OUTSIDE CONTRIBUTIONS;
release automation cannot take a screenshot of itself. It is now waived for
OWNER/MEMBER/COLLABORATOR, bot authors and drafts — the usefulness verdict,
the title rule and every mechanical red flag still run, and the sticky
comment says the check was skipped. author_association / draft / user.type
are read from the pr.json the workflow already fetches: no new API call, one
source of truth. `draft` is the one author-settable input, so
ready_for_review joins the trigger list and the exemption is folded into the
spend-guard hash — the draft-era verdict cannot be reused after the flip.
Stated as a deliberate decision in both the workflow header and the script.
3 — DOWNGRADE_FLAG_IDS omitted deletes_tests, adds_symlink and
adds_node_modules, so a PR deleting test/e2e/engine-parity.test.ts kept
merge-lane and a green check on the strength of its prose. All three added.
The old test used deletes_tests as its example of a NON-downgrading flag;
rewritten to pin the stronger invariant instead — every id detectRedFlags
can emit is a downgrade trigger (derived from the detector, so a new flag
fails until it is classified on purpose), and the set is still an allowlist
(an unrecognized id changes nothing).
4 — FENCE_RE backtracks superlinearly on a hostile body: 65KB of backticks
(GitHub's max body length) measured 8.2s across the two policy scans on a
pull_request_target runner. stripCodeFences now caps the scan at 16KB —
same input, 0.40s. Tradeoff documented at the constant: the intent
paragraph and the screenshot both sit near the top in practice (the PR
template puts them in the first two sections, and the model payload already
caps the same body at 6KB), so a real contributor is not judged on a
truncated tail.
Verified: test/pr-gate-workflow.test.ts 126 pass / 0 fail (was 95),
typecheck clean, actionlint clean, check-privacy /
check-no-tracked-symlinks / check-progress-to-stdout /
check-bun-test-timeout / check-key-files-current-state all exit 0. Each new
pin was mutation-tested against the pre-fix behavior: all six mutants fail
the suite.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
4a00c31b12 |
fix(ci): gate policy check survives an API outage; merge master; drop NUL separators
Three fixes on the strict PR usefulness gate (#3698), plus the merge that brings in the policy it enforces. 1. The mechanical policy check now outlives the model. The ANTHROPIC_API_KEY guard used to sit above detectPolicyMisses, so a PR with no intent paragraph and no screenshot got a green NEUTRAL skip whenever the key was absent or Anthropic was down — "wait for a 500" was a documented way past the one hard requirement. The #3745 branch now sits above the key guard and the spend guard: a policy miss is close-lane + the friendly fix-it comment + exit 1 with no API dependency at all. A compliant PR that hits a missing key or a dead API keeps the round-1 NEUTRAL behavior unchanged (loud comment, ::warning::, exit 0, stale gate:* labels cleared) — and the NEUTRAL comment now says plainly that the *usefulness verdict* did not run, while still reporting the title check and mechanical red flags it was able to compute without a model. 2. Merged origin/master, which carries #3745's CONTRIBUTING.md section and .github/pull_request_template.md. No conflicts: this branch never touched VERSION / package.json / CHANGELOG.md, so master's 0.42.72.1 carried through untouched — the feature branch adds no version bump. The test's inlined pull_request_template fallback (only needed while the branch predated the merge) is gone; it now reads the real file, so growing the template's own prose past the 40-word bar fails here instead of silently letting an untouched template through. The CONTRIBUTING_URL deep link is pinned against a GitHub-style slug of every heading in the merged CONTRIBUTING.md, with the slugger itself pinned so it cannot "pass" against an anchor GitHub never generates. 3. hashInputs joined its three fields with literal NUL bytes, which made grep treat the whole of scripts/pr-gate.mjs as binary — any future grep-based CI guard over that file would have matched nothing and passed silently. Replaced with JSON.stringify of the tuple: still unforgeable (each field is quoted and escaped), still stable by construction, and printable. `grep -c hashInputs scripts/pr-gate.mjs` now returns 2 instead of nothing. Existing sticky-comment state hashes are invalidated once, costing one re-verdict per open PR. Tests: 95 pass / 0 fail in test/pr-gate-workflow.test.ts. The no-API-key policy-miss case was verified to fail against the pre-fix ordering. Verified live against the Anthropic API: HTTP 200, strict JSON, all seven required keys, merge-lane on a compliant fixture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
94662eb1e1 |
feat(ci): gate enforces the #3745 intent-paragraph + screenshot requirement
CONTRIBUTING.md (#3745) requires every PR to carry a paragraph the author wrote themselves and a screenshot of gbrain actually in use. The gate now checks both, mechanically, before it spends anything on review. What it checks (no LLM, both exported for testing): - hasScreenshot: markdown ``, a bare user-images.githubusercontent or github.com/user-attachments/assets URL, or an <img> tag. Anything inside a fenced code block does not count — pasting the syntax is not attaching the picture. - hasIntentParagraph: >= 40 words of prose left after stripping fenced code, blockquotes, list items, headings, HTML comments, links and the PR template's own boilerplate. Per-character scripts are tokenized per character, so a paragraph written in Chinese counts as one. The two lane consequences: - missing_screenshot OR missing_intent forces close-lane from any recommended lane (exit 1) and skips the model call entirely — closed without review is the documented consequence, so there is nothing to spend a review on. The sticky comment leads with what is missing, how to fix it, and the reopen path; both misses are also recorded in the existing "Mechanical downgrades applied" section. - The model's new advisory intent_authenticity verdict forces needs-maintainer (exit 0) when it reads "ai_generated", and never close-lane on that signal alone. The comment says only that a maintainer will read the paragraph personally; the model's reasoning is consumed and never published at the contributor. Rubric + strict-JSON schema gain intent_authenticity and intent_authenticity_reason, with explicit instructions that rough grammar, terseness and non-native English are evidence of a HUMAN and that "unclear" is the answer whenever the evidence is not clear-cut. test/pr-gate-workflow.test.ts: 63 -> 88 tests, all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2f65ed8da6 |
fix(ci): harden the PR gate — comment-ownership, output sanitization, deterministic lane downgrades (blind review round 1)
Two independent blind reviews of #3698 (one APPROVE, one REJECT). Every confirmed finding from the reject side, plus the cheap hardening both reviews flagged. BLOCKING 1. Sticky-comment hijack. upsertStickyComment adopted ANY comment containing the marker, so a contributor could pre-post `<!-- gbrain-pr-gate -->`, have the gate PATCH it, then edit it into a fake green verdict. isOwnComment() now requires user.type === 'Bot' AND login === 'github-actions[bot]' AND the body to START WITH the marker; anything else gets a fresh comment. 2. LLM output injected as raw Markdown. Model-produced reasons[] and reviewer_checklist[] reached the comment unescaped — PR-body-driven injection could forge headings, a second marker, and live @mentions. sanitizeModelText()/sanitizeList() are now the single choke point in renderComment(): HTML comments stripped, mentions zero-width-broken, leading block markers removed, newlines collapsed, 300 chars per string, 8 entries per list, both caps self-marking. 3. Lane was purely model-decided. A well-written feature pitch could talk itself into merge-lane. The model now RECOMMENDS; applyMechanicalDowngrades forces merge-lane -> needs-maintainer on any of: workflow edits, a new package.json dependency, a new src/core/ai/recipes/ provider file, new KNOWN_CONFIG_KEYS entries, >40 changed files, >400 net source lines outside test/, or a src/ change with no test file touched (#3665). The sticky comment reports them under "Mechanical downgrades applied". 4. The test file did not pin what it claimed. Added: no gh pr checkout / git fetch / refs/pull / pull/*/head in any spelling; exact permissions key->value map plus a single-permissions-block assertion so no job-level grant re-widens contents; the ${{ }}-in-run scanner now covers folded (`run: >`) and chomped blocks, with a guard-the-guard test; and mocked end-to-end runGate() runs for close-lane exit 1, marker hijack, sanitizer, truncation, refusal routing, NEUTRAL label clearing, label swap, and the spend guard. 5. Version-first title regex rejected the documented suffix form. `v0.31.1.1-fixwave fix: ...` now passes. VERSION_AT_END_RE no longer false-positives on `chore: bump zod (3.25.76)`: it fires only on a v-prefixed or 4-segment trailing version, i.e. this project's own shape. 6. Refusal fail-open. stop_reason=refusal exhausted retries into a green NEUTRAL — a deterministic way to dodge the red X. Refusal and schema-invalid output now route to needs-maintainer with an explicit note; only transport failure stays NEUTRAL. Refusal also short-circuits the retry loop, since retrying a deterministic refusal only burns spend. ALSO 7. persist-credentials: false on the checkout step. 8. Dropped pull-requests:write. Everything the script calls is the issues API (comments, label create, label add/remove), so issues:write is the only grant that is actually needed. 9. Spend guard for the edited/synchronize amplification: the LLM call is skipped when sha256(title+body+head_sha) matches the hash recorded in the previous sticky comment's state block, and the stored lane's exit code is reused. 10. NEUTRAL runs now clear every gate:* label instead of leaving a stale verdict behind. Verified: bun test test/pr-gate-workflow.test.ts 63 pass / 0 fail, bun run typecheck clean, actionlint clean, check-privacy / check-no-tracked-symlinks / check-progress-to-stdout / check-bun-test-timeout clean, plus a live Anthropic smoke of the exact request shape (HTTP 200, valid strict JSON, injection attempt rejected). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ca1eed3e95 |
feat(ci): strict PR usefulness gate — merge-lane/close-lane verdict before any review effort (#3698)
Runs on pull_request_target against master. PR code is never checked out or executed: metadata + a 120KB-capped diff come from the GitHub API only; the base-repo checkout provides the rubric script. All interpolations are env-bound; actions SHA-pinned; permissions limited to contents:read + pull-requests:write + issues:write; per-PR concurrency with cancel. scripts/pr-gate.mjs (no deps, fetch only) classifies via the strict usefulness rubric (claude-sonnet-5, strict JSON schema, thinking disabled; sampling params omitted — rejected on this model), posts one sticky comment (<!-- gbrain-pr-gate -->), applies exactly one gate:* label, and exits 1 only on close-lane. Missing ANTHROPIC_API_KEY or API failure after 2 retries NEUTRAL-skips loudly (comment + warning, exit 0). Mechanical (no-LLM) checks: version-first title rule and diff red flags (>40 files, node_modules, symlinks, workflow edits force needs-maintainer, new package.json deps, deleted tests). test/pr-gate-workflow.test.ts pins the security invariants and unit-tests the exported title rule + red-flag detector (import side-effect guarded). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |