• v7.31.2 d6daafb426

    dpsifr released this 2026-06-09 21:54:06 +02:00 | 301 commits to main since this release

    Fix

    Documentation-only fix. Two type-definition comments incorrectly stated SQ4 vector quantization required a paid native provider. Brainy ships a pure-JS SQ4 implementation (`distanceSQ4Js` in `src/utils/vectorQuantization.ts`); the native SIMD acceleration is a drop-in via `setSQ4DistanceImplementation`, not a hard requirement.

    Corrected in `src/coreTypes.ts:472` and `src/types/brainy.types.ts:1123`.

    No behaviour change. Caught during an upstream open-core-boundary audit.

    Tests

    1468 / 1468 unit suite passing. No code paths changed.

    See RELEASES.md for the full entry.

    Downloads
  • v7.31.1 6716329d06

    dpsifr released this 2026-06-09 19:36:29 +02:00 | 303 commits to main since this release

    Fix

    A same-key rename race in FileSystemStorage.saveBinaryBlob caused brain.flush() to throw ENOENT and broke downstream jobs (GCS backups, snapshot exports) for any consumer that triggers concurrent flush + compaction.

    [job-queue] gcs-backup: failed — ENOENT: no such file or directory,
      rename '/data/brainy-data/.../_column_index/owner/DELETED.bin.tmp'
           -> '/data/brainy-data/.../_column_index/owner/DELETED.bin'
    

    Root cause: saveBinaryBlob used a bare ${filePath}.tmp suffix. Two concurrent same-key calls computed the same temp path; both writeFiled, the first rename succeeded, the second rename fired against a missing temp and threw.

    Fix: unique per-writer temp suffix (matches the pattern at every other atomic-write site in the same file) + defensive ENOENT swallow on rename + temp cleanup on any other failure.

    Audit

    One bug site, scoped audit confirms no other similar patterns:

    • FileSystemStorage — six sibling atomic-write sites already used unique suffixes. Only saveBinaryBlob was the outlier. All clean now.
    • OPFSStorage — WritableStream (no tmp+rename).
    • Object-store adapters (GCS / R2 / Azure / S3) — PUT is atomic.
    • MemoryStorage / HistoricalStorageAdapter — not affected.
    • COW / versioning / HNSW / aggregation / snapshot — all delegate to storage adapters; they get the fix automatically.

    Beneficiaries beyond the reported bug

    HNSW connection persistence (hnswIndex.ts:252) also writes via saveBinaryBlob and was structurally susceptible to the same race. No production reports of HNSW failures (probably because the HNSW write path is more naturally serialized by the index lock), but the fix removes the latent issue.

    Tests

    • New tests/integration/savebinaryblob-concurrent-rename.test.ts (4 tests)
    • 1468 / 1468 unit suite passing

    See RELEASES.md for the full entry.

    Downloads
  • v7.31.0 65cfd2cc6c

    dpsifr released this 2026-06-09 19:04:45 +02:00 | 305 commits to main since this release

    Highlights

    Per-entity optimistic concurrency, the read-then-CAS lock pattern, and idempotent-bootstrap singleton inserts.

    • entity._rev: number — monotonic per-entity counter, auto-bumped on every update(). Surfaced on get() / find() / search(). Pre-7.31.0 entities read as _rev: 1.
    • update({ ifRev: N }) — CAS check. Throws RevisionConflictError carrying { id, expected, actual } if the persisted _rev no longer matches.
    • add({ ifAbsent: true }) + addMany({ items, ifAbsent: true }) — by-ID idempotent insert. Returns the existing id if present, no throw, no overwrite.
    • RevisionConflictError exported from the public API.

    The full optimistic-concurrency guide walks through the lock pattern, read-modify-write retry, idempotent bootstrap, and how _rev interacts with branches, snapshots, and VFS file versioning.

    Why this scope (and why no brain.transaction())

    A public transaction wrapper was on the table but was cut. The internal TransactionManager exposes raw Operation classes that take a StorageAdapter constructor arg; a clean high-level facade in 7.31.0 was either (a) "delegate to brain.add() / update() / relate()" which each commit their own internal transaction before the closure returns (looks atomic, isn't), or (b) thread tx? through every internal write site (~2 days + regression surface). The SDK-scheduler use case driving this is the read-then-CAS lock pattern, fully solved by _rev + ifRev. Multi-write atomicity lands in 8.0's Datomic-style brain.transact() where it's atomic by construction.

    Cortex compatibility

    Zero changes required. _rev is a single integer field updated alongside every other metadata field; Cortex never sees the per-entity counter.

    Tests

    • New tests/integration/rev-and-ifabsent.test.ts (18 tests)
    • 1468 / 1468 unit suite passing

    See RELEASES.md for the full entry.

    Downloads
  • v7.30.2 ccc1d74953

    dpsifr released this 2026-06-08 21:54:32 +02:00 | 307 commits to main since this release

    Highlights

    • Recalibrate find({ limit }) cap formula from 100 KB/result to 25 KB/result (matches observed entity footprint). Typical caps ~4× more generous; maxQueryLimit / reservedQueryMemory overrides unchanged.
    • Two-tier enforcement: silent pass below cap, one-time warning per call site in the soft tier (cap < limit ≤ 2× cap), throw in the hard tier (> 2× cap).
    • Improved error / warning messages name the three escape valves (maxQueryLimit, reservedQueryMemory, pagination), include caller stack frame, link to new docs.
    • New public guide: docs/guides/find-limits.md.
    • findCallerLocation() extracted to src/utils/callerLocation.ts and shared with the 7.30.1 subtype enforcement.

    Cortex compatibility

    Zero changes required. All work is JS-side validation that runs before any storage / Cortex call.

    Tests

    • New tests/integration/find-limits.test.ts (9 tests)
    • Recalibrated tests/unit/utils/memoryLimits.test.ts (4 tests)
    • 1468 / 1468 unit suite passing

    See RELEASES.md for the full entry.

    Downloads
  • v7.30.1 34e8271c53

    dpsifr released this 2026-06-08 20:32:11 +02:00 | 309 commits to main since this release

    Affected products: anyone running a brain where a platform layer (SDK, framework wrapper)
    has registered brain.requireSubtype() rules on common NounTypes, AND anyone preparing for
    the upcoming Brainy 8.0 default-on strict mode. Additive; drop-in from 7.30.0. No behavior
    change for consumers not using strict-mode enforcement.

    Why

    Production incident 2026-06-08: a consumer using SDK 3.20.0 (which registers
    requireSubtype() rules on NounType.{Event, Collection, Message, Contract, Media, Document})
    saw their booking flow start returning 500s because brain.add({ type: NounType.Event, ... })
    calls in their codebase lacked subtype. An audit of Brainy's OWN source revealed 14 HIGH-risk
    internal write paths that also omit subtype — VFS move/copy/symlink edges, aggregation
    materializer, neural extraction, importers, integrations (Sheets/OData), MCP client, CLI.
    Any consumer running requireSubtype() rules on those NounTypes was one step away from breaking
    Brainy's own infrastructure paths, not just their own code. 7.30.1 closes both gaps before
    8.0 ships and makes strict mode the default.

    New — brain.audit() diagnostic

    Find entities and relationships missing a subtype value, grouped by type. The companion to
    migrateField() (and to 8.0's fillSubtypes()): answers "what would break if I enabled
    strict subtype enforcement?".

    const report = await brain.audit()
    // {
    //   entitiesWithoutSubtype: { event: 24, document: 3 },
    //   relationshipsWithoutSubtype: { relatedTo: 1402 },
    //   total: 1429,
    //   scanned: 8400,
    //   recommendation: 'Found 1429 entries without subtype. Migrate via `brain.migrateField()`
    //                    (7.x) — or wait for `brain.fillSubtypes()` (8.0) which closes the same
    //                    gap with caller-supplied rules.'
    // }
    

    VFS infrastructure entities are excluded by default (they bypass enforcement via
    metadata.isVFSEntity markers). Pass { includeVFS: true } to surface them.

    New — Improved enforcement error messages

    The error fired when subtype enforcement rejects a write now includes:

    1. The caller's source location — extracted from the JavaScript stack so you see your own
      call site, not a Brainy internal frame. Eliminates the "grep your repo for brain.add" step.
    2. Specific guidance — points at the registered vocabulary when one exists; mentions
      brain-wide strict mode and the except escape valve when not; otherwise the
      brain.requireSubtype() registration recipe.
    3. A documentation linkhttps://soulcraft.com/docs/guides/subtypes-and-facets#strict-mode
      for the canonical migration recipe.

    Before:

    add(): NounType.Event requires subtype but got undefined. Register vocabulary via brain.requireSubtype().
    

    After:

    add(): NounType.event requires subtype but got undefined.
      at BookingDraftService.getOrCreateByToken (/app/src/booking/draft.ts:42:23)
      Pass one of: booking, session, milestone.
      Migration recipe: https://soulcraft.com/docs/guides/subtypes-and-facets#strict-mode
    

    Internal subtype labels — Brainy's own infrastructure paths

    Every internal Brainy write path now sets a stable, queryable subtype. Consumers don't need
    to do anything for these — they're documented here so you can query Brainy-managed data:

    Code path NounType / VerbType Subtype label
    VFS root directory / Collection 'vfs-root'
    VFS subdirectories Collection 'vfs-directory'
    VFS files mime-driven (e.g. Document/Code/Image) 'vfs-file'
    VFS symlinks File 'vfs-symlink' (NEW — distinct from 'vfs-file')
    VFS Contains edges (create + move/copy/symlink/batch) Contains 'vfs-contains'
    Aggregation materialized output Measurement 'materialized-aggregate'
    Import-source provenance entity Document 'import-source'
    Importer-extracted entities extractor-driven type 'imported'
    Importer placeholder targets Thing 'import-placeholder'
    Neural extraction extractor-driven type 'extracted'
    GoogleSheets API entity writes request-driven 'imported-from-sheets'
    OData API entity writes request-driven 'imported-from-odata'
    MCP message storage Message 'mcp-message'
    brainy add CLI default user-supplied type 'cli-add'
    brainy relate CLI default user-supplied verb 'cli-relate'

    Query examples:

    // Every VFS-managed file in your brain
    await brain.find({ subtype: 'vfs-file' })
    
    // Document breakdown — distinguishes import-source from extracted/imported/user content
    brain.counts.bySubtype(NounType.Document)
    // → { 'import-source': 12, 'imported': 847, 'extracted': 34, 'vfs-file': 102, ... }
    

    Caller-supplied defaultSubtype on importers and extraction

    Importer + extraction paths now accept a caller-supplied defaultSubtype config so consumers
    can tag a whole batch with their own provenance label instead of the Brainy default:

    // SmartImportOrchestrator / ImportCoordinator / NeuralImport all accept this
    await brain.importer.import(file, {
      defaultSubtype: 'customer-upload-2026q2',  // your batch label
      // ...
    })
    

    Precedence: extractor-set subtype (highest) → caller's defaultSubtype → Brainy default
    ('imported' for importers, 'extracted' for extractors).

    CLI --subtype flag

    brainy add and brainy relate gain a --subtype <value> (-s) flag for use with
    strict-mode brains:

    brainy add "Avery Brooks — runs the AI lab" --type person --subtype employee
    brainy relate alice manages bob --subtype direct
    

    When the flag isn't supplied, the CLI uses 'cli-add' / 'cli-relate' as defaults so
    ad-hoc CLI usage still works against strict-mode brains.

    Cortex compatibility

    No Cortex changes required for 7.30.1. Every change is JS-side: internal subtype labels are
    arbitrary strings stored transparently by Cortex; brain.audit() runs purely on JS via existing
    storage.getNouns() / getVerbs() pagination; the CLI is JS-only; error messages fire in
    Brainy before any native call.

    For Cortex 3.0 (forward-looking, not blocking):

    • Native audit() proxy. For billion-scale brains, audit() walks every entity (O(N)).
      A native implementation reading from a "null-subtype" bitmap in the column store would be
      O(buckets). Listed as the 6th open question in the
      Brainy 8.0 spec doc.
    • Strict-mode parity test. Cortex should mirror Brainy's new
      tests/integration/strict-mode-self-test.test.ts against their native paths to catch any
      latent bug where native writes bypass JS validation.
    • Reserved-label awareness (optional). Brainy's internal labels ('vfs-*',
      'materialized-aggregate', 'imported', 'extracted', 'mcp-message', 'cli-*') become
      a documented part of the 8.0 contract; useful for telemetry that surfaces "X% of entities are
      Brainy-managed infrastructure".

    Docs

    Updated docs/guides/subtypes-and-facets.md with a new "Strict mode in practice" section
    covering the SDK_CORE_VOCABULARY pattern, a 4-step migration recipe, the Brainy-internal label
    reference table, and an 8.0 forward-look. docs/api/README.md documents brain.audit() and
    adds a strict-mode tip to the add() / relate() reference entries.

    Tests

    • New tests/integration/strict-mode-self-test.test.ts (13 tests): creates a brain under
      the exact SDK_CORE_VOCABULARY shape Venue hit + brain-wide strict mode, then exercises every
      internal Brainy path (VFS root/mkdir/writeFile/cp/mv/ln, aggregation engine, audit
      diagnostic, error-message UX). Zero rejections expected.
    • Existing 7.30.0 + 7.29.0 integration suites unchanged: 26/26 + 30/30.
    • Unit suite unchanged: 1468/1468.

    Downloads
  • v7.30.0 a82c3339df

    dpsifr released this 2026-06-05 20:16:15 +02:00 | 311 commits to main since this release

    Affected products: consumers modeling typed relationships with sub-classification
    (direct vs dotted-line management; spouse / sibling / colleague; collaborator vs competitor;
    etc.), and anyone wanting to enforce the pairing of type + subtype on every write.
    Additive; drop-in from 7.29.x. No deprecations.

    Symmetric — subtype on relationships (parity with 7.29.0 nouns)

    subtype?: string is now a first-class standard field on every relationship, mirroring the
    noun-side work shipped in 7.29.0. Verbs and nouns are now first-class peers — every API
    available on the noun side has a verb-side mirror.

    const ceoId = await brain.add({ type: NounType.Person, subtype: 'employee', data: 'Avery' })
    const vpId = await brain.add({ type: NounType.Person, subtype: 'employee', data: 'Jordan' })
    
    await brain.relate({
      from: ceoId,
      to: vpId,
      type: VerbType.ReportsTo,
      subtype: 'direct'                    // sub-classification on the edge
    })
    

    Read & filter:

    // Fast-path filter — column-store hit, not metadata fallback
    const direct = await brain.getRelations({
      from: ceoId,
      type: VerbType.ReportsTo,
      subtype: 'direct'
    })
    
    // Set membership
    const all = await brain.getRelations({
      from: ceoId,
      type: VerbType.ReportsTo,
      subtype: ['direct', 'dotted-line']
    })
    
    // Traversal filter (depth-1 in JS; multi-hop lands on Cortex native)
    const reports = await brain.find({
      connected: { from: ceoId, via: VerbType.ReportsTo, subtype: 'direct', depth: 1 }
    })
    

    New — updateRelation() closes a long-standing gap

    Verbs previously had no update method — the only way to change a relationship was
    delete-then-recreate, which lost the relation id. 7.30 ships brain.updateRelation():

    await brain.updateRelation({ id: relId, subtype: 'dotted-line' })
    await brain.updateRelation({ id: relId, weight: 0.5, confidence: 0.9 })
    
    // Change verb type — re-indexes in graph adjacency, id preserved
    await brain.updateRelation({ id: relId, type: VerbType.WorksWith })
    

    New — O(1) verb subtype counts via the persisted rollup

    _system/verb-subtype-statistics.json mirrors the noun-side rollup shipped in 7.29.0.
    Per-VerbType-per-subtype counts are maintained incrementally and persisted; reads are O(1):

    brain.counts.byRelationshipSubtype(VerbType.ReportsTo)
    // → { direct: 12, 'dotted-line': 3 }
    
    brain.counts.byRelationshipSubtype(VerbType.ReportsTo, 'direct')   // O(1) point
    // → 12
    
    brain.counts.topRelationshipSubtypes(VerbType.ReportsTo, 3)
    // → [['direct', 12], ['dotted-line', 3]]
    
    brain.relationshipSubtypesOf(VerbType.ReportsTo)
    // → ['direct', 'dotted-line']
    

    New — brain.requireSubtype(type, options) per-type enforcement

    Unified API for noun OR verb types — register specific types as requiring a subtype,
    optionally with a fixed vocabulary:

    brain.requireSubtype(NounType.Person, {
      values: ['employee', 'customer', 'vendor'],
      required: true
    })
    
    brain.requireSubtype(VerbType.ReportsTo, {
      values: ['direct', 'dotted-line'],
      required: true
    })
    
    // Now this throws — Person requires subtype:
    await brain.add({ type: NounType.Person, data: 'no subtype' })
    
    // And this throws — 'matrix' isn't in the registered vocabulary:
    await brain.relate({ from: a, to: b, type: VerbType.ReportsTo, subtype: 'matrix' })
    

    New — Brain-wide strict mode

    new Brainy({ requireSubtype: true }) enforces subtype on every public write across the
    whole brain. Composes with per-type rules; per-type rules win when both apply.

    // Every write must include subtype
    const brain = new Brainy({ requireSubtype: true })
    
    // Exempt specific types (e.g. catch-all Thing)
    const brain2 = new Brainy({
      requireSubtype: { except: [NounType.Thing, NounType.Custom] }
    })
    

    When strict mode is on:

    • Every add() / addMany() / update() / relate() / relateMany() / updateRelation()
      rejects writes missing a subtype on a non-exempt type.
    • addMany() and relateMany() validate every item BEFORE any storage write —
      atomic-fail semantics, no partial writes.
    • Brainy's own infrastructure writes (VFS root, directories, files) bypass via the
      metadata.isVFSEntity: true marker so existing consumers' VFS usage continues to work.

    The brain-wide flag becomes the default in 8.0.0; the type-level subtype field becomes
    required at the type system level. The full 8.0 contract upgrade is coordinated through the
    internal platform handoff (CTX-SUBTYPE-8.0-CONTRACT).

    migrateField() extended to verbs

    The migration helper shipped in 7.29.0 now walks verbs too via the new entityKind option:

    // Migrate verb-side metadata.kind → top-level subtype
    await brain.migrateField({
      from: 'metadata.kind',
      to: 'subtype',
      entityKind: 'verb'
    })
    
    // Or walk nouns and verbs in one pass
    await brain.migrateField({
      from: 'metadata.kind',
      to: 'subtype',
      entityKind: 'both'
    })
    

    Default is entityKind: 'noun' (backward-compatible).

    Symmetry — noun + verb capability matrix

    7.30 closes every gap between nouns and verbs. Every capability available on the noun side
    has a verb-side mirror:

    Capability Nouns Verbs
    subtype top-level field ✓ (7.29) ✓ (7.30 new)
    Standard-field set STANDARD_ENTITY_FIELDS STANDARD_VERB_FIELDS (new)
    Field resolver helper resolveEntityField resolveVerbField (new)
    Statistics rollup _system/subtype-statistics.json _system/verb-subtype-statistics.json (new)
    Counts breakdown counts.bySubtype / topSubtypes / subtypesOf counts.byRelationshipSubtype / topRelationshipSubtypes / relationshipSubtypesOf (new)
    Fast-path filter find({type, subtype}) getRelations({type, subtype}) + find({connected, subtype}) (new)
    Update method update() updateRelation() (new — closed pre-7.30 gap)
    Migration helper migrateField() migrateField({entityKind: 'verb'|'both'}) (new)
    Enforcement requireSubtype(NounType, ...) requireSubtype(VerbType, ...) — one unified API
    Brain-wide strict mode new Brainy({ requireSubtype }) covers both same

    Cortex compatibility

    Verb subtype works under Cortex out of the box via auto-field-indexing. Cortex parity items
    for the verb side (native query planner recognition, verbSubtypeCountsByType native rollup,
    per-edge subtype on native graph adjacency for multi-hop traversal filtering) ship in the
    next Cortex release. Not a Brainy blocker.

    Docs

    Full guide: docs/guides/subtypes-and-facets.md (extended with Layer V + Enforcement
    sections). The verb subtype + enforcement APIs are documented in docs/api/README.md.


    Downloads
  • v7.29.0 8c68396e71

    dpsifr released this 2026-06-05 02:26:04 +02:00 | 313 commits to main since this release

    Affected products: anyone modeling entities with per-product sub-classification — every
    consumer that's been reaching for metadata.kind, a custom metadata.subtype field, a
    data.kind shape inside the payload, or similar ad-hoc conventions. Additive; drop-in from
    7.28.x. No deprecations.

    New — subtype promoted to a top-level standard field

    subtype?: string is now a first-class standard field on every entity, alongside type /
    confidence / weight. Use it as the platform-standard primitive for sub-classifying entities
    within a NounType (Person'employee' / 'customer'; Document'invoice' / 'contract';
    Event'milestone' / 'meeting'):

    await brain.add({
      data: 'Avery Brooks — runs the AI lab',
      type: NounType.Person,
      subtype: 'employee',                  // top-level write param
      metadata: { department: 'ai-lab' }
    })
    
    // Fast-path filter — column-store hit, not metadata fallback:
    const employees = await brain.find({ type: NounType.Person, subtype: 'employee' })
    
    // Set membership:
    const internal = await brain.find({
      type: NounType.Person,
      subtype: ['employee', 'contractor']
    })
    

    Flat string, no hierarchy — your vocabulary, your choice. Brainy stores and counts, never validates.

    New — O(1) subtype counts via the persisted rollup

    A new _system/subtype-statistics.json rollup is maintained incrementally as entities are added,
    updated, and deleted — mirroring the existing nounCountsByType machinery with the same self-heal
    behavior on poison detection. Counts are O(1) at billion scale:

    brain.counts.bySubtype(NounType.Person)
    // → { employee: 12, customer: 847, vendor: 34 }
    
    brain.counts.bySubtype(NounType.Person, 'employee')   // O(1) point count
    // → 12
    
    brain.counts.topSubtypes(NounType.Person, 3)
    // → [['customer', 847], ['employee', 12], ['vendor', 34]]
    
    brain.subtypesOf(NounType.Person)
    // → ['customer', 'employee', 'vendor']
    

    New — brain.trackField() for other metadata facets

    For facets that aren't the primary sub-classification (status, source, role, paradigm),
    trackField registers a field for cardinality + per-NounType breakdown stats without promoting
    each one to a top-level slot. Piggybacks on the existing aggregation engine, so backfill-on-define
    applies — registering on a populated brain scans existing entities on the first query:

    brain.trackField('status', { perType: true })
    
    await brain.counts.byField('status')
    // → { todo: 12, doing: 3, done: 47 }
    
    await brain.counts.byField('status', { type: NounType.Task })
    // → { todo: 8, doing: 2, done: 30 }
    
    // Opt-in vocabulary validation:
    brain.trackField('priority', { values: ['low', 'medium', 'high'] })
    // brain.add({ ..., metadata: { priority: 'urgent' } }) → throws
    

    New — generic brain.migrateField() for one-shot rewrites

    Streams every entity and copies a value from one field path to another. Supports top-level
    standard fields, metadata.*, and data.* paths; idempotent; with optional readBoth: true
    deprecation window that preserves the source field alongside the new one:

    // Phase 1: dual-populate (legacy readers still work)
    await brain.migrateField({
      from: 'metadata.kind',
      to: 'subtype',
      readBoth: true
    })
    
    // ...readers migrate to subtype at their own pace...
    
    // Phase 2: clear the source field
    await brain.migrateField({ from: 'metadata.kind', to: 'subtype' })
    // → { scanned: 1500, migrated: 1500, skipped: 0, errors: [] }
    

    Supports batchSize and onProgress for large brains.

    Aggregation composition

    subtype is a standard-field group-by dimension out of the box — no where: wrapper, no
    metadata-fallback overhead:

    await brain.find({
      aggregate: {
        groupBy: ['type', 'subtype'],
        metrics: { count: { op: 'COUNT' } }
      }
    })
    // [
    //   { groupKey: { type: 'person',   subtype: 'customer' }, count: 847 },
    //   { groupKey: { type: 'person',   subtype: 'employee' }, count: 12 },
    //   { groupKey: { type: 'document', subtype: 'invoice'  }, count: 2103 },
    //   ...
    // ]
    

    Cortex compatibility

    Subtype works under Cortex out of the box via auto-field-indexing. Cortex will land its own
    native-side query-planner recognition + subtypeCountsByType native rollup in the next Cortex
    release for full parity with type's fast path. Not a Brainy blocker — subtype reads/writes
    function correctly with current Cortex.

    Docs

    Full guide: docs/guides/subtypes-and-facets.md. Subtype is documented as a core primitive
    throughout README.md, docs/DATA_MODEL.md, docs/architecture/finite-type-system.md,
    docs/api/README.md, and docs/QUERY_OPERATORS.md.


    Downloads
  • v7.28.0 4927d49e87

    v7.28.0 Stable

    dpsifr released this 2026-05-28 21:27:29 +02:00 | 318 commits to main since this release

    Downloads
  • v7.27.0 338946cec2

    v7.27.0 Stable

    dpsifr released this 2026-05-28 20:55:37 +02:00 | 320 commits to main since this release

    Downloads
  • v7.26.0 d30eb6f39f

    v7.26.0 Stable

    dpsifr released this 2026-05-28 19:38:18 +02:00 | 322 commits to main since this release

    What's Changed

    New Contributors

    Full Changelog: https://github.com/soulcraftlabs/brainy/compare/v7.25.0...v7.26.0

    Downloads