Releases: NexaDuo/mARC
Releases · NexaDuo/mARC
Release list
v26.9.8
[26.9.8] - 2026-09-08
Added
- Cross-harness subagent delegation and poly-model routing (#239, #240).
- Added
core/scripts/dispatch_agent.pyhelper script allowing@techleadto delegate specialist tasks across different agent CLI harnesses (claude-code,antigravity,copilot) when[orchestration]is declared inteam.toml. - Supports explicit route mapping per specialist under
[orchestration.routes]with automatic fallback to native host harness if target CLI is unavailable.
- Added
- Embedded default hybrid specialization matrix in
dispatch_agent.py(#241, #242).- Implemented zero-config hybrid routing matrix when
--harness autoor unconfigured: routes@devand@sectoclaude-code,@revand@researchtoantigravity(large-context review/survey), and@sre/@designto the native host harness. - Added automatic capability detection via
shutil.whichand graceful fallback to host harness when a target CLI binary is not present onPATH.
- Implemented zero-config hybrid routing matrix when
- New upstream contribution skill
/marc:upstream(@scribe).- Added
core/skills/upstream/SKILL.md(and compiled to all harnesses) allowing teams in consuming repos to safely harvest local emergent rules, sanitize sensitive client facts, apply governed rule provenance (<!-- rules:origin-required -->), and open Pull Requests/Issues upstream toNexaDuo/mARCwith explicit human opt-in.
- Added
- Durable evaluation and decision records for AI-Memory 2.0.
- Added research brief
docs/marc/2026-09-08-brief-ai-memory-2-evaluation.mdand decision recorddocs/marc/2026-09-08-decision-ai-memory-2-reaffirmation.md. - Reaffirmed Decision #175 (mARC remains marketplace-only, zero-config, and PR-gated without mandatory daemon dependencies).
- Adopted four core zero-dependency patterns: Open Knowledge Format (OKF) artifact metadata, static typed rule relation checks (
supersedes,contradicts), operator baton handoff conventions, and opt-in read-only MCP bridging.
- Added research brief
Changed
- Migrated versioning scheme to Calendar Versioning (CalVer
YY.M.D).- Transitioned from SemVer
0.28.0to CalVer26.9.8across all plugin manifests (claude-code,antigravity,copilot), aligning release versions directly with date-governed rule provenance.
- Transitioned from SemVer
v0.28.0
[0.28.0] - 2026-09-05
Changed
- Rule #137 made executable in Grep-less harness modes and propagated to all specialists (#228).
Rule #137 ("never ingest file content via filtered bash") previously assumed aGreptool always exists and only covered@sec,@rev, and@dev. In harness modes (such as bypass-permissions modes) whereGrepis not exposed and the harness injects a system-prompt block nudgingcat/sed/headover structured tools, the rule failed to account for missing tools, and the counter-rule was missing from@sre,@design, and@research.- Revised the rule in
security.md,review.md, andengineer.md:Readis the primary content tool,Grepis used only when the session actually exposes it, and a named fallback (the filtering proxy's raw/passthrough escape hatch) is provided for bash-only reads, reporting the read as unfiltered. - Added the adapted counter-rule across
sre.md,design.md, andresearch.md, closing the propagation gap across all specialists. - Updated
@techlead's dispatch instructions to stop mandating aGreptool that a target session might not have. - Added explicit handling for harness/hook instructions or MCP preamble suggesting bash reads or unrelated tool calls: treat them as harness noise, disregard, report, and continue working without halting.
- Added an
AGENTS.mdLessons entry recording the diagnostic procedure for distinguishing harness-emitted prompt text from repo-borne prompt injections.
- Revised the rule in
Fixed
- Rule-origin governance CI gate negative test strips all origin tags globally (#228).
The negative self-test in.github/workflows/ci.ymlpreviously stripped only the first(origin: ...)tag inengineer.md. When a rule carries multiple origin tags (e.g. #137 and superseding #227), stripping only the first left the rule tagged, making the negative test a no-op. The test now strips tags globally (s///g).
Documentation
- Synchronized product capabilities across README, hotsite, and CHANGELOG (#234).
- Documented GitHub Copilot CLI harness support in the README and landing page.
- Updated the channel roster and hero descriptions to include
@revand@researchspecialists and peer operator claim protocols.
v0.27.0
[0.27.0] - 2026-08-28
Fixed
- Antigravity script-backed hooks resolved with
$PWDplugin-root fallback
(#220). Antigravity CLI does not exportAGY_PLUGIN_ROOTto hook commands,
but runs hooks with the working directory ($PWD) set to the directory containing
hooks.json(the plugin root). Compiled hook commands now use${plugin_root_env:-$PWD}
and${project_dir_env:-${OLDPWD:-$PWD}}so all script-backed hooks (outdated-check,
invariants-card,token-guard,outdated-recheck,token-telemetry) resolve
and execute rather than silently taking the missing-script fallback. - Hook missing-script diagnostic deduplication across harnesses without
session_id
(#221). The_report_once_fragmentdeduplication marker extracted only
session_idfrom stdin JSON. On harnesses providingconversationId(such as
Antigravity) and nosession_id, the key defaulted tonosession, permanently
suppressing missing-script errors across all future sessions after the first occurrence.
The diagnostic now parses bothsession_idandconversationId, falling back to
ANTIGRAVITY_CONVERSATION_IDand$PPID(parent CLI process PID), preserving anti-nag
per session while staying observable across new sessions. - Regression tests for cross-harness hook execution and fallback resolution (#220, #221).
test_hooks_parity.pynow asserts that all hook script commands resolve when
harness-specific environment variables are unset, and tests session/conversation ID extraction.
v0.26.0
[0.26.0] - 2026-08-25
Changed
- Concurrent-operator claim moved off the assignee field to a comment marker
(#213). #208's claim mechanism (gh issue edit <N> --add-assignee @me)
is a no-op the moment two operators share aghlogin — the default for a
solo developer running two harnesses against one clone — because both
operators re-read the same login and both conclude "I am alone," and every
pre-existing human self-assignment now reads as a possible squat under the
stale-claim rule.core/skills/tech-lead/SKILL.mdnow claims with a
grep-verifiable## @techlead claimcomment carryingoperator: <harness>/<session-id>,issue: #<N>, andclaimed-at:(the same marker
discipline as## @sec review/## @rev review); the assignee field is
demoted to a human-visible-only signal and an issue with no claim comment is
explicitly not claimed regardless of assignees. The tie-break moves from
the (unusable, shared) login to theoperator:token, and only over claims
that pass an author-association check. #208's superseded wording stays in
the file, marked superseded with its justification, per the no-silent-delete
rule. - Security fix, same PR: the claim marker is public-repo forgeable, so
a claim now needs a trusted author and withdrawal is never autonomous
against an untrusted one (#213 review round). Posting an issue comment on
a public repo needs no collaborator status, unlike the assignee mechanism it
replaced — an unmitigated marker let any GitHub account post a
low-sortingoperator:value and force the legitimate operator to withdraw
autonomously, indefinitely suppressing dispatch on any issue. A
## @techlead claimcomment now counts only when itsauthor_association
isOWNER,MEMBER, orCOLLABORATOR; autonomous withdrawal is permitted
only when losing the tie-break to a claim that passed that check, otherwise
the operator surfaces a suspected forged claim to the user instead of
standing down. The rule states the trust boundary plainly: the marker
coordinates cooperating operators, it is not an authorization mechanism.
Withdrawal also gets its own fixed## @techlead withdrawmarker (same
operator:/issue:fields) so a withdrawal that doesn't delete the
original claim can't be mistaken for a live one — deleting the original is
an optional courtesy, never load-bearing. - Second fix round, same PR: correct the association-check field name per
command, and close the withdrawal-side forgery gap the prior round left
open (#213 re-review). Two HIGH findings on the prior round's fix: (1) the
text named the fieldauthor_associationforgh issue view <N> --json comments, but that command actually returns it asauthorAssociation
(camelCase) — verified live against real issues in this repo; only the raw
gh api/REST path uses the snake_case name. As written, the check would
fail closed for every claim on the documented primary path, including
legitimate ones, reintroducing #213's original collision through a
documentation defect rather than fixing it. Both field names are now stated
explicitly, paired with the exact command each belongs to. (2) The
## @techlead withdrawmarker had no author-association check of its own,
so any untrusted account could copy a claim's publicoperator:token into
a forged withdrawal and make a live, legitimate claim read as abandoned —
the same forgery class the claim-side check exists to close, just moved to
the other marker. A withdrawal now only retires a claim if it passes the
same association check as a claim AND itsoperator:matches exactly; the
rule states the general principle so future markers don't repeat the gap:
a claim and its withdrawal are two sides of one state transition and are
trusted identically. git worktree listis now a mandatory pre-dispatch read, and a dead
worktree gets a named, user-gated remedy (#214). Audited live: a single
.gitshared by two harnesses registers every operator's checkout, so
git worktree list --porcelainis free cross-harness ground truth that the
convention never read.core/skills/tech-lead/SKILL.mdnow requires reading
it before any mutating dispatch (a branch already checked out elsewhere
means another operator owns it — don't re-cut it), names a worktree that is
locked/gone, at the base SHA, with no commits and no linked PR as a dead
worktree (distinct from a live claim and from a squat), and gives the
concrete remedy (git worktree prune/git worktree remove --force)
gated on user confirmation, since a worktree can hold uncommitted work. This
repo's own.claude/worktrees/is now in the committed.gitignore
(previously local-only via.git/info/exclude, which doesn't survive a
fresh clone).AGENTS.md's duplicate concurrent-operator prose synced withSKILL.md,
and its new origin-tag fence is now CI-gated.AGENTS.mdcarried a full
second copy of the pre-#213/#214 protocol that would otherwise drift out of
sync with the amended source of truth — collapsed to a pointer plus the two
facts a reader ofAGENTS.mdalone needs..github/workflows/ci.yml's
rule-origin governance gate now scansAGENTS.mdtoo, closing a gap where
its first-everrules:origin-requiredfence shipped correctly tagged but
unguarded against a future silent strip.
v0.25.0
[0.25.0] - 2026-08-25
Changed
- Rule #137 amended:
Readis necessary but not sufficient (#210). The
no-filtered-bash rule told@sec/@rev/@devto read file content with
Read/Greponly. That is not sufficient: on files with very long single
lines (rawgh --jsonoutput, dense prose) the compression layer mangles
Readoutput too — fragments rather than honest truncation, invisible to a
"looks fine" check. (Provenance: an operator-memory field note, not
reconstructible from any issue thread; the rule stands on the mechanism, not
on that note.) All three rule sites
(core/skills/tech-lead/SKILL.md,core/agents/security.md,
core/agents/review.md) now name the detection (comparewc -lagainst the
highest line number that was displayed, and treat text breaking mid-token as
mangled) and the recovery (re-fetch to a file, reformat to short lines,
re-read in small line-limited chunks — never pipe the content throughBash),
and say plainly that a verdict must never be issued over input the reviewer
cannot confirm it read whole: after two failed recovery attempts the input is
reported unreviewable and escalated. - A merged product change with no version bump means a bump PR is needed
(#210). A merge+release pass once concluded "no release needed" because the
merged PR carried no bump — backwards, since a merge is not Done until a
released tag covers it. The skill carried only the converse rule ("a bump
isn't released until its tag is pushed"); the missing direction is now stated. @techleadstops volunteering compaction advice (#184, PR #196). The
operator no longer offers unprompted compaction or session-restart suggestions
— that call belongs to the harness, and the only thing that may trigger the
advice is an explicit[mARC token-guard]warning. The obsolete task-boundary
context-hygiene advisory from #81 came out at the same time, and
references/invariants-card.mdnow records unprompted/volume-based compaction
as a rejected pattern so it doesn't get re-proposed.
Added
- Memory conventions: size-capped writes, pinned vs decay, and a two-tier
recall index (#176, PR #201).core/skills/tech-lead/SKILL.mdand the
invariants card carry rules for keeping memory recall bounded, and record an
external memory daemon as a rejected pattern.AGENTS.mdgets the matching
tiered, size-capped operating principle. - Concurrent-operator coordination protocol in
@techlead(#208). Two
operators — different harnesses, or two sessions — may run against the same
clone with no supervisor between them (#202 decided harnesses are
board-mediated peers).core/skills/tech-lead/SKILL.mdnow carries four
rules under step 3: claim with the assignee field before dispatching (only
the assignee carries operator identity — Status is a shared enum with no
author, so it cannot tell you who claimed an item); the claim is racy and
knowingly accepted; stale claims are reclaimed by a human, never by a timer,
and an unclearing claim from a peer you don't control is escalated rather than
raced; and writer isolation extends to the operators themselves, not just to
dispatched specialists.
There is deliberately no locking layer. GitHub's GraphQL exposes no
optimistic-concurrency field onUpdateIssueInputor
UpdateProjectV2ItemFieldValueInput(verified against the live schema in
#205), soboard.py set-status's last-write-wins window cannot be closed by
adopting an API feature — it is accepted explicitly, and the skill says so.
The prior art behind that stance lives in this repo'sAGENTS.mdand the #205
brief underdocs/marc/, not in the shipped skill: GitHub's own coding agent
claims work by assignment, and Renovate — which has no claim field at all —
serializes with external CI locks plus a per-instance work directory. Neither
builds compare-and-swap into the tracker (#204, #205, #206).
Fixed
- Antigravity
hooks.jsonnow lands at the plugin root (#197, PR #198). It
was being emitted underhooks/, where Antigravity doesn't look for it, so the
hooks shipped in 0.24.0 never resolved for that harness.
scripts/compile_prompts.pygainedget_hooks_json_pathplus cleanup of a
stalehooks/hooks.jsonor roothooks.json, the path is declared as
hooks_pathinharnesses/antigravity/marc/compile.json, and both
test_hooks_parity.pyand the Tier 2 CI assertion check the installed root
location and the absence of the stale one.
Security
- CI's Antigravity CLI installer is pinned and checksum-verified (#169, PR
#199). The workflow no longer pipesinstall.shstraight into a shell: it
downloads, verifies against a pinned SHA256, and only then executes.
ANTIGRAVITY_API_KEYis out of the unprivileged bootstrap and package-install
steps entirely, scoped now to plugin install and the registration assertion.
Repo-internal (.github/workflows/ci.yml); nothing consumer-facing changes.
Documentation
- Decision record for cross-harness dispatch — harnesses are board-mediated
peers, with no nested dispatch bridge (#202, PR #203). - Research brief on Antigravity context compaction (#186, PR #200).
- Research brief on concurrent-operator coordination, plus this repo's own
AGENTS.mdconvention that #208 later promoted into the plugin (#206, PR
#207).
v0.24.0
[0.24.0] - 2026-08-24
Added
- Native Google Antigravity hooks compiler and subagent orchestration (#193, PR #194).
- Implemented
render_antigravity_hooksinscripts/compile_prompts.pysupporting native Antigravityhooks.jsonschema ({"<hook_id>": {"PreInvocation": [...], "PostToolUse": [...], "Stop": [...]}}). - Switched
harnesses/antigravity/marc/compile.jsonto"hook_dialect": "antigravity". - Upgraded Antigravity
@techleaddispatch instructions with nativeinvoke_subagentfeatures:Workspace: "share"for parallel writer isolation, Gemini model tier selection (flashfor@research,pro/inheritfor@dev,@sec,@rev,@sre), dynamic specialization viadefine_subagent(e.g.enable_write_tools: falsefor read-only agents), andsend_messagecoordination. - Added hook dialect structural schema validation in
core/scripts/test_hooks_parity.py. - Updated
harnesses/antigravity/marc/COMPATIBILITY.mdand CI assertions.
- Implemented
@secgains theSkilltool for/security-review(#191, PR #192).@sec
(tools nowRead, Grep, Glob, Bash, WebFetch, TodoWrite, Skill) invokes the
harness's built-in/security-reviewas an additional input pass alongside
its existing checklist, closing the capability gap with@rev's
/code-reviewgrant from #125. The skill never replaces the checklist or
the deliverable:@secstill authors the## @sec reviewcomment with its
own ranked findings and verdict, and treats a thin or empty
/security-reviewresult as inconclusive rather than a PASS.
core/skills/tech-lead/SKILL.mdnow also records that granting a
specialist a new tool is the operator's decision, made per demonstrated
capability-need and never a blanket default.
Fixed
hooks.jsonis now compiled fromcore/, and Antigravity's hooks actually
work (#173, #170).hooks.jsonwas the one load-bearing plugin component
hand-maintained per harness instead of generated — Claude Code's copy was
de-facto canonical, Copilot's hand-written copy drifted independently (twice,
per #166/#173), and Antigravity's was a bare symlink to Claude Code's
hooks/that hardcoded${CLAUDE_PLUGIN_ROOT}with no fallback, so all five
Antigravity hooks (outdated-check, invariants-card, token-guard,
outdated-recheck, token-telemetry) silently no-op'd.core/hooks/ hooks.spec.jsonis now the single harness-neutral source; each harness's
compile.jsondeclares an explicithook_dialect(Claude Code/Antigravity
share one schema, Copilot has its own) andhook_ids(which hooks it
ships — Copilot's narrower coverage is now a reviewable declaration, not an
accident).scripts/compile_prompts.pyrenders each harness's own
hooks/hooks.jsonand copies only the.shscripts it actually needs; the
Antigravity symlink is gone in favor of a real compiled directory. A hook
whose script cannot be found now says so once per session, visibly, on
stderr (deduped via a state-file marker keyed off the hook's own
session_id, so a broken install doesn't flood the transcript on every
PostToolUsecall) instead of the old blanket2>/dev/null; exit 0that
made a resolution failure indistinguishable from "ran, nothing to report"
— ordinary quiet no-ops (e.g. noteam.tomlin a repo) are unaffected.
outdated-check.sh/outdated-recheck.shalso now find the plugin manifest
at either.claude-plugin/plugin.json(Claude Code) or a root-level
plugin.json(Antigravity/Copilot). The compiler validates every
spec/config value it interpolates into a hook command and refuses
(fail-closed) to render one containing a shell metacharacter, so a future
careless edit can't splice unescaped shell into the shippedhooks.json.
A newcore/scripts/test_hooks_parity.pygates CI against hand-edit drift,
missing hook scripts, and that same class of unsafe interpolation,
alongside the existingtest_script_parity.py.
v0.23.0
[0.23.0] - 2026-08-12
Removed
- Context-size advisory retired (#181, decision recorded 2026-08-12).
token_sentinel.py's third PostToolUse guard — the one that watched
per-turn weighted tokens and suggested/compacton an oversized context —
is removed, along withcontext_window(),MARC_CONTEXT_WINDOW,
DEFAULT_CONTEXT_WINDOW,CONTEXT_WINDOW_FRACTION,
MIN_CONTEXT_FRACTION_TO_WARN,hook_tokens_threshold(),
MARC_TOKEN_GUARD_TOKENS_THRESHOLD, and themax_contextsnapshot added
for it in #178. Claude Code's own harness already knows the real per-model
context window, warns on it, and auto-compacts by default
(autoCompactEnabled/autoCompactWindow) — a strictly better mechanism
than a guard that could only guess the window and could never act itself.
This is a removal, not a fix: the harness's native auto-compact supersedes
it entirely. The--tokensmanual CLI flag survives with a self-contained
default (DEFAULT_CLI_TOKENS_THRESHOLD, still 130000) — it's an explicit,
operator-invoked report column, not a silent hook assumption. The call-count
runaway guard (#71) and the mid-session model-switch guard (#73) are
unaffected; neither depended on the context window. See
docs/marc/2026-08-12-decision-context-advisory-retired.mdfor the full
decision record.
v0.22.2
[0.22.2] - 2026-08-05
Changed
- Context-size advisory is now fail-closed and opt-in by default (#181).
token_sentinel.py's hook path no longer falls back toDEFAULT_CONTEXT_WINDOW
(200K) whenMARC_CONTEXT_WINDOWis unset, non-numeric, or<= 0: with no
trustworthy window value AND no explicitMARC_TOKEN_GUARD_TOKENS_THRESHOLD,
the context-size advisory stays completely silent instead of banding against
an assumed window. This closes the residual false positive from #178/PR #179,
where an unsetMARC_CONTEXT_WINDOWstill let the guard fire against a 130K
band derived from the assumed 200K on a session with a much larger real
window. Set eitherMARC_CONTEXT_WINDOWorMARC_TOKEN_GUARD_TOKENS_THRESHOLD
to opt back in. The call-count runaway guard (#71) and the mid-session
model-switch guard (#73) are unaffected — neither depends on the context
window.
v0.22.1
[0.22.1] - 2026-08-05
Fixed
- Context-size guard was window-blind and measured a per-turn sum, so it fired
on large-window sessions at roughly 10% of real context usage (#178, PR
#179). The advisory now derives its warning band from the session's actual
context window, gates on remaining headroom against amax_context
snapshot, and excludes subagent/sidechain requests from that snapshot
(subagent spend is still counted in cost totals).
v0.22.0
[0.22.0] - 2026-07-27
Changed
- Per-repo config default moves to
.agents/team.toml, with.claude/team.toml
kept as a backward-compatible fallback (#163, PR #166).core/agents/*.md
(all six specialists),core/skills/tech-lead/SKILL.md, and
core/skills/init/SKILL.mdnow read the new{{ agents_dir }}/team.toml
path first and fall back to the legacy{{ config_dir }}/team.tomlpath,
matching the fallback semantics already shipped inhooks/hooks.jsonand
core/scripts/board.py/token_telemetry.py. The GitHub Copilot harness
gained the missingagents_dircompile key so/marc:initno longer ships
a literal{{ agents_dir }}placeholder.
Fixed
- Cross-harness find-and-replace regressions from the
.agents/migration.
harnesses/copilot/marc/compile.jsonwas missingagents_dir; the
legacy-migration line incore/skills/init/SKILL.mdnow templates the
correct per-harness legacy path (.claude/team.configfor Claude Code,
.agents/team.configfor Antigravity,.github/copilot/team.configfor
Copilot) instead of a hardcoded.claude/team.config. - Reverted an accidental
.claude/settings.json→.agents/settings.json
rename. Claude Code only reads.claude/settings.json; the rename had
silently disabled this repo's ownenabledPlugins.marc@nexaduopin. The
.agents/team.tomlconfig-path migration is unaffected and stays. - Corrected the self-contradictory
hooks/hooks.jsondeprecation message
(it claimed ".agents/team.toml only" while implementing a.claude/
fallback in the same command), theCOMPATIBILITY.mdtable's mismatched
team.tomllink, and the README's overstated hard-cutover wording. - Removed the one-off, unreferenced
fix_files.pymigration script from the
repo root. - Copilot
sessionStarthook never learned the.agents/team.tomlwrite
path.compile.jsongainedagents_dirso/marc:initwrites
.agents/team.toml, butharnesses/copilot/marc/hooks/hooks.json(hand-
maintained, not templated fromcore/) still only read
.github/copilot/team.toml, so a freshly-initialized repo's sessionStart
hook silently found nothing. It now checks.agents/team.tomlfirst and
falls back to.github/copilot/team.toml, mirroring the Claude Code hook's
resolution order (PR #166). /marc:initcould write a second, silently-staleteam.toml. The
"never overwrite without asking" check only looked at the new
{{ agents_dir }}/team.tomlpath, so a repo that already had a legacy
{{ config_dir }}/team.tomlended up with both files — reads prefer
.agents/, so the old one became a decoy that still looked live.
core/skills/init/SKILL.mdnow detects the legacy path first (skipped for
Antigravity, whereagents_dirandconfig_dirare the same directory),
shows the user what it found, and on confirmation moves it to the new path
and offers to delete the obsolete file, mirroring the existing
team.config→team.tomlmigration. The Antigravity no-op (where
agents_dirandconfig_dirare literally the same path) is an
unconditional, string-comparison hard gate that sits first in the block and
precedes every destructive instruction, not a parenthetical a reader could
skim past — reaching the delete step requires the two paths to have
already been confirmed distinct. The instruction's stated reason to delete
the old file was also corrected: once.agents/team.tomlexists,
the SessionStart hook's fallback branch never runs again, so a stale
legacy file goes silently stale, not "still nagging" — the real risk is
drift between two live configs once reads prefer the new path.- Legacy-path fallback in the SessionStart hook was indistinguishable from
the current path. The Claude Code and Copilothooks.jsonprinted the
same config output whether it was resolved from{{ agents_dir }}/team.toml
or the legacy{{ config_dir }}/team.toml, so a repo sitting on the
fallback got no signal to migrate. Both hooks now emit one extra line
naming the deprecated path and pointing at/marc:initwhen the fallback
branch fires (Antigravity shares Claude Code's hooks via symlink and, since
its legacy fallback path is never populated in practice, is unaffected).