Skip to content

Releases: luizgmassa/massa-ai

v1.15.0

Choose a tag to compare

@github-actions github-actions released this 30 Jul 21:45

Added

  • skills/ duplication and reachability now ship as a committed regression gate.
    scripts/skills-duplication-metric.ts measures shingled line duplication across every
    skills/ Markdown file (duplicatedLines is the total footprint, excessLines is the
    removable figure — length × (copies - 1) — and the two are now reported and tested
    separately after an earlier draft conflated them) and scripts/skills-reference-graph.ts
    resolves every skills/ file against the rest of the repository to flag orphaned or
    weakly-referenced files. Both ship with unit tests reached by bun run test:scripts, and
    excessLines carries a ceiling assertion so future duplication growth fails the gate
    instead of accumulating silently; a snapshot-equality assertion was rejected because it
    would go red on any unrelated edit.

Fixed

  • skills/AGENTS.md's Agent Table hand-named a model per specialist in a second location,
    already stale for two roles and unresolvable or profile-ambiguous for two more —
    skills/model-profiles.json is the only place that should name a model or effort level
    for any agent on any host. The "Model hint" column is removed in favor of a pointer to the
    charter's own metadata.model_tier, and a scripted, mutation-demonstrated scan now bans
    any model display name or model ID from that file.
  • Three citations across two shipped Maestro reference files pointed at one developer's
    home directory
    (references/maestro.md, references/maestro/fact-ledger.md, one
    instance named as a tier of the fact ledger's evidence taxonomy), unreachable for any
    other developer, CI runner, or session, and copied verbatim into all four
    apps/*-plugin/skills/ bundles. The coverage-checklist rule is rewritten to a reachable
    location instead of being dropped, and a repo-wide scan now fails on any /Users/ or
    /home/ path under skills/.
  • references/agent-orchestration.md's Roles table omitted judge and meta-judge
    for a full release cycle, invisible to the existing guard because it validated only the
    charter paths already mentioned there. Both charters are now documented, and a new
    coverage test asserts every directory under skills/agents/ has a Roles table entry.
  • The sub-agent roster guard could not match the string it existed to ban. It rejected
    the literal patterns 16 subagent, 16 [Ss]pecialist, and 16 reusable sub-agent, but
    the hyphenated 16 sub-agent specialists in docs/ONBOARDING.md defeated all three while
    CLAUDE.md separately still read "15" — both passed the guard. It now rejects any count
    other than 17 in that shape, with an explicit historical allowlist for legitimate
    past-tense narration, and all four stale counts found by scanning
    (CLAUDE.md, docs/ONBOARDING.md, FEATURES.md, .claude-plugin/marketplace.json) are
    corrected.

v1.14.0

Choose a tag to compare

@github-actions github-actions released this 30 Jul 17:53

Changed

  • Model and effort for the 17 subagent specialists now resolve from one registry, and
    three separate defects closed with it.
    skills/model-profiles.json is the only
    hand-authored place that names a model or an effort level for any agent on any host;
    each charter declares a metadata.model_tier (replacing metadata.model_hint) and
    scripts/lib/model-profiles.ts resolves tier + host + profile → {model, effort} at
    generate time. Before this, 184 of 304 model facts were hand-copied across six surfaces,
    and the four per-host tables in generate-subagent-artifacts.ts,
    subagent-parity.test.ts and FEATURES.md could — and did — disagree.

    Three shipping defects, not one cleanup:

    1. Cross-host tier drift on three roles, changing five shipped pins. navigator,
      requirements-analyst and planner each carried contradictory tiers across hosts.
      The sharpest proof was inside FEATURES.md itself: navigator's Claude and Codex
      rationale cells held the same sentence — "no frontier reasoning needed" — beside a
      standard-tier model on one host and a light-tier one on the other. Normalized to one
      tier per role, so navigator drops to the light tier on Claude, requirements-analyst
      rises to standard on OpenCode, and planner rises to deep on OpenCode.
    2. The Cursor emitter had never produced a valid agent file. It emitted tools and
      reasoningEffort — neither is a Cursor frontmatter key — plus a human-readable
      display name in model: where Cursor requires a model ID, and no readonly, which
      is Cursor's only documented permission mechanism. All 17 Cursor agents now emit only
      Cursor's documented fields, with readonly: true on the 14 read-only charters.
      model: is inherit on every tier: Cursor publishes no display-name→ID mapping and
      its catalog lists no entry for two of the three models previously pinned there, so
      inherit is the documented default rather than a guessed ID. cursor-agent models is
      the discovery path, and restoring differentiation is a registry-only change.
    3. OpenCode was sending name and metadata to the model provider as bogus model
      options on every subagent invocation.
      OpenCode's documented rule forwards
      unrecognized frontmatter keys to the provider as model options, so these were not
      inert — this was live behaviour, not hygiene. name: is gone (OpenCode takes the agent
      name from the filename, which already yields massa-ai-<n>), and the
      massa-ai-owned: true ownership marker moves from frontmatter metadata: to a
      <!-- massa-ai-owned: true --> body comment. It moves rather than being deleted
      because massa-ai-config agents uninstall scopes by that literal substring: removing
      it would have matched zero files, printed removed 0, and orphaned 17 installed
      agents. The substring is unchanged, so uninstall also still matches agent files an
      older version installed in the frontmatter form.

    Profiles are open data: adding, renaming or removing one is a registry edit with no
    TypeScript type, enum, or doc table to update. Selection is --profile=<name> >
    MASSA_AI_MODEL_PROFILE > registry hostDefaults[host], with no fourth rank — an
    unknown name at any rank is a named error, never a silent fallback to a default model —
    and validateRegistry reports every violation in one throw instead of one per run.
    Resolution is build-time because no host resolves a per-agent model from an env var;
    switching profiles means regenerating. One consequence is documented rather than worked
    around: CLAUDE_CODE_SUBAGENT_MODEL set to a real model silently defeats every registry
    pin on Claude, because it overrides frontmatter. FEATURES.md loses its four 17-row
    per-host tables and all four rationale columns in favour of one role→tier table, guarded
    by a doc-drift test; bun run verify:model-ids probes the installed harness CLIs for
    unresolvable IDs and skips an absent CLI with a named reason instead of passing vacuously.

    Merge note: main gained the judge and meta-judge charters (2 more specialists,
    17 total) from a concurrently-merged PR while this branch was in review. Both still
    declared metadata.model_hint under the retired convention; merging this branch onto
    that state migrates them to metadata.model_tier: deep — matching their original
    Claude/Codex pins (opus / gpt-5.6-sol, both this registry's deep tier) — and their
    Cursor/OpenCode output now goes through the same emitter fixes as the other 15 (Cursor
    inherit instead of the raw charter hint; OpenCode drops name/metadata).

    That normalization moves two more shipped model pins, which is a behaviour change and not
    bookkeeping: on OpenCode judge goes opencode-go/deepseek-v4-proopencode-go/minimax-m3
    and meta-judge goes opencode-go/kimi-k3opencode-go/minimax-m3. Both had the same
    cross-host drift this release removes — judge shipped deep on Claude and Codex but light on
    OpenCode. Note the side effect: judge-with-debate wants per-slot model diversity, and one
    tier resolves to one model per host, so the two charter defaults now coincide on OpenCode.
    That affects the fallback only — the workflow requests per-slot models at dispatch time
    where the host supports it, and names the degraded case DIVERSITY DEGRADED per its own
    contract.

v1.13.0

Choose a tag to compare

@github-actions github-actions released this 30 Jul 12:59

Added

  • judge-with-debate workflow + meta-judge and judge sub-agents (17 specialists). A new
    standalone workflow evaluates a user-supplied artifact through multi-agent debate: a meta-judge
    authors a tailored evaluation specification once (two-stage syntactic/semantic validation with a
    single named retry), three independent judges score it with quoted evidence, and the panel
    debates disagreements over up to 3 rounds until consensus (overall gap ≤ 0.5, every criterion
    gap ≤ 1.0, explicit accept) or reports an honest no-consensus — never a forced verdict. Judges
    communicate through the filesystem only; the orchestrator computes consensus from structured
    reply blocks and never opens judge report files. Reports persist under the new
    audits/judge/ family in audit-report-io.md (per-judge + consensus contracts, fidelity
    checklists). Per-slot model diversity (meta kimi-k3, J1 deepseek-v4-pro, J2 minimax-m3,
    J3 GLM-5.2) is requested per dispatch; hosts without dispatch-time model selection run charter
    defaults and the verdict is marked DIVERSITY DEGRADED, with a per-invocation capability probe
    so diversity activates automatically when a host gains the capability. Both charters are
    registered across all four plugin bundles (Claude opus, Codex gpt-5.6-sol, Cursor charter
    hint, OpenCode opencode-go/kimi-k3 + opencode-go/deepseek-v4-pro), enforced by a new
    workflow prose-contract test and the existing integrity/parity/validate-repository gates.

v1.12.1

Choose a tag to compare

@github-actions github-actions released this 30 Jul 00:12

Security

  • CodeQL code-scanning close-out (SEC-1..SEC-6). All GitHub workflows now declare
    least-privilege GITHUB_TOKEN permissions (contents: read baseline; publish jobs keep
    their elevated per-job grants). Synapse session IDs are generated by a single shared
    newSynapseSessionId() helper backed by crypto.randomUUID() instead of the duplicated,
    guessable Math.random() template. The code chunker's inline block-comment stripping is
    a linear single-pass scan, removing a quadratic-regex indexing stall on minified or
    adversarial single-line files. sanitizeFilePath removes ../ traversal tokens to a
    fixpoint so overlapping tokens like ....// can no longer smuggle a live traversal
    segment through a single pass. The unused isValidEmail export (quadratic regex, no
    production callers) was deleted. Compaction snapshot retrieval calls now interpolate
    values via JSON.stringify, closing a backslash-quote escape gap.

v1.12.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 20:51

Added

  • Plugin bundles now auto-detect hosts and upgrade only on version change. The
    harness plugin phase (the root install.sh k) harness menu,
    scripts/setup-local-first.sh, scripts/install-harness.sh --plugins) detects each agent host — its config dir
    exists or its binary is on PATH — and installs only detected hosts; absent hosts
    produce one skip log line and no filesystem writes. Every successful plugin install
    records the bundle version and an ISO-8601 timestamp in
    ~/.config/massa-ai/install-state.json (a v2-compatible extension that
    install-skills.sh round-trips but never writes): re-runs at the same version are
    no-ops, older recorded versions upgrade automatically, newer ones are never
    downgraded, and a host whose installer fails records nothing and never aborts the
    remaining hosts. --dry-run reports the per-host decision (install / upgrade /
    skip-current / skip-absent) without writing anything, and --uninstall removes the
    version record while keeping its existing semantics (whole-record delete only for
    plugin-owned platforms). Direct apps/<host>-plugin/install.sh --user installs
    behave exactly as before, plus the same version recording.

v1.11.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 19:00

Changed

  • The search subsystem's first capability module is extracted, and the split is now measured rather than asserted.
    rlm-fusion.ts becomes result-fusion.ts: fuseResults and generateScoreExplanation take no
    dependency record at all. The only facade member either ever read was RRF_K, the literal 60,
    now a module constant — so the module has never heard of ContextualSearchRLM, which is the
    property scripts/search-hub-metric.ts measures. Foreign modules reading the facade drop from
    6 to 5. Deepest foreign reach stays at 14 and the gate still fails, both expected: the
    maximum is set by rlm-search.ts and cannot move until that file is split. Three of the four
    frozen needle anchors live in this file and travelled byte-identical — resolution is by content,
    so moving an anchor is safe and reflowing one is a hard gate failure.

  • Before-baselines are frozen to committed fixtures instead of measured against the working tree.
    The previous sensors recorded their before-state as unit tests that scan the live directory, which
    holds only until the refactor those baselines exist to police begins — one extraction reddened five
    of them, and at the target state the scanned set is empty by design. Updating the pins per task
    would have turned a before-record into an after-record tracking whatever the change produced,
    leaving the final comparison with no referent. scripts/capture-facade-baseline.ts writes the
    matrix, fan-in/fan-out and anchor records once and refuses to run when the measured subject has
    moved; the suites assert that attestation, so a silent re-capture fails loudly instead of
    relocating the reference. Scoped to the nine figures that actually change — assertions about
    untouched directories, and those written as floors rather than pins, still measure the live tree.

  • The frozen-anchor check now pins anchor text, not anchor paths. It asserted that four needles
    resolved to two named files, which is the opposite of the constraint it enforces: anchors are
    resolved by content, so moving one between files is explicitly legal and only reformatting is
    not. The path pin therefore failed on the permitted operation and caught nothing the uniqueness
    check above it did not already catch.

v1.10.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 17:57

Changed

  • The shipped skills now carry their implementation obligations in the references every workflow already loads, instead of re-stating them per workflow. Six updates (SWU-01..06) consolidate onto the two load-line references, so the rules cannot drift out of sync with the workflows that inherit them. references/code-annotation.md §3 now excludes data/domain models — ORM and persistence entities, schema-mapped classes, repository entities, and behaviorless value objects — from unit testing, because their behavior lives at the repository/service seam, not in the fields; the exclusion is named by kind and language so it cannot be read as "skip tests on anything called a model" and does not weaken test-every-changed-path for code that has behavior. references/implementation-delivery.md makes worktree isolation mandatory for every implementation task with no size exemption. The commit, spec-driven, and ticket workflows were tightened so a Jira Phase/Wave maps to one Task with per-Task sub-tasks, branch names follow the Phase/Wave, and each Task is one [KEY]-prefixed commit, with the three prefix sites (branch, commit, PR) cross-linked rather than duplicated.

  • spec-driven now enforces the target repository's own rules and requires dependency injection per task and per phase. A new references/repo-rules-discovery.md defines how the workflow discovers and loads a target repo's AI-harness rule files (.claude/, .cursor/, .cursorrules, and the repo's module/unit-test/testing-area conventions) before implementation, enforces conformance, and records repo-rules: none present rather than fabricating rules when none exist. Independently (DI-01), references/spec-driven/tasks.md and references/spec-driven/execute.md now require dependency injection at both the per-task and per-phase granularity. Both are inherited through the existing load lines; no workflow body was duplicated, and the four host skill bundles (claude, codex, cursor, opencode) were regenerated from the single source.

v1.9.2

Choose a tag to compare

@github-actions github-actions released this 29 Jul 16:55

Fixed

  • A structural refactor of the search subsystem is now measurable, not asserted. contextual-search-rlm.ts
    was split once before, in M14, and the split moved code without moving responsibility: the host
    file went from 1668 to 463 lines while every extracted delegate took the class straight back as
    its first parameter. Lines-per-file — the only metric anyone watched — reported that as a
    success. scripts/search-hub-metric.ts measures what it missed. For every class, interface
    and type declared in a directory it counts maxForeignReach: the most distinct members any
    single other module reads off a binding of that type. Run across M14, the numbers invert the
    verdict — deepest foreign reach went 1 → 14 while the member count stayed flat (26 → 24).
    M14 did not widen the type; it multiplied who reaches into it.

    The script takes no type name, deliberately. An earlier version audited one hardcoded name and
    was evaded two ways: renaming the class produced a vacuous 0/0 pass, and moving the same state
    onto a differently-named aggregate record made the identical hub invisible. Enumerating every
    declaration closes both, and each has a regression test. One evasion is documented as surviving —
    passing collaborators as N individual parameters drives reach to 0 with coupling unchanged — so
    the gate is stated as necessary, not sufficient.

    It ships with 13 unit tests, one per defect found while building it. That is not ceremony: the
    measurement method was wrong four separate times, and two of the defects pushed in opposite
    directions, so the reported figure was stable across runs and commits and still incorrect. The
    worst of them stripped string literals, which pairs quotes across unrelated apostrophes and
    deleted real code — under-reporting by 42% on one commit and 0% on the others, which is exactly
    the shape that survives cross-commit comparison. Stability is evidence of determinism, not of
    correctness.

  • Four more sensors, so the refactor's before/after can be taken at all.
    scripts/search-facade-matrix.ts rebuilds the facade's member→consumer matrix from source;
    scripts/search-facade-metrics.ts measures fan-in and fan-out over git ls-files;
    scripts/check-frozen-anchors.ts verifies every needle anchor still resolves to exactly one
    location, in under a second, instead of only at the Ollama-backed retrieval gate; and
    scripts/check-characterization.ts pins the three behaviors that have exactly one real test
    each, by assertion count rather than by the presence of a describe name a hollowed block
    keeps.

    benchmarks/needles/run.ts now records each needle's rank and its resolved target into the
    report. Both were computed and then dropped on write, which made a report uncomparable across
    exactly the kind of change it exists to measure: recomputing an old report's rank against a
    renamed tree resolves the anchor to its new home, matches nothing in the old hit list, and
    reads as a miss on every needle — a total collapse manufactured by the measurement rather than
    observed by it.

    The recurring lesson, hit three times while building these: a reading can be an artifact of the
    state it was taken in. One suite was green at 17 pass / 0 fail while its own files were
    untracked, and it enumerates git ls-files — so it was blind to itself, and tracking it moved
    the number it reported.

v1.9.1

Choose a tag to compare

@github-actions github-actions released this 29 Jul 03:26

Fixed

  • Indexing no longer aborts when one file declares the same name twice with different
    kinds.
    let total in one block and const total in another — or class X alongside
    interface X, which is ordinary TypeScript declaration merging — made the whole index
    fail with fqn_identity_collision, discarding every file in the run. Measured on this
    repository: the abort came after all 1219 files had been discovered and parsed, roughly
    four seconds in, so the visible symptom was a project that simply never became
    searchable. The uniqueness check that decides whether a symbol gets the simple
    file#name identity or a disambiguated one was counting names per (file, name, kind)
    while the identity it protects is keyed on (file, name) alone; two declarations
    differing only in kind therefore each believed themselves unique and both claimed the
    same identity. Same-name/same-kind declarations were already disambiguated correctly and
    are unaffected. Symbols that do not share a name keep the identity they had.

  • An allow-list with exactly one file extension no longer matches nothing. The three
    file scanners built a single combined glob, **/*{.ts,.js,…}. A brace expansion with one
    alternative is not an alternation — it is matched literally — so a one-extension allow-list
    found zero files and indexing reported success over an empty corpus. Measured: a bounded
    index completed in 181 ms over 0 files. This was unreachable while the extension list was
    always the 33 built-in defaults, and became reachable with the security.allowedExtensions
    fix below; a single-language project is exactly the case where someone sets one extension.

  • capturePolicy in config.json now actually reaches indexing. The block was parsed,
    bounds-checked and denyUnknownFields-validated at config load, and then never consulted:
    applyCapturePolicy had no caller anywhere in the product, so a configured policy narrowed
    nothing. Discovery now applies it after the .gitignore merge, which is the composition
    ignore-patterns.ts has documented since the policy was introduced. With no policy
    configured the built-in DEFAULT_POLICY applies and its Drop set mirrors the default
    ignores, so a default install discovers exactly the same files as before. Keep cannot
    resurrect a path .gitignore excludes — the two layers compose with AND, and the ignore
    layer runs first. Only Drop excludes; MetadataOnly files are still discovered.

  • security.allowedExtensions in config.json now actually narrows what gets indexed.
    Setting the key had no effect whatsoever: the assembled config hardcoded the built-in
    default list and never read the value, and the user-facing config type did not declare
    the field at all — so the value was parsed, then discarded. The indexer, the search index
    scanner and the MCP upload collector all read the assembled value, so every consumer saw
    the defaults no matter what the file said. Omitting the key still yields the same 33
    default extensions, so installs that never set it are unaffected. An empty array is now
    rejected at config load
    rather than honoured: it would match no files, and indexing
    would report success over an empty corpus. Entries must be dot-prefixed (.ts, not ts).

  • search_memories honours includePersistent. The option has been advertised in the
    published MCP tool schema — and forwarded from the tool to the controller — while
    nothing ever read it, so a caller passing false silently received persistent memories
    anyway. It now excludes L0 (MemoryLevel.PERSISTENT) memories: the level assigned to
    orchestrator decisions and criticals, and the one the bootstrap seed writes. The
    published schema is unchanged; this makes the existing advertisement true rather than
    altering it. Callers passing includePersistent: false today will see a smaller
    result set.
    The default remains true, so callers that omit it are unaffected.

v1.9.0

Choose a tag to compare

@github-actions github-actions released this 28 Jul 16:43

Changed

  • BREAKING — the ten RLM_LLM_* environment variables are renamed to MASSA_AI_LLM_*.
    The old names no longer do anything.
    There is no dual-read and no deprecation window:
    an unrenamed variable is silently ignored, and the setting falls back to its default —
    which for RLM_LLM_ENABLED means every LLM-driven feature degrades to its rule-based
    path. RLM_ was the last surviving prefix from the pre-rename project identity, kept as
    a compatibility boundary by two earlier renames; that boundary is now retired
    (AD-010). Update any .env, shell profile, CI secret, compose file, or systemd unit:

    Retired Replacement
    RLM_LLM_ENABLED MASSA_AI_LLM_ENABLED
    RLM_LLM_BASE_URL MASSA_AI_LLM_BASE_URL
    RLM_LLM_API_KEY MASSA_AI_LLM_API_KEY
    RLM_LLM_MODEL MASSA_AI_LLM_MODEL
    RLM_LLM_CODE_MODEL MASSA_AI_LLM_CODE_MODEL
    RLM_LLM_TEMPERATURE MASSA_AI_LLM_TEMPERATURE
    RLM_LLM_MAX_OUTPUT_TOKENS MASSA_AI_LLM_MAX_OUTPUT_TOKENS
    RLM_LLM_TIMEOUT_MS MASSA_AI_LLM_TIMEOUT_MS
    RLM_LLM_DISABLE_THINK MASSA_AI_LLM_DISABLE_THINK
    RLM_LLM_PROMPT MASSA_AI_LLM_PROMPT

    config.json is unaffected — its keys were already prefix-free, so there is no
    config migration and no file to edit. Only environment variables change.

    The rename also fixed a bug it would otherwise have carried under a new name: only four
    of the ten were listed in turbo.json's passThroughEnv, so the other six arrived
    undefined in any test run under bun run test while working fine when bun test was
    invoked directly. All ten are listed now.

  • Coverage is no longer computed on every bun test. bunfig.toml set
    coverage = true, so every invocation — including a single-file run in a debug loop —
    paid to instrument and report coverage, and nothing ever failed on the result: the cost
    of a gate without the gate. Coverage is now opt-in (--coverage) and enforced by the
    new explicit gate below.

  • The three divergent copies of run-tests-isolated.ts are one shared module.
    packages/core, apps/tools-api and apps/mcp-client each carried their own copy
    (236 / 124 / 141 lines) which had drifted apart; they now share
    scripts/lib/run-tests-isolated.ts and supply only their own isolation predicate.
    Observable behaviour is deliberately unchanged, including each package's distinct log
    wording and its exit code for unknown arguments.

Added

  • bun run lint is a real gate. It was declared in turbo.json but implemented by no
    package, so it printed "No tasks were executed" and passed unconditionally. It now runs
    oxlint (pinned exactly to 1.76.0) with the correctness category on and every other
    category off. Adoption surfaced 337 violations; all 337 were fixed rather than
    downgraded, so every correctness rule ships at error rather than warn. It runs over
    the whole repository rather than per package, because turbo dispatches only to workspace
    packages and scripts/ and benchmarks/ — which held 21 of the 337 — are neither. CI
    runs it in the build job. No formatter was added and nothing was reformatted.

  • bun run test:coverage enforces a coverage floor. 90% line coverage per file, with
    nine documented exclusions, each carrying the justification that earned it. Both the
    floor and the exclusions live in scripts/check-coverage.ts as executable data rather
    than as prose in a scratch file that gets rewritten. The gate refuses to run unless the
    dedicated test database is configured, because 50 core suites are gated behind
    describe.skipIf(!DEDICATED_DB) and would otherwise report near-zero coverage for
    subjects whose own tests are sitting skipped beside them. It also runs the suites against
    a scratch XDG_CONFIG_HOME, so the numbers are a property of the tree rather than of
    whatever is in the developer's ~/.config/massa-ai/config.json.

Fixed

  • Unit tests no longer make live LLM calls against the developer's own configuration.
    CodeCompressor resolves llm.enabled from ~/.config/massa-ai/config.json, so on a
    machine with a local Ollama configured, tests that construct it without pinning the
    test seam issued real network requests — measured at 42 s on a cold model load against
    a 5 s per-test budget, and 0.7 s once warm. That is why they looked flaky rather than
    broken, and why CI, which has no configuration file, never saw it. The affected tests
    now inject the seam their subject already exposes.