Releases
Release notes for qargi, from 0.2.1 back to 0.1.0: what was added, changed, removed, fixed and secured in each version, from the changelog.
Updated
0.2.1 #
Added #
tests/run.sh --changed-from REF(or--changed-files FILE, one path per line) adds to the fast tier the steps whosecoversglobs match a file changed since REF (git diff REF...HEAD) and those whose command names a changed file; a step with nocoversis not added by its globs.--planprints the selection and the shard plan and runs nothing, and a sharded run ends with ashard i/K: planned Ns, the steps took Ns togetherline.tests/durations.txtholds the recorded duration of every step, from a whole run, for--shard i/K --durations tests/durations.txt: the four parts of the suite plan within 1.3 times of each other (CONTRIBUTING, Tests, says how to regenerate it).tests/test_ci.pychecks the CI workflow and the shard balance.- The methodology parameter
ci_timeout_minutes(default 30; a positive whole number, a parameter may carry apattern) sets thetimeout-minutesof the CI job the gitflow scaffold writes, so a project sets it from its own suite budgets. qargi release statusshows, in the suite record's verdict, how long the run took and the mode its command names (-j N,shard I/K):pass at this head (command: C; ran AT; took 412s; -j 8).release verifyandfeature verifyrecord both; a record made before shows the command and the time only.- Catalog entries for common development tooling, found with
qargi discover searchand installed as role packages:pkg-golangci-lint,pkg-bats,pkg-podman,pkg-mandoc,pkg-golang(the Go toolchain and a C compiler, sogo test -raceruns) andpkg-gh(the GitHub command line, for its release commands). A test checks every entry of the shipped catalog against the schema of its kind. qargi project profile check [NAME] [--profile FILE](read only, so a member may run it) reads a repository's profile (~/.qargi/config/profiles/<repository>.md, the repository being the project's directory name, or FILE). It exits 1 while the profile still carries the shipped example's title or "Copy to" line, and warns (exit 0) of each backticked command in the profile's Gates section that is in none of the repository'sCLAUDE.md,AGENTS.mdand Makefile targets. No profile is fine.project validateprints the header finding as aWARN:line.roles.ROLE.tools: the tools a role needs beyond its apt packages, declared once in the project's answers as a list of{kind, name, ...}entries.go(a package path and a tagged version:go install NAME@VERSION),binary(a file name, version, the URL of the file and its sha256: downloaded to~/.local/binand kept only when the hash matches) andsubmodule(a name and the path of a submodule of the repository).project validatechecks each kind's keys and that two roles do not pin one tool differently.qargi env planlists every tool with its state here (ok, missing, wrong, path) and the token covers the list;qargi env applyinstalls the go tools (go must be in--approve-exec) and binaries that are missing or wrong, after the bootstrap, and records them;qargi env checkreports a tool that is not installed, is another version (go version -m) or another file (sha256), sits in a directory PATH lacks, or a declared submodule that is not initialised or at another commit.qargi local preparechecks the role's declared submodules out in the member's clone from the lead's copy of their objects, offline (a mirror with no remote underRUN/sub, push URL disabled) and printsSUBMODULES=path,...; a commit the lead's copy lacks, a submodule not initialised there, or a run made with--repo-url, is a note and nothing is fetched (lib/tools.py).- Providers as data, one runtime per surface. Claude ships as two runtimes of one family,
claude-code(local) andclaude-platform(cloud), with a shared model catalog. The family nameclauderesolves per surface (a notice says so) in a project'smodels.providerand inprovider of; everyproviderverb that takes a NAME takes a runtime name (there is no runtime namedclaude). Definitions followschema-2.json: tiers with the date and source each model was verified, effort levels, a capability matrix, alayout(skills, agents, settings, memory, transcripts, render target, launch line; a verb that needs a facility the runtime lacks saysprovider NAME has no FACILITY, exit 3), a renderer for the member file, a dispatch (in-session or a command), a barrier andnotes. models.providerper surface:qargi project set P models.provider.local|cloud NAME(a runtime that does not serve that surface is refused, exit 2), andproject new|attachtake--provider NAMEor--provider-localand--provider-cloud; nothing is guessed.project validate,init,attachandnewprintfinding: models.provider is not set(exit status unchanged); a verb that needs a surface the answers leave out exits 2 and says which.- Provider notes: a definition's
notes(up to 16 topics, one line each, plain text) say what the skills would otherwise have to say about the runtime.qargi provider notes [TOPIC] [--project P] [--surface cloud]prints one of the project's runtime (exit 1 for a topic it lacks, exit 2 for a project with no runtime or one that cannot be used as it stands).provider checkfails a bad note and warns when an in-session local runtime has nospawnnote; an extension cannot add notes.claude-codeshipsspawn,session-model,exploration,skills-dir,identityandcommands;claude-platformshipsoutput-dirandclone-dir. - Contract addenda: a definition lists
resources.contract_addenda, text files of its own directory (16 KB each, no control or format characters) that are added to a member's contract, so the neutral contract never names a surface.claude-platformcarriescloud-run.md(the detached-gate recipe, the no-wrapper git case,/workspace/<repo>,/mnt/session/outputs);claude-codecarrieslocal-run.md(background commands,Monitor, no foregroundsleep).qargi provider addenda NAME SURFACEprints them. A cloud agent's prompt is the contract, the output format and the addenda of the project's cloud runtime (the prompt hash covers them);local preparecopies the local runtime's into the run'sCONTRACTdirectory asruntime-addenda.md.instances refresh-promptstops withcould not render the system promptwhen an addendum cannot be read. - Provider packs in the library:
qargi provider new|import|seal|remove|enable|disable|attestfor provider, catalog and extension packs, each a plan, a token and your approval, with--approve-execnaming every executable. A pack holds text only (binaries, archives, links and secrets are refused). A pack counts only while it is what you sealed, and a capability counts only with averified.jsonrecord bound to the definition's hash, written byprovider check(hermetic cases) orprovider attest(asked each time; it never runs or spends anything). A name defined both shipped and in the library is refused. The conformance harness islib/conformance/(tests/conformance.shstill runs it). - Extensions: a project's
models.extensionscompose onto its runtime; two that provide the same thing, or one that collides with the runtime, are refused, and an extension's capability counts only afterprovider checkorattestof it passes.provider show|check|layout|require-skills|launch --project Pandprovider effective ... --project Pshow the composed view, andagent for,provider accept,sessionandprovisionread it. A member whose text depends on an extension showsSTALEuntilqargi agent refresh. qargi provider adapter-run NAME SURFACE VERB [--check]runs a verb of a runtime's adapter (a library adapter only as a sealed private copy; a verb the definition does not list exits 3), andqargi provider model NAME SURFACE TIERprints a tier's model.qargi local run RUN [--dry-run]runs a task through a runtime whose dispatch is a command (a vendor CLI): the argv comes from the definition, with no shell and a scrubbed environment (only the names indispatch.env_pass, shown in the review), and the output goes toOUT/transcript. It builds from the project's composed definition; a read-only run is refused when the barrier comes from an extension, or the runtime's own barrier is a hook or adapter, unless the project recordedprovider accept. A runtime that works inside the session is refused.qargi project link|unlink PROJECT [--resource skills,agents,memory]: one plan and token links the project's skills, agents and memory into the work tree, each as the runtime's definition says (a directory link, one link per entry, or a copy; the memory directory becomes a link into~/.qargi/memory/projects/PROJECT). A real directory in the way moves toNAME.qargi-backup.qargi packs link|unlink PROJECT: the project's skills directory in the repository becomes a link to the render ofpacks apply --target, so a session in the checkout loads the packs without--add-dir. The plan lists what a real directory there holds (same as the library, differs, or new); apply moves it toskills.qargi-backup,unlinkmoves it back (also when the link is already gone); nothing is deleted. A foreign link, a symlinked parent, files tracked by git and a missing render are refused.qargi packs check --targetends with the skills a session would load, eachqargi,accepted,drifted,excludedorunmanaged(onlydriftedchanges the exit status), says when the checkout is not linked, and reads the exclusion from the runtime's settings in every form.qargi skill accept|unaccept PROJECT NAME [--user]records a skill qargi does not manage, by name and content hash.project attachhints when the repository has its own skills.- Members are rendered from one neutral definition (
agents/member.json,member.body.md) through the runtime's renderer.agent listandagent modelsshowSTALE(definition, catalog, renderer or an enabled extension changed) andDRIFT(file edited by hand). - The render of a member is kept in the configuration directory,
~/.qargi/config/agents/PROJECT/NAME.EXT(besideagents.json, outside every repository), and the runtime's file is written from it:agent add,renameandrefreshwrite both,removedeletes both. A checkout that is cleaned or cloned afresh gets its members back withqargi agent refresh NAME --project P, which also hides the file from git again.agent listandagent modelsshowMISSING(the runtime's file is gone),STALE(the copy in config is gone or changed, or the inputs of the render did) orDRIFT(the file was edited by hand), each with that command.env checkreads the new form. - Every skill and tool pack declares
compatible_providers(runtime names, family names orany) as part of its content hash:skill|tool new|import --compatible LISTandseal --compatible. A project's runtime skips a pack it is not on, with a note ineffective,check,packs apply|check|linkanddiscover install [--compatible]. - The member guard takes its hook protocol and protected directories from the provider definitions (
qargi __guard --provider NAME). qargi help guardsays in one place what a local member's guard refuses and what it allows, andqargi help approvalssays that a token covers the plan seen, that--approvedis recorded and not verified, that gated verbs are never allow-listed and that a member never applies. The manual page has a GUARD section.qargi project validateprints aWARN:line for each pack linked to a role of the project that is not in the pack library.packs check --targetandskill|tool check PROJECT ROLEprintno pack is linked to any role of PROJECTwhen none is.qargi marshal handoff [NAME] [--write FILE] [--json]: a read-only note for a fresh session, built from stored facts only (repository, HEAD, base, host, features, the marshal record, the packs check and launch line, the lastenv checkandenv apply), with the lead-onlyqargi-sessionskill.qargi marshal claim NAME --as-successor-of ID: a lead session that was restarted continues as the lead, without the owner's takeover, when the record's lead is ID, its lease is stale and the host, OS user and work tree are the same. Anything else stays the owner'stakeover.feature startcontinues the same way, andmarshal status,marshal checkandmarshal handoffname the command.qargi lead usage [--project P] [--session ID | --transcript FILE] [--json]says how much of the lead session's context is in use.qargi lead compact-check [--advise] [--json]says whether a/compactis safe now (exit 0) or not (exit 1) with every reason (an open local run, a merge waiting in the conflicts directory, a whole-suite run in progress, a handoff note older than the last merge, an open release or hotfix branch) and prints what to re-read after it. The window is the project answerlead.context_window(default: the runtime'sresources.transcripts, whichclaude-codeships). qargi cannot run/compact.feature finish, thefeature mergethat closes a feature's last open run,release finishandmarshal handoffend withlead: N% context; compact is safe now: run /compact(or why not) at or above the answerlead.compact_advice_pct(default 40; 0 turns it off). They never fail or wait for it.qargi env show [--project P] [--repo DIR] [--json]prints what a repository declares about its development environment, read as text from files it already has: theAGENTS.mdorREADME.mdsection headed Development environment, Environment or Setup; version pins (go.mod,.tool-versions,mise.toml,.python-version,.nvmrc,package.jsonengines);.gitmodules; thebootstrapanddoctortargets of a Makefile, justfile, Taskfile or mise tasks; a devcontainer. It derives the doctor, bootstrap and dry-run commands. The project answersenv.doctor_command,env.bootstrap_command,env.dry_run_commandandenv.known_failuresoverride them. A member may run it. Seeqargi help environment.qargi env check [--project P] [--repo DIR] [--timeout SECONDS] [--json]says whether a checkout is set up the way its project declares and ends withENV OKorENV INCOMPLETE: N missing (...)(exit 0, 1, or 2 for usage). It runs the doctor command once (no shell, standard input closed, a minimal environment, a time limit of 300 s by default), so it runs the project's code. A doctor line that begins withmissing,fail,error,not ok,staleor[x], or ends withmissing,not foundornot installed, is a missing item; one that begins withexpectedor holds anenv.known_failuresentry is accepted. With no doctor command a built-in minimum runs (submodules, tags, pinned tools onPATH); for a stored project it adds whether skills, agents and memory are linked,STALEandDRIFTmembers,packs checkandmemory check. The verdict is recorded instate/env/PROJECT.json. A member may run it.qargi env plan [--project P] [--repo DIR] --approve-exec NAMEandqargi env apply --plan TOKEN --approved WORDS --approve-exec NAMES [--allow-root](the lead's) look at, then run, a project's bootstrap. The plan runs the dry-run command (which can run project code, so it exits 2 without--approve-execnaming its executable), shows its output with privileged lines (sudo,doas,pkexec,su,run0) apart, and prints a token. Apply runs the dry run again, refuses when the token or the approved executables differ or when privileged lines are present without--allow-root, then runs the bootstrap on your terminal and ends withenv check, so a bootstrap that leaves the environment incomplete exits 1. The member guard refuses both.qargi project attachprints adevelopment environment found:line and listsenvgaps (no Development environment section inAGENTS.mdorREADME.md, no bootstrap or doctor target).qargi project adoptwrites a missingAGENTS.mdand, when there is no task runner, a Makefile with stub targets; it never edits an existing file.qargi project new(the gitflow scaffold) writes the sameAGENTS.mdand Makefile stubs, and the scaffold'sCLAUDE.mdstarts with@AGENTS.md.qargi provision planprints the bootstrap command as a suggested setup script for the cloud environment.qargi help environmentstates the doctor line convention. This repository declares its own environment the same way (make -n bootstrap,make bootstrap,make doctor).- Release publishing: pushing a tag
X.Y.Zruns.github/workflows/release.yml, which creates the GitHub release with the version's changelog section as notes and the source archive and its SHA-256 as files (a tag not onmaster, or an existing release, is left alone; it can be run by hand for an earlier tag).qargi release publish TAGdoes the same throughgh(QARGI_GH) with a plan and token.release status|verifywarn about a tag with no release and a commit without the workflow.lib/changelog_section.sh VERSION [FILE [HEADING]]prints a section. - Release signing is optional and off unless the project answers hold
release.sign(key,namespace,files, optionalverify_command).qargi release sign VERSIONsigns the listed files withssh-keygen -Y signon your own terminal (the passphrase never passes through qargi, a log or an agent; no ssh agent is asked), runsverify_commandand records the result understate/release/. It runs only from the tag afterfinishand beforepublish, and is refused without a terminal, in a member run and underQARGI_NESTED.release publishattaches assets in the same call (--asset FILE, repeatable, andrelease.assets;--notes-file FILEorrelease.notes_filereplaces the changelog notes) and, withrelease.signset, refuses until a signature that still holds is recorded.release statussaysawaiting signatureorVERSION is ready for signing: qargi release sign VERSION. - Release answers that fit a project's own layout, all optional and checked by
project validate,initandset:release.version_file(defaultVERSION;nonefor a project with no version file),release.changelog.unreleasedandrelease.changelog.released(heading templates with{version}and{date}),release.man_pages,release.test_commandandrelease.preflight(a command,{version}replaced, run byrelease startandrelease pushbefore they change anything).project attachandadoptread the changelog heading and version file by them. - Detached long-lived branches: the answer
repo.detachedlists branches (a documentation site, say) that never meet develop, master, release, hotfix or feature branches. qargi refuses every merge between them by name and by history. Tasks are cut from such a branch as from any base, andqargi feature merge BRANCH agent/SLUGwith the detached branch as NAME merges a finished task into it (plan and token, local only). - Parking branch: the answer
repo.parking_branch(defaultscratch) names the branch the main checkout rests on while qargi moves develop; a refusal because develop is checked out suggestsgit -C DIR switch -c scratch develop.qargi feature park [--project P]refreshes it (a dirty tree, or a parking branch with commits develop lacks, is refused). Afterfeature finishandrelease finisha clean main checkout on the parking branch is fast-forwarded to develop. The parking branch is never pushed or merged. - Branch lifecycle answers:
branches.push_features(never,when-shared,always; defaultnever) decides whetherfeature start|merge|syncpush the feature branch, andbranches.delete_merged(local,local+remote; defaultlocal) decides whetherfeature finishalso deletes it on origin (the plan shows it, and a branch with commits the feature lacks is kept). - Worktree root: the answer
worktrees.root(default<state>/worktrees/PROJECT) is where qargi makes worktrees, atROOT/<branch>.qargi feature start NAME --worktreecreatesROOT/feature/NAME;feature merge|syncupdate a clean worktree of the feature, andfeature finishremoves it.feature list|statusflag other worktrees of the project with thegit worktree movethat fixes it. qargi branch prune [--project P] [--remote]: a plan and token for the mergedagent/*andfeature/*branches (with--remotealso on origin, as a lease on the tip the plan showed) and the worktrees qargi made for them;--plan T --approved WORDSdeletes them. A branch counts as merged only when it was merged with--no-ff; long-lived and detached branches, the parking branch, unmerged branches and branches checked out in a worktree qargi did not make are never touched, and each kept branch is listed with its reason. The member guard refuses it andfeature parkwith sentences of their own.lib/site.py, a documentation site generator (Python 3.11 or later, standard library only;python3 -I lib/site.py).build --release X.Y.Z --source DIR --out DIR [--pages DIR] [--url URL]writes a static site: a command reference generated fromlib/commands.py(one page per command group, plus the help topics, files and environment variables), a releases page fromCHANGELOG.md, the hand pages of--pages(Markdown withtitleanddescriptionfront matter, converted by a strict subset converter that refuses what it does not support with the line number),sitemap.xml(dates from the last commit, never the build time),robots.txt,404.htmland.nojekyll. Each page has a title, description, canonical address, social-preview tags and structured data that describes only what the page shows; the stylesheet follows the reader's light or dark setting and needs no JavaScript.checkvalidates the hand pages against the source (everyqargicommand and option they name exists inlib/commands.py, links and anchors resolve, the description is 120 to 155 characters),check-output DIRvalidates a built tree (well-formed HTML, unique ids, links, sitemap, no scripts, frames, forms or unsafe link schemes) anddiff OLD NEW --source-old DIR --source-new DIRlists the commands, verbs and options that changed and the hand pages that still name what is gone. The build refuses paths outside the source tree, symbolic links, an--outthat is not new or empty and a config orstatic/folder other users can write; it reads the source'slib/commands.pyin a boxed child process, so give it only a source you trust.qargi release site VERSIONregenerates the documentation site when a release ships: it builds the site of the tag VERSION withlib/site.py(the tag's tree is the source; the hand pages andsite.jsonare those of the detached site branch) in a temporary worktree of that branch and commits it there asSite: built from release VERSION(site.jsonrecords the release,_site/holds the built site), behind a plan token and an approval, touching no working tree. It never pushes and prints thegit pushcommand instead; a build error refuses with the generator's report. The answers aresite.enabled,site.branch(defaultgh-pageswhenrepo.detachedlists it) andsite.config(defaultsite/site.json, the hand pages inpages/beside it). Withsite.enabled,release finishends withnext: qargi release site VERSION(the newafter_releasestep of the gitflow methodology, checked bymethodology checkand shown bymethodology show) andrelease statussayssite: not built for VERSION,site: built at SHA, not pushedorsite: pushed. The member guard refuses it.qargi local list [--scratch DIR]lists the local runs the registry names, with role, slug, age, applied and merged.qargi local clean --applied --scratch DIRcleans every applied and merged run and keeps the rest (exit 1 when one was kept);local clean --stale DAYS --scratch DIR [--yes]lists finished runs older than DAYS and, with--yes, cleans each by the rules ofclean RUN.qargi provision plan|apply --forcepassesant apply --forcefor a resource changed on the server; the token covers the flag.- Four baseline packs, each
compatible_providers: ["any"]:review-findings,secrets-scan,docs-site-memberandqargi-contributing. Theqargi-providersskill ships with the others, andinstall.shrefuses a python3 older than 3.11. python3 lib/help.py man --keep-date FILEkeeps the.THdate ofman/qargi.1, so regenerating the page afterrelease startno longer undoes the date.- Tests:
tests/test_skill_budget.shholds every text a member or lead loads to an exact word ceiling (tests/skill-budget.txt; a ceiling raised above its value at the merge base needs a# raised:comment), forbids a 12-word sentence in two texts (tests/skill-dup-allowlist.txt), requires everyqargi X Yexample to name a real verb, and requires every reference file underskills/*/to start withLoad when ...and to be sent to ("loadFILE") by itsSKILL.md; a runtime'snotescount as one text.tests/test_neutral.shfails a vendor, model family or cloud CLI name inskills/,roles/,agents/,contract/,packs/skills/,provision/templates/*.mdandmethodologies/beyond the counts intests/vendor-words-allowlist.txt, which can only shrink.tests/test_tool_copy.shkeepsinstall.sh --copy'stool_dirs, the tests'TOOL_DIRS(onecopy_toolhelper intests/hermetic.sh) and the top-level directories in step.tests/test_valued_opts.shcovers every option parser. - Numbered changelog items, opt-in with the answer
release.changelog.numbered: true. Under the unreleased heading (release.changelog.unreleased, default## [Unreleased]) an item is a line that startsN.or- N.; the lines up to the next item belong to it.qargi changelog renumber [--project P] [--check]finds duplicate item numbers (two branches that each added an item 5), numbers the items again in file order from the first one's number and rewrites the(item N)cross-references (with a duplicated N, to the nearest item numbered N at or before the reference, else the nearest after it). It works on theCHANGELOG.mdof the work tree around the current directory and leaves the change uncommitted;--checkonly reports and exits 1 on a duplicate.feature syncwarns about duplicates after the merge, andfeature finishrefuses (exit 1, nothing changed) until they are renumbered, committed and verified. A member may run only--check;renumberwithout it is refused by the guard. qargi upstream status|review|syncfor a project that is a fork of an upstream repository and keeps its own changes, the overlay, on top of it. The answers name the remote (upstream.remote), the branch it follows (upstream.branch) and the local branch that carries the upstream line and nothing else (upstream.into, defaultupstream);upstream.overlay_check, optional, is a command array.statusfetches that one branch of that remote (no tags, the only verb that reaches the network) and reports how far the branch is behind and ahead with the newest upstream commits;review [--limit N]lists the upstream commits not yet in the branch with the files each touches and marks the ones that touch a file develop changed since it parted from the upstream line;syncfast-forwards the branch to the fetched tip from a plan and a token (--plan,--approved), creates it at the tip when it does not exist, journals the move, refuses a branch with commits of its own, pushes nothing and never merges into develop (the next step,feature start upstream-Xthenfeature merge, is printed); withupstream.overlay_checkit then runs the command on a trial merge of the branch into develop in a temporary worktree (exit 1 on a failure or a conflict; the fast-forward stands).project validatechecks theupstreamanswers. The guard refuses a memberupstream status(its fetch changes the remote-tracking refs of the lead's repository, whichlocal verifycompares) andupstream sync, andproject setof anyupstreamanswer;reviewstays allowed.tests/test_upstream.shruns the verbs against a local bare repository acting as upstream.qargi ref record --rewrite REF OLD NEW --why "TEXT": the lead rewrites anagent/*branch to a commit with the same tree (git rev-parse OLD^{tree}must equal NEW's) while REF is at OLD, and the move is journaled with the reason, solocal verifyandlocal cleanof a run prepared before it accept it and name it as a rewrite, andlocal cleanandlocal listcount the run's applied tip as merged when the commit it was rewritten to is. Refused to a member.- Local runs survive a reboot.
local preparewarns on stderr when--scratchis on a tmpfs (a reboot loses the run; use a directory on disk). A passinglocal verifycopies OUT, a run bundle beside it and the run's snapshots to<state>/runs/<run id>/;qargi review apply --from-saved IDapplies from that copy with the same checks as a normal apply (review check,local verify --saved ID, the registry's repository, slug and base, applied once), and the guard keeps a member out of<state>/runs/.local listandqargi reportshow a run whose directory is gone asdirectory gone, with its saved copy;local clean RUN --discarddrops the registry entry (and the saved copy) of such a run, andclean RUNdoes once its saved work is applied and merged. A clean marks the run cleaned in the registry and removes its saved copy.
Changed #
- The CI workflow
.github/workflows/test.yml(no scheduled run): a pull request into a branch other than master runs the fast tier and the steps its changed files cover, or the whole suite when it carries the labelfull-suite; a push to develop, a pull request into master (the release pull request) and a manual run run the whole suite in four shards, balanced bytests/durations.txt, and a last job,result, is red when any shard failed. A pull request replaces its own earlier run, a push to develop is never cancelled. Each job has a time limit computed from the manifest's budgets (35 minutes; the former 30 minutes for the whole suite in one job was less than the budgets add up to, and the job was cancelled), the packages jq and shellcheck come from a cache keyed by their list (.github/actions/tools), and every action is pinned to a full commit SHA. tests/run.shreads its steps from the manifesttests/suite.txt(tier, parallel-safe or serial, timeout, time budget, optionalcoversglobs) and runs them-j Nat a time (default: the CPUs, at most the cgroup quota;-j 1is the former serial run).--tier fastruns a fast tier of about a minute that covers every area,--only PATTERNselects steps,--shard i/Kdeals them to K parts by recorded duration or budget,--shuffle SEEDand--repeat Nlook for a test that fails now and then, and--enforce-budgetsfails a step over 1.5 times its budget (otherwise a warning). Each step has its own session,HOME, no standard input and a timeout, and fails when it leaves a process running; its output is printed as one block when it ends, and the summary lists the failed steps and the slowest ten before thetotal:line. The linter runs throughxargs -P. The runner fails when atests/test_*file is not in the manifest or a listed file does not exist;tests/test_gitflow_detached.sh, which no list ran, is now listed.tests/test_runner.shtests the runner.tests/run.shgives every step aTMPDIRof its own under the runner's directory, and compares the set of directories at the top level of the suite'sTMPDIRbefore and after each step: a step that removed one it did not make, or left a new one, is named with the directories in its block (WARN directories outside its own changed: removed ..., left ...) and in a summary section. It is a warning, never a failure: withTMPDIRunset that place is/tmp, which every process on the host shares.- Every command starts faster: what
bin/qargidoes before a verb runs (the dev-run banner, the arguments filled in from the current directory, the project's cloud binding) is one python process,lib/help.py preamble, instead of three, and the model list behind the completion of a model value is read from the runtime descriptors only then, not on every start.qargi role listtakes about half the time it did.tests/test_startup.pykeeps the count at one process and the time under the shrink-only ceiling oftests/startup-budget.txt. - The tests are hermetic and safe to run concurrently with each other and with another whole suite:
tests/hermetic.shandtests/hermetic.pybuild one environment (fresh HOME, TMPDIR, config and state under one directory, UTC, C.UTF-8, umask 022, git without the host's configuration, no bytecode, no colour), and a lint (tests/hermetic_lint.py, run bytests/test_hermetic.sh) fails on a test that does not source it, names a literal/tmp,/dev/shmor/runpath, kills by pattern, waits with a baresleep, removes a directory it did not make, binds a fixed port, keeps a lock outside its directory or compares a wall-clock difference with a constant. QARGI_WATCH_INTERVALsets the poll interval ofqargi watchin seconds (default 30), so a test of the watch runs in two seconds and not in a minute.roles.ROLE.gois gone: a Go tool is aroles.ROLE.toolsentry of kindgo, and the cloud provision takes the environment's go packages from there (NAME@VERSION).project validaterefuses the old key and says so.roles.ROLE.aptand.pipstay as they are: plain package lists of the cloud environment, with nothing pinned or hashed in them.- Breaking: nothing assumes Claude any more. Set a runtime per surface for every project (
qargi project set P models.provider.local claude-codeandmodels.provider.cloud claude-platform, ormodels.provider claudefor the family).agent,provision,local prepare,local run, session starts,project validate,packs apply|check|link|unlink,skill acceptandmarshal handofftake the runtime from the project and exit 2 withset one with qargi project set P models.provider NAMEwhen it is unset; a name that is not a project saysno such project;--targetmust be the runtime'slayout.render_target. - Breaking: members belong to a project.
~/.qargi/config/agents.jsonis{schema: 2, projects: {P: {NAME: record}}}and any other shape makes everyagentverb exit 2 without touching the file (qargi upgradewarns): move it aside and add the members again withqargi agent add NAME --project P.--useris gone fromagent add|refresh|renameand nothing is written to a runtime's user-level directory. Two projects can each have aworker.agent listshows PROJECT, NAME, ROLES, MODEL/EFFORT, FILE;agent remove NAME --project Pis new;rename,refreshandremovetake--project(else the project in effect, set byQARGI_PROJECTor the checkout) and act only on that project's member;agent modelsneeds a project and saysroles, notabilities, in--json. A member that an earlieragent refreshwrote into the checkout is not moved: remove it and add it again. - Breaking: the packs render lives in
~/.qargi/state/render/PROJECT/RUNTIME/(it wasstate/claude/PROJECT; nothing converts the old directory: render again withqargi packs apply --target RUNTIME PROJECT, thenqargi packs link PROJECTso a session in the checkout loads it, and remove the old directory by hand).lib/packs_render.pytakes the skills directory, MCP file and settings from the runtime's definition. The link engine behindpacks linkhandles a directory link, one link per entry, or a copy. - Breaking: library skill and tool packs need
compatible_providers:qargi skill seal NAME --compatible any(or a list);packs seeddeclares it on the baseline packs. Skill content hashes are stricter (symlinks, the executable bit and nested files count), so an accepted skill showsdriftedonce:qargi skill acceptit again. Members showSTALEuntilqargi agent refresh NAME, and cloud agents' prompts are stale:qargi instances refresh-prompt NAME --if-stale. - Breaking: a usage error exits 2, as the manual page says (an unknown option, a usage line, a missing NAME, RUN or project, options
that need each other); it was 1 in every shell verb and in
discover,export,importandmethodology. A refusal that depends on state or on an approval is still exit 1. A valued option given as the last word (qargi agent for build --surface,upgrade --branch,project new --dir) now ends with exit 2 andOPT needs a value. - Breaking: the marshal lease length is the project answer
marshal.lease_minutes(it wasmarshal.ttl; set the new key and drop the old one), a whole number of minutes from 1 to 1440, default 15.project setandproject validaterefuse any other value. - Breaking:
feature finishno longer deletes the feature branch on origin by default (it did whenever a tracking ref existed). Setbranches.delete_mergedtolocal+remotefor that. - Breaking:
models.extensionsandmodels.accept_no_barrierchange only throughprovider enable|disableandprovider accept.project setrefuses them with exit 2 (it was 1), andproject init|attach|newandqargi importrefuse a file that adds or changes an entry (dropping one is allowed; restore a project byproject init, thenprovider enable). - Breaking:
install.sh --uninstallremoves exactly the paths an install recorded ininstall.manifestbesideinstall.json(links, copied files, created directories; a directory that still holds a file you added is kept and said). With no manifest (an install made before this release) it removes nothing and says so, and--uninstall --forceremoves only the fixed paths the script writes. The directories come from the runtime definition's layout, and--runtime NAMEchooses the runtime (defaultclaude-code; an unknown one, or one with no skills directory, is refused before anything is written). The retired skills, old agent links and oldqargi/devskill links are no longer removed byinstall.sh: remove any left over by hand. - Breaking: an archive made by
exportcarries a mode for each file (0600, or0700for an executable file of the pack library).importrestores it, and a file that differs only in that bit is a change in the plan. An archive with another mode or none (any made by an earlier qargi) is refused: export it again. Local runs prepared by 0.2.0 are refused bylocal verify|clean|runandreview apply(the registry does not record their repository): finish them with 0.2.0 or remove them withqargi local clean RUN --discard. Memory stores on canon version 1 areunknowntocanon plan|apply: bring them to version 2 by hand. - The member contract is
contract/contract.mdwithcontract/output-format.md(the two shapes: patches, or a report and manifest for a read-only role), about half its length, every rule once: one commit rule (one logical change with its tests and docs, or docs and changelog in the next commit when one would span three top-level directories;review checkagrees); checks and gates merged (the profile's "always" gates, the repository's lint step and a secret scanner overBASE_SHA..HEAD); temporary directories underTMPonly; the identity configured in the clone; background commands through "your runtime's background facility", neverpgrep -f; "write outputs only to OUT" (RUN_DIR is no place a member is told about); and no memory-write rule but the task block's MEMORY packet (members propose notes in their report and the lead files them). The cloud preamble, the lead's contract seed and the role seeds say the same and no longer nameRUN_DIRor repeat what the addenda say. The cloud-only part of the contract is a resource of theclaude-platformdefinition, not appended by the core; a project with no cloud runtime gets no addendum. - A read-only role puts a suggested fix in its report as a fenced diff, never a patch file (
local verifyandreview checkrefuse patches of a read-only run; the manifest kindsuggestionis gone).CONFIRMEDandPLAUSIBLEmean the same in the review role and thereview-findingspack. The four role texts and the member text are shorter and state only what the contract does not (the review role's product is "a report in OUT, not patches"; the documentation-branch rule is in thedocs-site-memberpack). - The lead skills are lean and load their rarely used parts on demand:
qargi-flow(the marshal inmarshal.md; the models section moved toqargi-instances; never invent an approval; marshal records are data and a handoff carries no approval),qargi-task(cloud.mdfor a cloud instance,local-run.mdfor a command runtime;CONTRACTamong the task block fields; the private-repository line in the local flow),qargi-verify(one take-in sequence: verify every open run before applying any, apply and merge from the run's slug,local cleanonly after the merge is confirmed;git amor a split by file for a cloud collect or a project with no open feature; the reason code asked again when rework was needed),qargi-packs(apply.md,seed.md),qargi-providers(definition.md,verify.md),qargi-memory(curate.md;memory checkexits 1 on any FAIL),qargi-project(repository.md),qargi-instances(the two bases, profiles, tiers,agent for,instances init --location local --agent FILE),qargi-sessionandqargi-team. No skill names one runtime's tools, directories or settings: each points atqargi provider notes spawn|session-model|exploration|skills-dir|identity|commands, and the plan-token and guard rules are one pointer each toqargi help approvalsandqargi help guard. Each description says what the skill does and when to load it. ASKILL.mdmust send a session to each reference file ("loadFILE") and every file underskills/that is not.mdis budgeted.qargi-sessiontells the lead to runqargi env checkwhen the handoff note's environment check is older than HEAD or incomplete. - The
keep-a-changelogpack andCONTRIBUTING.mdstate the contract's commit rule; the pack says the member never dates the changelog or cuts a release (release startdoes). Theqargi-contributingpack names no tool of one runtime. The shippedshellcheck-gatepack has two more lines: a disable for a newly introduced shellcheck code names the version that introduced it, and a lint severity the project configures itself beats-S warning(packs seedoffers the update). qargi local preparegives the run its own copy of the contract and printsCONTRACT=in the first, eval-able block and in the TASK-BLOCK PATHS block; it records a relative--repoand--scratchas real paths andtmpdirin the registry. Its help lists every key it prints.qargi team briefandqargi collectread the hosted output and clone directories from the cloud runtime's notes (output-dir,clone-dir); a brief for a project with no cloud runtime is refused.collectfinds the project from the session's instance.provider checkis no longer on the member guard's read-only list (it records a result);provider modelandprovider notes|addendaare allowed to a member (they only print).provider checkruns adapters with a fixed environment.- The member guard reads the text of a quoted argument or heredoc body as data when every command of the line is
echo,cat,tee,greporgit commit|log|status|diff|show|add, so a commit message that mentions a refused verb is no longer refused. Any other program, a comment, a backslash, a substitution,$'..',${..}, an unclosed quote or an assignment keeps the plain split, andgit push,ghandsshin text are refused as before. Under the read-only class a Bash command may write OUT and the run's temporary directory, as the Write tool does, andgit tagwith no argument or with-l/--listlists tags. qargi project set P deployment VALUEsays only what was set for a methodology option.skill importprintsnote: the source's top-level .pack.json ...when the source holds a record of its own.- Help and manual wording:
agent,conceptsandrolesspeak of roles, not abilities;providerdescribes one runtime on one surface;local run,local prepareand the render warning name no vendor and say--target RUNTIME;local runlists its exit statuses 0 to 4 andreview checkits exit 1; a refusal of the read-only class says "under the read-only class"; the manual page's SEE ALSO lists every shipped skill (a copy withoutskills/names them generically).qargi help guardnames every verb the guard refuses, the protected files and directories (the pack library, credentials,install.json,state/render, the memory tiers, the state'sproviders/, the ref journal and the run registry, the marshal and feature directories, the runtimes' skills, agents and plugin directories, the lead's git configuration and session files), the rules forcd, the 5-second budget and thegit commit -F FILEadvice, within 330 words;tests/test_help.pyfails when a refused verb is in neither the topic nor a documented exemption, and compares the manual page's APPROVALS and GUARD sections with the topics. qargi feature finish NAMErefuses a feature that is behind develop when both sides changed the same files since their merge base (exit 1, nothing changed). It lists those files and says to runqargi feature sync NAMEfirst. A feature behind develop with no file in common is refused as before, without the list.qargi release status VERSIONshows, next to the full-suite verdict, the command that produced it and when it ran (from the suite record). Whenrelease.test_commandis set and is not that command, or the project'stest_commandchanged since the run, a line of its own says so, so the pass is not read as the release gate. A record made by another command is shown, not reported as "not run".qargi release start VERSIONlists, after it creates the branch, the features merged into develop since the previous tag (the--no-ffmerges whose message is "Merge branch 'feature/...'"), one per line with the short sha, or says there are none.local verifyandlocal cleanaccept qargi's own moves and name them (develop moved by qargi feature finish), so a run no longer has to be cleaned before the nextfeature finish. The ref journal also records the pushes, the deletions on origin and the tags offeature finish,release finish(and hotfix),release push,branch prune --remoteand the pushes ofbranches.push_features(lines withremote: "origin"and the origin URL); verify accepts a remote-tracking ref of origin that says what origin has (a fetch), a tag fetched from origin, the parking branch fast-forwarded to develop's tip (by hand too), and every journaled move after a passing verify as well as before (a journaled deletion failed there).
Removed #
- Schema 1 provider definitions (
lib/providers/claude.json,schema.json, theclaude/adapterrouter) and all schema-1 code.claudeis a family, not a runtime (unknown provider 'claude'from the verbs that take a runtime name).project templatereads no Claude file: roles carrymodel: nulluntil a runtime is set. qargi migrate(rolesandhome), the refusal that told you to run it, the import of archives made with the old role names, andlib/roles.py; the guard no longer protects~/.config/qargi.qargi canon import(--map docs-v0), theimportslist ofcanon/layout.jsonand the canon section that described it: move the notes of such a store into the layout by hand. Canon chains older than version 2 (canon plan|apply --default-role,provision/canon-seed/v1/,tests/canon_sim.py); setting up an empty store has its own step.qargi review prune [--yes](useqargi branch prune, which shows a plan and a token, also clears mergedfeature/*branches and the worktrees qargi made, and checks that a branch was merged with--no-ff;review prunedeleted anyagent/*branch that was an ancestor of develop or a feature).agent add --abilities(use--roles),local prepare --ability(use--role) and theABILITY=line oflocal prepare.- The one-per-role fallback:
agent for ROLEresolves only the project'sworkerorreviewer(exit 1 with theagent addcommand when the base is missing;--jsonhas nolegacy;agent modelssaysnot a base), on the cloud surface too (agent for ROLE --surface cloud --project Pprints the base or refuses: exit 1 unregistered, exit 2 overrides not verified). An instance of one role still works by name (qargi send <project>-build ...). - The judgement of a local run made before
RUN/classandRUN/startedexisted: noclasscounts as read-only, nostartedhas no ref-journal entry and no age (local listshows?,--stalenames it as skipped). qargi credential askpass FILE(internal, nothing called it) and the unreachable secondversion)branch ofbin/qargi.- From the role texts and the contract: the paragraphs that repeated the contract, the "Before DONE" tail, the build role's "Return numbered patches" line, the review role's enforcement note, the PDF-skill sentence and the memory-store bullets.
Fixed #
- The memory packet
local prepareprints keeps to its cap of 3000 characters also when it ends with the line that says rows were left out (it could exceed it by up to 33 characters, more or fewer depending on the paths in the rows). - The memory packet
local prepareprints keeps to its cap of 3000 characters also when it ends with the line that says rows were left out (it could exceed it by up to 33 characters, more or fewer depending on the paths in the rows). marshal status,marshal handoffandfeature statuslist an unmerged agent branch only under the feature its run was prepared against (local prepare --base feature/NAME, the base the registry of local runs records), not under every feature; the agent branches of no open feature are said once, on oneunattributed agent branchesline. The handoff note lists only the agent branches merged since the feature was cut from develop, not every merged branch in the history (the(and 51 more)tail).- Task lines tell the truth:
marshal status,marshal handoffandfeature statussaidnot verifiedof a run that had passedlocal verify, been applied, merged and cleaned. A passinglocal verifymarks the run's line in the registry of local runs verified andlocal cleanmarks it cleaned (the line stays, one per run, rewritten under the registry's lock); a task readsverified,appliedornot verifiedas the registry and the run directory say, and a cleaned run is listed only on acleaned:line, never as an open task.feature mergeof an agent branch whose run exists and has no passing verify warns. packs check --target T PROJECTno longer reportsmanifest missingfor a project that was never applied, which looked like a broken render. It printsnot applied: ... (qargi packs apply ...)and exits 0, and a render directory without its manifest still fails. The answerpacks.none: true(qargi project set P packs.none true;project setofpacksis refused to a member) says the project uses no project packs: the check passes with no render, and fails when a pack is linked to a role of the project. The section of the skills a session would load, the unmanaged ones in~/.claude/skillsincluded, is printed in every case.qargi project set P PATH falsestored the stringfalse; it stores the JSON value, liketrueand numbers.local clean --stale DAYS --scratch DIRreported 0 runs when the run directories were gone (a reboot that emptied a tmpfs): it now plans and, with--yes, drops the records of such runs older than DAYS, keeping the one whose saved copy holds work that was never applied; a scratch directory that is gone itself is still read from the registry (local list --scratchtoo).agentverbs check the records of the project they are about, not every project;agent renamekeeps a member file edited by hand and says so; a record that lacksrolesorfileis refused with one line naming the member.- The dispatcher finds the project of an instance
PROJECT-ROLEfor every role, not only the four built-in ones and lead. - The git authentication helper is a fixed script,
lib/askpass.sh, reading the file inQARGI_ASKPASS_FILE; it left aqargi-askpass.*file in the temporary directory on every call. qargi release:finishnames the repository without.gitand reaches GitHub only through$QARGI_GH;startrefuses a version whose tag exists; the pull request is titledPROJECT VERSION;publishshares the one exit handler.release startalways sets the date and version of every manual page (an empty.THdate is filled) andrelease finishrefuses a page that lost them, and, whenrelease.version_fileisnone, a changelog with no section for the version.qargi review check OUTreads the patches fromOUT/patches/*.patchonly (it also tookOUT/*.patch, whichreview applynever read), accepts a read-only run's report under anOUT_PREFIX, saysOUTwhere it saidRUN_DIR, and refuses an unknown option or a second directory with exit 2.review applytakes the patch files as a list (a space in a name is one patch) and finds the run by its real path. Theqargi-verifyskill and the README sayreview checktakes the output directory.qargi local clean: refuses a run whose commits were never applied or whose applied branch is merged into no branch butagent/*(--discarddrops them on purpose and says how many);--discardalso removes a run whose refs changed (verify's outcome is a warning). It removes the temporary directory the lead's registry names for the run (only a real path directly under/dev/shm,QARGI_RUN_TMPor the run, never through a link or a trailing slash; a run prepared before this has none, so its directory is left), and onlyRUN/tmpwhen the registry has no line.local list|cleancheck the second word ofappliedbefore giving it to git, andlocal listprints the role, slug and path without control bytes.local runtakesTMPDIRfrom the registry too.local verifyandlocal cleanno longer refuse the move thatfeature merge DETACHED BRANCHmade to a detached branch.qargi memory checkreports a half-initialised tier asINFO ... (qargi memory init NAME finishes it)instead of asking you to correct the path; an interrupted set-up of a store or tier can be finished by running it again.exportandimportcan move a project between hosts: a name in an archive may begin with a dot (so the library's.pack.jsonrecords pass;.and..do not), a memory note's name is checked with the same rule as every other name,exportchecks every name before it writes and stops, naming the file and how to go on (rename it, or--only), and says when it leaves out a symbolic link or a note whose name the reader would refuse. A script in a skill keeps its executable bit.- Importing a pack directory that carries qargi's own record follows one rule shared by import, review, content hash, render and
audit: the top-level
.pack.jsonis qargi's metadata and is ignored and not copied; a deeper one is skill content, so the review fails it and the hash counts it. install.sh --user --copycopied the tool withoutcanon/andcatalog/; it copies from one list (tool_dirs), andtests/test_install.shrunsqargi helpandqargi discover searchfrom the copy.- A cloud agent's prompt and
local preparestop with the reason when the project's runtime is set but does not resolve, instead of leaving the addenda out;instances refresh-promptrecords no hash for a prompt that failed to render, soinstances showsaysunknown. No project, a project not stored, or no runtime for the surface still means no addendum. packs check --target claude-codeno longer reports "shadowing" for every rendered skill once the checkout's skills directory is the link made bypacks link.packs link|unlinkwork through no-follow descriptors and say where your skills are when something fails; names in offers are quoted and control characters stripped; an unusual name isskipped: unusual name.- The member guard no longer refuses valid work: a file write because one provider definition in the library is broken (it skips it
with one note);
qargi provider model;tee OUT/report.md < r.md;$OUTand${OUT}in a write (they meanRUN/outunless the command could giveOUTa value);git commit -m push,git log -S push,git grep pushandgit branch push(it looks only at the git subcommand); a copy into the home directory (cp src/a.go ~/,rsync -a build/ ~/,grep -r TODO ~,find ~), which is no read of the signing key (copying or searching the credentials directory itself still is); anmvinto a directory that only lies above a protected place; a trimming expansion such as${f%%:*}; the script ofsed -i -e,sed -i -fandperl -pi -e;cd ../../out && echo "$(date)" > report.md; and a build member editing the repository's owndocs/tmpdir. A directory of the clone that holds a file named like the run's record is not taken for the run. Its tool names are the member's matcher's (MultiEditandNotebookReadare gone from the hook data and the shipped definition). - The member guard stays within the hook's time: a command it cannot check within 5 seconds is refused (a killed hook does not
block), and two quadratic regular expressions (a long run of blanks; many
marshal handofforproject setwords) are gone. A brace expression is expanded whole or the call is refused (more than 256 words for one word or 20000 for the command). - The guard's refusals to a read-only member say the report goes to
OUT(they saidRUN_DIR), and the refusal of a skill change says to propose it in the report. qargi env checkprintedpacks: no driftfor a project whose packs were never applied, sincepacks checkpasses then; it printspacks: not applied (qargi packs apply --target T PROJECT), a note and not a missing item.qargi env checkcounted aSTALE,DRIFTorMISSINGmember as current when the path of its file made theagent listline longer than 160 characters: the line was cut before its state was read.install.sh --project DIR --uninstall(and--user --uninstall) left an empty~/.qargi/configbehind. The install records the config directory and the parents it had to create, and the uninstall removes them once nothing else is left in them.
Security #
- A member's
qargi project setofroles.ROLE.tools(or of a whole role, or ofroles) is refused, asenv.*,release.*andtest_commandare: the list names whatenv applyinstalls and runs with the lead's rights and whatlocal preparechecks out. - The member guard refuses more of what runs a refused verb or
git push, and allows nothing the earlier guard refused:- It cuts a Bash command at every
;,&&,||,|,&and newline, inside quotes, comments and heredocs too (write a commit message to a file andgit commit -F FILE), reads a command substitution as a command of its own wherever it sits, a verb afterthen,do,!,(,{, a case pattern, a redirection or a function definition,git pushandghinsidesh -c,$'...'and$"..."quoting as the plain string, a subcommand or verb that a variable or substitution builds, and the command afterfind -execor inparallel's operands. It refusesgit send-pack,http-push,svn dcommit, an rsync operand of the formhost:path,git -cwith an alias or program key,--config-env,--exec-pathand theGIT_CONFIG_*,GIT_EXEC_PATHandGIT_SSH_COMMANDvariables. - A command with a NUL byte, over 262144 characters, nested more than 40 substitutions deep, with a glob that needs more than 4000 directory entries, with more than 24 directories at once, or taking over 5 seconds to check is refused, because a hook killed at its timeout does not block.
- Redirections before the program or standing alone,
>|,<>,>&FILE,git log|diff|show --output,format-patch -o,archive -o,bundle create,sort -o,uniq IN OUT,cp|mv|install|ln -t,dd of=,sed -i,tee,tar,unzip,curl,wget,patch,git apply --directory,git clone|init|worktree add DIR,awk -i inplace,ex|vi|vim|nvim|ed,rename,sqlite3,setfacl,mkfifo,find -delete|-exec rm,lnof a protected location, and the git subcommands that write objects or the index are writes to their target; the Edit and Write tools are held to the same. A target built by a glob, a variable or a substitution is refused when it could be a file of the run's record or hide it, as is a file name with a;,|,&or newline that leads into a protected location. - A
cdis followed only when it surely happens (a plain top-level command, or a chain of cds after&&); otherwise the commands after it are judged from every directory the shell may be in. After acdit cannot read (cd "$d",env -C,CDPATH) a relative write is refused until an absolutecd.git --work-tree=DIRandGIT_WORK_TREEcount like-C DIR.${NAME...}forms are read by the environment's value. - A line with
printf -v(andreadorlocal) is no longer read as text only, since it assigns a variable; a heredoc delimiter that starts with#is left to the plain split. - The run's record (
ability,class,profile,refs.before,refs.before.new,applied,repo.path,repo.url,cred.path,dispatch.json,task.md,tmpdir,started,verified,refs.after,refs.diff,git) cannot be written, moved, removed, copied, linked or truncated, nor the run directory itself used as a destination (cp -r Y/. ../..,tar -C ../.. -xf,unzip -d ../..);notes,out/,clone/andtmp/stay writable. The read-only class holds when its hook passes--class read-onlyeven ifabilityandrefs.beforewere moved away, and refusesgit bisect,symbolic-refwith a value,reflog expire|delete,hash-object -w,unpack-objects,git lfswrites, agit tagthat makes, replaces or deletes a tag (options read as git reads them, abbreviations included) and settingPATH,GIT_*,PAGER,BASH_ENV,RIPGREP_CONFIG_PATH,LD_*. - For every member: the lead's git configuration (
~/.gitconfig,~/.config/git,git config --global|--system|--fileoutside the clone,git initof the home directory), session settings and startup files (~/.claude/settings.json,settings.local.json,CLAUDE.md,commands/,hooks/,~/.claude.json,~/.bashrcand the other shell startup files,~/.ssh/config,authorized_keys), the run registry and its lock, the lead's run and agent verbs (local prepare|verify| clean,agent remove,project new,team run,task, also throughlocal.sh,agent.sh,team.sh),project set|init| link|unlinkwhenever its words cannot be read statically,provider checkandprovider adapter-run, and the link and render scripts of the tool itself. Every read of the signing key is refused, also through a leading or numbered redirection (<KEY cat,exec 3<KEY), andmv -Tinto a protected place is too.
- It cuts a Bash command at every
local verify|clean|run,local list,review applyandclean --applied|--staleact only on runs the lead's registry (state/local-runs.jsonl) names, and take the run's repository (repo, orurlandcred, the path of the credential file and never a token), slug, base and temporary directory from it, not from files in the run directory that a member can write. A repository a member chose ran its own.git/configin the lead's shell, and arepo.urlwith acred.pathsent any readable file as a token to a host of the member's choice. A run whose files differ from the registry is refused with the difference named; a run the registry does not name, or names with no repository, is refused too, andclean --discardstill removes it.local listreadsstartedas a number and prints?for anything else (the text was used in shell arithmetic).qargi local runwritesOUT/transcript,OUT/transcript.errandRUN/dispatch.jsononly as a new or a regular file with no second name, opened without following a link or waiting for a reader, and checksdispatch.jsonbefore it starts the process;OUTmust be a directory of the run. It readsRUN/task.md,profileandabilitythe same way, and a run with noabilityis refused with a message.qargi review applywrites the run'sappliedrecord andrefs.beforesnapshot in the lead's state directory and renames them into the run (mv -T), and anappliedthat is a link or not a plain file refuses the apply.- A runtime's adapter (
provider adapter-run, soagent for --surface cloudand the session verbs) ran with the caller's whole environment, forge tokens and ssh agent included. It gets the small environment a pack gets plusdispatch.env_pass, which also refusesHOME,PATH,LD_*,GH_*,SSH_*,JDK_JAVA_OPTIONS,FPATH,ZDOTDIR,KSH_ENV,LUA_*,LESSOPEN,GLIBC_TUNABLES,NPM_CONFIG_*,GOFLAGS,GOENV,INPUTRC,TERMINFOandSUDO_ASKPASS. A member's command also getsLC_ALLandTZ. - A library provider pack with no record (placed by hand) is not loaded, run, checked or attested: seal or import it. Skill and tool packs are held to the text-only policy of provider packs (no binaries, archives, links, special files or file over 1 MB; scripts stay allowed and listed); the review refuses control and bidi characters, also as JSON escapes, and prints everything escaped. The content hash of a skill pack also covers a link, a pipe, a socket, an empty directory and a file with a second name; a pack without any of them hashes as before, so no approval is lost.
- A runtime that claims
read_only_barrierwith barrier kindnoneand no read-only arguments failsprovider checkand import and reads as no barrier, as does a barrier hook whose refusal cannot be told from an allow (equal exit codes, or an empty stdout object); alocal runof a read-only member is refused for it unless the project ranprovider accept. qargi local preparerefuses a--repo-urlthat carries a user name or a password; the token goes in--credential-file.qargi project link(memory) compares real paths, so a link below the memory root that leads outside it is refused, and a project whose tiers resolve without a project tier is refused with a message, not a traceback.qargi project setofrelease.*,test_command,env.*orsite.*is refused to a member, asmodels.*is: the lead later runs those commands, or builds and commits the documentation site fromsite.*(qargi release site), so a member that set them would run its own command with the lead's rights.- The member guard refuses
git configthat writes a key which makes git run a program, read another configuration file or give a command or a remote another name, as it refused the one-shotgit -cform:git config core.fsmonitor ...,git config alias.x '!...',git config include.path ...,git config core.hooksPath ...andgit config alias.p push(thengit ppushed) were allowed since 0.2.0. Both forms share one list (core.fsmonitor, hooksPath, sshCommand, editor, pager, askPass, gitProxy, include.path,includeIf.*.path,alias.*, filter, diff, merge and tool drivers, credential, sequence.editor, gpg, uploadpack, receivepack,remote.*.url|pushurl|uploadpack|receivepack,url.*.insteadOf,submodule.*.update, protocol.ext.allow), read in any case, in every scope and file, after-C, in thegit config setsyntax;--edit, a renamed section and a key that a variable or xargs brings are refused too.user.name,user.emailand the other plain settings stay allowed, andGIT_CONFIG=FILEin front ofgit confignames the file it writes, so a protected one is refused. - On a line that also runs git, the member guard also refuses a write under
.git/modules/,.git/worktrees/or to aconfig.worktree(the configuration and hooks of a submodule or a worktree), as it did for.git/configand.git/hooks. - The member guard reads the program a shell takes from its standard input:
echo "qargi upgrade" | bash,bash <<< "qargi upgrade",sh -s <<< ...,. /dev/stdin <<< ...,echo ... | source /dev/stdin,echo ... | xargs -0 sh -c,printf '%s' ... | bashandecho ... > f; sh fran the verb unseen since 0.2.0. The text the line shows (a here-string, an echo or printf piped in or written to the file the shell runs) is now read assh -ctext is, and a program it cannot see (cat file | bash,curl ... | sh,bash < file, a process substitution, a filecpwrites in the same command) is refused: write the script with the file tool and runbash FILE. - The member guard finds the lead identity
QARGI_LEADamong the names a command sets the way it findsQARGI_NESTED:printf -vQARGI_LEAD x(the name glued to-v),mapfileandreadarrayinto it, andenv -uQARGI_LEADwere not refused.
0.2.0 #
Added #
qargi marshal forget NAME: drops this machine's accepted marshal mark of one feature, so a stale mark (left after the feature was deleted or re-created on the remote) stops reportingrollback detected. It needs integrity advisory or strict and a mark for NAME, prints the mark's revision and commit in a plan whose token covers both, applies with--planand your words (audited asmarshal:forget), touches no other feature and nothing on the remote, and is refused in a member run.qargi project adopt PROJECT: closes the safe gaps of an attached repository. With a plan token and your words it creates the missing long-lived branches locally (at the default branch tip; none is moved) and writes the methodology files that do not exist as one commit onagent/adopt-<methodology>cut from develop, for you to review. It never overwrites a file, never pushes, leaves the working tree alone, refuses a dirty tree, a detached HEAD and a member run, and lists the changelog format, tag format and CI content as left for you.- Signed marshal records. With the shared marshal, each lead machine can have its own ssh key (
marshal key init), a per-project signers file lists the trusted keys (marshal signers add|remove|list, never in the repository or the refs), andmarshal enable --integrity off|advisory|strictsets the mode. Records are signed withssh-keygen -Y(namespaceqargi-marshal) over a canonical payload that names project, feature, kind, revision, parent and lead, so a record cannot be altered or moved to another feature. Advisory tags records signed, unsigned or INVALID; strict ignores unsigned and invalid ones and falls back to the last accepted record. A local high-water mark detects a rewound ref (rollback detected), and junk or replayed pushes neither renew a lease nor block a feature.marshal resign NAMEsigns an existing record when moving from advisory to strict. Keys, signers and the mode are yours to set; nothing is signed until you choose a mode. Members cannot read the signing key or change the signers. Review hardening: a lagging machine builds on the newest signed record rather than overwriting it; advisory builds on an unsigned head instead of dropping it; a signed finish records the last features commit so a reused name keeps its rollback mark; records are identified by what the signature covers, so a re-committed head is the same record;resignshows the head, lead and goal in a plan whose token covers that head and refuses a lead other than the accepted one;disableneeds the plan token and your words while integrity is on; the guard followspushd/popd, refuses links and moves of the key, and normalises/./in scanned text. Member definitions now also hook the guard on the Read and Grep tools (to keep the key unread); members created earlier keep the old matcher until they are refreshed (qargi agent refresh NAME). - Shared marshal.
qargi marshal enable|disableswitch a project between the local records and a shared backend that keeps each feature record as a commit onrefs/qargi/features/NAMEof the remote origin: create-only claims, leased updates, an atomic move torefs/qargi/done/NAMEon finish, a fetch that never brings other refs, and takeover judged on the taker's own monotonic clock. It is off by default, needs the plan token and your typed words, is audited, cannot be used from a member run, andproject setrefusesmarshal.*exceptmarshal.ttl. Offline verbs use the cache and sayremote not checked; an offline claim isprovisional.local verifyignoresrefs/qargi/;marshal status|checktake--no-fetch(the session hook uses it). Hardening from review: an offline claim is dropped, not pushed, when another machine finished the feature meanwhile; remote records with wrong field types are ignored instead of crashing readers;takeoverof a provisional claim is refused; the lease renewer survives an unreachable remote (exit status 3,remote not checked) and stops only on a lost lease; the fetch leavesFETCH_HEADalone (git 2.29+);marshalignoresAPPROVEDandPLAN_ARGfrom the environment; andqargi feature start|finish|verifyare refused to a member. - Cloud profiles. A project can provision two cloud base agents,
<project>-worker(write class) and<project>-reviewer(read-only), and start a session of a role and profile on one:qargi start|send <project>-worker --role build --profile build. The model and effort are overridden per session with a full model object and the role's prompt is rendered fresh, so the base needs no prompt refresh; its tools stay fixed on the agent.agent for ROLE --surface cloudresolves the instance and uses a base only when it is registered and the provider's cloud override capabilities are verified (they now are forclaude, from a scratch run on 2026-11-01); otherwise the four role agents keep serving, and a profile with a different model than the role agent refuses. A reviewer base needs the project'sprovider acceptfor the missing cloud read-only barrier before it is provisioned or started. The claude adapter gained astart-sessionrequest builder, with a conformance check that a body never carries tools, skills or MCP servers. - Providers. Each model provider is a descriptor (
lib/providers/NAME.json, one schema) with its tier-to-model map and verification dates, effort levels, surfaces and a per-surface capability matrix;qargi provider list|show|checkprints and validates them, andchecksays what degrades (an unverified capability counts as unsupported). Theclaudeprovider has a local adapter (lib/providers/claude/adapter: resolve, dispatch-local, barrier-check) behind which members are rendered, and the Claude model ids and id pattern that were spread through the code now come from its descriptor. A surface with no read-only barrier cannot serve the review role unless the project recordsmodels.accept_no_barrierwithqargi provider accept(plan and token, audit-logged); forclaudethat means a cloud review role needs your acceptance, while the local guard counts as a partial barrier. A provider with no effort control has the effort dropped andagent modelssays so,agent forrefuses a profile on a provider that cannot override the model per dispatch, andlocal verifyandreview applynever apply patches from a read-only run.bash tests/conformance.sh NAMEruns a conformance suite (verb shapes, unsupported verbs, secrets out of argv, a barrier corpus, the read-only definition) against an adapter, with a deliberately broken adapter proving the suite catches faults. A member cannot runprovider accept. - Base members and profiles. Two base members, one per guard class:
worker(build, write, test) andreviewer(review, read-only);qargi agent add worker|reviewerdefaults to the base's own abilities and refuses abilities of the other class. Specializations are profiles {base, tier, effort, purpose}, resolved at dispatch: shippedbuild,write,test,review,auditor(Haiku),advisor(Opus) and the opt-inresearcher(frontier tier), plus your own underprofiles.NAMEin a project's answers (validated; a profile whose abilities do not fit its base's class is refused).qargi agent for ROLE [--profile NAME] [--json]now prints one line,MEMBER MODEL EFFORT PROFILE, and the lead passes the model and the effort to the Agent tool, which override the member definition.qargi local prepare --profile NAMEresolves the profile before any clone, records it inRUN/profile(a file the guard keeps from the member) and printsPROFILE,MEMBER,MODELandEFFORT. The tier-to-model table lives in the provider file (lib/providers/claude.json, with an alias per tier for local dispatch).qargi agent modelsflags legacy per-ability members, a missing base, refused profiles and mixed-class members. The earlier model-table entry is superseded:rolesandsuggestedbecameprofiles, andauditor,advisorandresearcherare profiles, not members. - Model selection. One table (
lib/model-defaults.json) names a tier and an effort for the lead and each role (Sonnet 5.5 for the lead, build, write and test, Opus 5.5 for review), andlib/providers/claude.jsonmaps the tiers (fast, balanced, deep, frontier) to models; a project chooses the provider withmodels.provider.qargi agent addtakes its model and effort from the project's answers, else the table, and writes the effort into the member definition (so doesagent refresh); the project template does the same, and its write role is now Sonnet rather than Opus.qargi agent models [--project P] [--json]shows the table, the project's overrides, the shipped profiles (auditoron Haiku,advisoron Opus,researcheron the opt-in frontier tier, Fable 5.1) and each member, marking an alias as unpinned and a frontier member as opt-in. The flow skill has an advisor sign-off beforefeature finishand says search is a read-only exploration on a small model, not a role. qargi marshal status --all(and--brief,--json, and the session-start hook that uses them) lists only the open features;--historyadds the finished and abandoned records after them.qargi marshal check NAMEfor a name that has no branch and no record now exits 1 withno feature NAME, and so do the flow verbs andlocal prepare --base feature/NAMEfor such a name. The error for a branch that is checked out elsewhere says how to free it (git -C DIR checkout --detach).- Memory tiers (writing, second slice). The session-start hook now lists the notes of the tiers, highest first, as data after the
marshal block (at most 4000 characters per note and 16000 in all; a tier that is missing or unreadable is skipped and named).
qargi memory init TIERcreates a tier from the standard layout andqargi memory move PATH --from TIER --to TIERmoves a note, or a folder of notes, between tiers; both print a plan and a token and apply only with your approval, and nothing is synced or deleted without it. A repo tier with notes that are not committed warns with the count.local prepareprints a TASK-BLOCK MEMORY packet for the member, and the guard refuses a member every verb and script that writes a tier. The memory, task and canon documents describe the tiers. - Baseline packs. qargi ships three packs under
packs/:shellcheck-gate(lint every shell file you touch and quote the verdict),keep-a-changelog(one Unreleased entry per user-visible change, in the same commit) andgitflow-member(a member's part of a git-flow project).qargi packs seedstores them in your library: it prints one line per pack (add, update, current or kept) and a plan token, and applies with your approval; an untouched shipped copy is updated when qargi ships newer text, a copy you edited and sealed or a pack of your own with the same name is kept and never overwritten, and seeding never links a pack.install.sh(and soqargi upgrade) adds the packs your library lacks withpacks seed --install, which needs no token and never updates. Link a pack to a project withqargi skill link; proposing links when a project is created or attached is not built yet. A member cannot runpacks seed. - Memory tiers (read-only, first slice). A project's memory is five ranked tiers, highest first: repo, cloud, project, shared,
system. A tier is a memory store of the standard layout in a directory (defaults under the qargi root;
memory.tiersin the project answers moves any of them) or, for cloud, the project's memory store.qargi memory tiers [--json]lists them with their state and note counts;qargi memory checkruns the canon check on each tier that exists and fails when a note path is present in two tiers (README, INDEX and the like repeat per tier by design). The reference store is not a tier. Nothing is written to a tier; creating and moving notes between tiers, and the session hook composing them, come later. - Agents write verified facts only under their own role folder of the memory store;
shared/is curated and reached by promotion (qargi canon promote --approved) or by the person, and an agent proposes a shared note in its report. The cloud preamble, the store README, the lead contract, the build role and skill and the canon say so.canon auditstill reports only cross-role writes, so an agent writing undershared/is not yet detected. The cloud prompts change: refresh them. - A lead-only ref journal (
ref-journal.jsonlin the state directory) records every local branch that qargi's own verbs create, move or delete (feature start|merge|sync|finish,release,review apply|prune).local verifynow accepts a branch change since the run was prepared when the journal leads from the snapshot to the branch's current value, so several runs prepared in parallel all verify after the lead merged the first one. Remote refs, tags and any unjournaled move are still refused, and the refusal says so. The guard keeps members from writing the journal; it is trusted only as far as the state directory is (signed records come later).local preparerecordsRUN/started. qargi marshalkeeps one record per open feature: its lead (a Claude Code session, identified by the session id the SessionStart hook exports asQARGI_LEAD, or--as), a lease, status, goal, plan and note.claim,status [--all] [--brief|--json],set,check,renew,release --note,take,takeover --approvedandfinish. Tasks, agent branches, worktrees and files that two features both change are derived from git and the local runs, not declared. Query it before starting work:qargi marshal check NAMErefuses when another live lead holds the feature and warns about a stale lead, an anonymous identity and overlap. The lease ismarshal.ttlminutes per project (default 15;qargi project set PROJECT marshal.ttl N), renewed by the lead's actions and for the whole run offeature verifyandfeature finish, so a suite of any length is safe. A feature whose lead stopped is taken over only after the lease ran out and with your words, which are audited. Records are local to the host unless the project switches to the shared backend (marshal enable).feature start|merge|sync|verify|finishandlocal prepare --base feature/Xask the marshal first and refuse when another live lead holds the feature;feature startrecords the starter as lead,finishmarks the record finished,feature statusends with the marshal's view andfeature listshows each lead. A marshal that is absent or fails never blocks.local-runs.jsonllines gainlead.- The session-start hook lists the open features as data for the lead session. The guard keeps members from the mutating
marshal verbs, from the state
features/directory and from settingQARGI_LEAD. qargi release starthas a preflight. Before it creates the branch it checks what a release must update and refuses, changing nothing, with a message naming the file and the fix: a man page (man/*.1) needs a.THline with a date and a version field, and the first commit now sets them to the release's date andVERSION; aCHANGELOG.mdneeds a## [Unreleased]section with content (--allow-empty-noteswaives that). Projects without a man page or changelog are not checked for that part.- Roles. A role is a directory with
role.json(its class,writeorread-only, and a description) andROLE.md(its instructions).build,write,reviewandtestship inroles/; add your own under~/.qargi/config/roles/NAME/(one lower-case token, neverlead, never a built-in's name).qargi role list|show|prompt|check. A local task names its role (qargi local prepare --role R, which records the class inRUN/class;--abilitystill works), the task block carries the role's text,qargi agent add --abilitiesandqargi agent fortake any role (a role of yours without a member falls back to a member of the built-in role of the same class), and a cloud agent of a role gets its text in its system prompt. The class, not the namereview, decides what the guard allows and which cloud tools are off. qargi local clean RUNremoves a collected, verified local run (clone, bare copy, output) and its private temporary directory.qargi local preparemakes that directory (TMP, on/dev/shmwhen it exists,QARGI_RUN_TMPmoves it) for the agent's test sandboxes, so parallel runs never share files and a run leaves nothing behind. The agent contract asks for targeted checks per task (the full suite belongs to the coordinator after a collection), and the task skill describes dispatching non-overlapping tasks in parallel.- Methodologies. A methodology is the way a project is developed and released, as data: branches (long-lived, release
feature/<version>, taskagent/<slug>), versioning and changelog, test tiers (targeted per task, the whole suite once per collection), CI, release steps, deployment options with instructions, and the generic files a new project starts with (README, VERSION, CHANGELOG, CONTRIBUTING, CLAUDE.md, .gitignore, a GitHub Actions test workflow).gitflowships inmethodologies/; add your own under~/.qargi/config/methodologies/NAME/.qargi methodology list|show|check|files|deploymentonly read; the scaffold carries no reference to qargi. qargi project attach [DIR]attaches qargi to an existing repository without changing it. It reads the remote, branches, tags, version and changelog files, test command and CI, prints the answers it would store, the gaps against the methodology and what it could not determine, and a plan token; with--planand--approvedit stores the answers outside the repository. The safe gaps are closed byqargi project adopt, the rest by hand.qargi project new NAMEstarts a project from a methodology: it prints a plan (directory, branches, files, deployment options with instructions) and a token; with--planand--approvedit creates the directory, runsgit init, writes the generic files in a first commit and creates the long-lived branches, leaving you ondevelop. With--urlthe qargi answers are stored too. Nothing is pushed and no remote is created.--deployment IDrecords a deployment option and prints how to do it. The generated.gitignoreexcludes.claude/worktrees/and.claude/settings.local.json.- Git-flow, as its author described it.
qargi feature start|list|sync|merge|status|verify|finish: a feature is a broad, related branch cut fromdevelop; verified task branches are merged into it with--no-ff; the whole suite runs once when it is complete, and only then is it merged intodevelop(one merge per feature). Several features can be open at once;feature listshows their purposes and the files two of them both change.qargi release start|status|verify|push|finish VERSION [--hotfix]:release/<version>fromdevelop(a hotfix frommaster), the version bump as its first commit, a pull request intomasterso CI runs on the commit that will be released, then merge intomasterwith--no-ff, an annotated tag, merge intodevelop(a hotfix: into the open release branch), delete, push. Merges are made in temporary worktrees and a branch moves only after every merge succeeded; a conflict changes nothing, keeps the merge in progress in a worktree it names (exit 3), and--resolvedapplies the merge you resolved. Merging and pushing need a plan token and an approval; a release starts only from adevelopwhose head passed the whole suite. A finished feature's whole-suite run carries over todevelop's new head, and a feature must containdevelopto finish, so a release needs no extra run. A release or hotfix finishes only on the commit that is on origin with passing checks, and pushes master, develop and the tag together or not at all. - The
feature finishandrelease finishplans list the commits and files being merged, so the approval is informed. - The
qargi-flowskill: the lead's git-flow process with the qargi commands (features and tasks, the one whole-suite run per feature, releases, hotfixes, resolving conflicts). A session that leads is pointed at it when it starts. qargi review apply RUN_DIRputs a collected run on a task branch of the project's repository: it needsreview checkandlocal verifyto pass, createsagent/<slug>(or--branch) at the commit the base had when the run was prepared and applies the patches withgit amin a temporary worktree, so your checkout is never touched. A patch that does not apply removes the branch again; a run is applied once.qargi review prune [--yes]lists, or deletes, theagent/*branches already merged intodevelopor afeature/*branch.qargi packs apply --target claude-code PROJECTrenders the skills and tools a project has enabled into a managed state directory,$QARGI_STATE/claude/PROJECT, replacing the previous render atomically, and prints the exactclaude --add-dir ...line that loads it. It needs no--asked; without--approvedit prints a plan token. Skills render under.claude/skills, tool packs asmcp.jsonandsettings.json, with a manifest. Claude Code cannot enforce role scoping, so the render is the union of the project's roles and role scoping is advisory there. Nothing is written into a repository or into~/.claude.qargi packs check --target claude-code PROJECT(read only) exits 1 on drift: a changed or unreviewed pack, a tampered, extra or missing rendered file, an enablement change, a rendered skill shadowed by~/.claude/skillsor the checkout's.claude/skills, or a missing manifest.--target managed-agentsis the existing cloud update of an instance.qargi packs apply --target claude-coderefuses at plan time, naming the pack and writing nothing: aSKILL.mdwhose name differs from the pack name or whose front matter is not closed or has twonamekeys, a skill and a tool with the same name, and a tool allowlist name outside letters, digits,_,.and-. Each plan stages into its own hidden directory under a lock, so concurrent applies serialize and the second refuses if the render changed meanwhile.qargi packs check --target claude-codealso reports a rendered file that differs from the library copy, a changed file mode, a group- or world-writable render, a malformed manifest, and a manifest naming another project or with role scoping other than advisory.qargi local prepareprints a WARNING block to stderr when the project's Claude Code render has drifted (the project is found by matching the repository against the stored answers), with the findings and the fix,qargi packs apply --target claude-code PROJECT. The run goes on; stdout and the TASK-BLOCK PATHS block are unchanged.- The guard protects that directory: members can no longer write under the state's
claude/directory, and the operands ofpacks renderare judged as write destinations. - A GitHub Actions workflow runs the whole test suite on pull requests and on pushes to
develop. qargi packs load [PACK] --role R [--project P]: read-only. Without a pack it lists what the role has enabled, with each description; with one it prints its approved text and the library path of its other files. It reads the links when called, so linking, unlinking, enabling and disabling take effect at once, and it refuses a pack that fails its review or changed since it was approved. Members may run it.qargi upgrade [--branch B | --tag T]: plan, then apply with--plan TOKEN --approved "...": moves the installed tool (the root-owned checkoutinstall.shrecorded) to a newer branch (fast-forward only on its current branch) or tag of its origin, re-runsinstall.sh --userand lists the members to refresh. The writing git commands run throughsudo; members cannot use the verb.qargi cloud list|show|set|unset|check|login: bind a project to anantprofile and the workspace it must reach (the project's optionalcloudanswer). Every command about that project then runsantunder that profile, and a cloud call is refused if the profile no longer reaches the workspace; a conflictingANTHROPIC_PROFILEis refused. Projects without a binding are unchanged. qargi never reads or writes a token:loginrunsant auth loginon your own terminal, and members cannot useset,unsetorlogin; the guard also protects~/.config/anthropic.qargi mark SID applied|rejected --why CODErecords a reason (codes inlib/reasons.json);qargi reportcounts them.marknow refuses a session the tracker does not know.qargi noticed list|resolve: collecting a session records thenoticed_not_acted_onnotes of its manifest (short, secret-looking strings redacted, once each) in the append-onlyevents.jsonlbeside the audit log; each stays open until given a disposition (followup, dismissed with--why, accepted, superseded);qargi reportshows how many are open and how old the oldest is, never the text.qargi agent refresh NAME: re-render a member from the member definition qargi ships now, keeping its name, abilities and model, so members created before a change (such as the hardened hook) can catch up without being removed and re-added.qargi memory split|merge|copy FROM TO: move memory between two stores, each a directory of notes, a.jsonldump or amemstore_id. Nothing is deleted: what a split archives goes to/_archive/<date>/and a collision to/_conflicts/<date>/, with the origin of every note written added to itsSources:line, so each change can be undone (merging a split's target restores the archived originals byte for byte). Without--approvedthey print the plan and its token and change nothing; a store whose notes changed since the plan is refused and left as it was. On a directory store qargi's own writers are serialised by an exclusive lock, and each write re-reads the note immediately before replacing it or releasing its name, so a change made by an editor in that instant is refused and kept, not overwritten; an editor that writes in the last instant after that read is still not prevented (the lock is advisory), which is why the digest check exists.lib/membackend.pyis the backend interface (directory, fixture dump and Managed Agents store throughant) with one ops executor,lib/memplan.pythe pure planning;lib/canon.pygainsmake_plan, the migration plancanon planprints, for reuse. A.jsonldump is read-only (a source, never a target of split, merge or copy). A note named with a leading dot or trailing white space is not a note, and a note the target cannot hold (too many path segments or too large for a Managed Agents store) makes the plan refuse and name it. The plan token is always required:QARGI_NESTEDdoes not stand in for it, and the audit log records the token, the selector and every step, with a second line once the apply succeeded.mergeandcopywithout a selector leave the source's/_archive/behind. A provenance segment counts only when its digest matches the note it was added to./_archive/is a tolerated folder of canon 2 (canon checkwarns about a note there without our segment). Known limit:canon applyis not run through the new executor;memplan.migrateexists for it.qargi report [--since DATE] [--instance NAME] [--stale-after D] [--local-stale-after D] [--runs-root DIR] [--json]: a read-only report from the session tracker (spend and spend per applied session, sessions pending review oldest first, sessions that look stale, idle sessions by why they stopped, time from creation to close) and from local run directories (finished DONE or BLOCKED, no manifest and still changing, no manifest and quiet: possibly stalled). It states what it cannot tell and never prints a session's note or request.qargi local prepareappends one JSON line per run (run directory, slug, ability, base, start time) tolocal-runs.jsonlin the state directory, soreportcan list local runs.instances refresh-prompt --if-stale: does nothing, with no cloud call, when the prompt rendered now has the hash recorded at the last provision or refresh. The registry entry of a cloud instance gets apromptobject (sha,skill,at);instances listmarks a changed promptprompt STALE, andinstances showsays which part changed. An entry without a hash (older release, orinstances init) is unknown, not stale.qargi supervise tick|status|nudge|delivered|hooks: the first version of the stalled-agent supervisor. Hooks only record; one idempotent tick (the only writer ofruns.json, under a lock) folds the per-run event files, the local-run registry and the session tracker, classifies every unfinished run in a fixed order (waiting on an approval, API stop failure, interim with a cap, looping on three identical tool digests, suspected dead on stale activity; cloud:requires_action,budget_reached, stale while running, idle awaiting review) and acts only by printingQARGI SUPERVISE <class> <run> <next-action>lines, appending tosupervisor.logand the audit log (originsupervisor), and preparing a nudge innudges.jsonl. A nudge is a fixed template, at most 2 per run with at least 10 minutes between them, never while the run waits or has little budget left, and never an approval; a local one is delivered by the coordinating session and counts onceqargi supervise delivered KEYrecords it, a cloud one is sent only byqargi supervise nudge SID --asked .... Nothing is sent automatically (a standing permission is a documented extension point, off). Thresholds are data inlib/supervise-defaults.json: conservative floors untilqargi reportshows real run lengths.statuswarns when the last tick is older than twice the interval.qargi hook subagent-start|subagent-stop|tool-use|tool-failure|notification|stop-failure|pre-compact: handlers for the Claude Code hooks that append one small line (ids, times, the tool name and a hash of its input; never a command, message or error text) torun-events/<agent_id>.jsonlin the state directory. They exit 0 at once, even on malformed input.qargi supervise hooksprints the settings snippet; it never writessettings.json.qargi reporthas a short Supervisor section (suspected stalls by class, what waits on you, prepared nudges awaiting action) once a tick has run.- The audit log may carry an
originfield (supervisor); the tracker row recordslast_processed_at(the session'supdated_at) on refresh. - Sessions record
interrupt_sent_atwhenqargi stopsends the interrupt (an interrupted turn also ends withend_turn) andstop_event_idsforrequires_action. QARGI_HOMEreplaces the root~/.qargithat holdsconfig/andstate/;QARGI_CONFandQARGI_STATEstill win over it.qargi migrate home: copies the data from the earlier XDG directories to~/.qargi/configand~/.qargi/state. Without--approvedit prints the plan (every directory, file and link with mode and digest, the*.lockfiles it skips, the files in which it rewrites old absolute paths: a project's credential file,local-runs.jsonl, anything else naming the old directories; the recordsaudit.log,supervisor.logandrun-events/are copied byte for byte and the plan lists them on a "Copied verbatim (records)" line) and a plan token covering it, and changes nothing. With--plan TOKEN --approved "<words>"it copies (the old directories stay as a backup), keeps modes (credentials 0700, files 0600), checks every file by digest, removes what it made if a step fails, refuses when the new root exists and is not empty, and logs anaudit.logline. Run again it says there is nothing to do.- Dev runs:
install.sh --userrecords the tool it installed in<config>/install.json(--uninstallremoves it;--projectwrites nothing). A checkout run from any other place, with none ofQARGI_HOME,QARGI_CONFandQARGI_STATEset, keeps its data in<checkout>/.qargi(git-ignored), prints oneqargi: dev run from ...line on stderr per command (none forhelp,version, completion, the guard and the run-event hooks), and never touches live data; with noinstall.jsona checkout behaves as the installed one.qargi migrate homerefuses in a dev run unless a location is named. Seeqargi help dev. lib/paths.pyand the top oflib/lib.share the one place each language resolves these locations; the nine copies of the XDG logic are gone.
Changed #
- The plan token of
packs apply --target claude-codealso covers file modes, the manifest records them, and the swap installs only the tree the token approved. qargi local verifyandqargi local cleanaccept the lead's own work. After a passing verify the run recordsRUN/verified, and later checks compare with it: fast-forwards ofdevelop,masterandagent|feature|release|hotfix/*branches and new branches there are accepted, while remote refs, tags, deletions and rewrites still fail. A change now exits 2 (it was 1). Runqargi local verifyon every run before merging any of them.qargi local prepareends its output with a blank line and aTASK-BLOCK PATHSblock (RUN_DIR,CLONE,OUT,TMP,GIT,BASE_SHA); a caller that evals the output must stop at the blank line.qargi project attachreads the tree of the integration branch (develop, else the default branch, elseHEAD) and says which, not the working tree. It prints origin as read and the normalised url. When answers are already stored it shows stored against detected and the fields the stored file lacks, stays read-only and gives no plan token; with--forcethe plan keeps what it cannot detect (the roles' models and effort, cloud, memory, budget).qargi feature startrefuses a name origin already has, asked live withgit ls-remote.qargi feature status|finishandqargi release status|push|finishask the live origin withgit ls-remotewhere the branches are (QARGI_REMOTE_TIMEOUTseconds, default 10) instead of the last fetch, and printremote not checkedwhen origin cannot be reached.tests/run.shruns without the caller's cloud binding or anyQARGI_*orANTHROPIC_*variable and with a freshHOME, and every test is hermetic (tests/hermetic.sh,tests/hermetic.py): fresh qargi config and state, and failingantandclaudestubs first onPATH.- The role and contract text lists the rules to follow before DONE. Every cloud agent's prompt changes: run
qargi instances refresh-prompt NAME --if-stalefor each. qargi feature verifyandqargi release verifyrun the project's test command without the project's cloud binding (ANTHROPIC_PROFILE,QARGI_CLOUD_WORKSPACE,QARGI_CLOUD_PROJECT), andtests/run.shunsets them, so a project with a cloud binding can get a passing whole-suite record.- The cloud agent contract, the member definition and the task skill speak of a role and its text instead of loading an
ability skill. Every cloud agent's prompt changes: run
qargi instances refresh-prompt NAME --if-stalefor each. The - Breaking before 1.0: the default locations are
~/.qargi/configand~/.qargi/state, not$XDG_CONFIG_HOME/qargiand$XDG_STATE_HOME/qargi; the XDG variables no longer move qargi's data. While only the earlier directories exist, every command excepthelp,version, completion,__guard, the run-event hooks andmigrate homestops and asks you to runqargi migrate home; qargi never creates an empty~/.qarginext to old data, andinstall.sh --userdoes not record the install until then. - The member guard protects the pack library,
<config>/credentials/and<config>/install.jsonat the new location (the resolved configuration directory,$QARGI_HOME/config, the live one and~/.qargi/config) and keeps the earlier XDG paths protected as a backup. The rest of the configuration directory stays writable (the guard decides by path, not by qargi verb), and reading a protected path is still allowed. - The member guard refuses a member every skill-modifying verb:
qargi skill|tool new|import|seal|remove|unlink|link|enable|disable,qargi discover install,qargi packs applyandqargi import(found after wrappers, insh -c,evaland$(...), and whenpacks.shordiscover.pyis run directly), settingQARGI_NESTED, and writes under the pack library ($QARGI_CONF/packs, honouring the XDG defaults),~/.claude/skills,~/.claude/agents,~/.claude/pluginsand any.claude/skillsdirectory outside the run's clone (inside the clone it is the work product, reviewed as a patch; paths are resolved first, so a link in the clone to a protected location is still refused). Edit, Write and NotebookEdit are decided by the resolved path; Bash by a documented conservative heuristic (redirections, tee, cp, mv, rm, ln, install, rsync,sed -i,dd of=, find, tar and so on). The repository's ownskills/directory in a member's clone and its run directories stay writable. Read-only verbs (list,show,check,effective,search,render) are unchanged. A barrier, not proof; code that writes a file itself (python -c) is not covered. - The guard fails closed: an unexpected exception inside it is exit 2 with the reason on stderr, not a silent pass.
agents/member.mdsetsonFailure: "block"and atimeouton the hook, so a hook that cannot start, crashes, times out or exits other than 0 or 2 blocks the call (needs Claude Code 2.1.295 or later; an older one runs the call, per the hooks documentation). Frontmatter hooks of a project-level agent file run only once the workspace is trusted. QARGI_NESTEDno longer stands in for the plan token ofskill|tool new|import|seal|remove|link|unlink|enable|disableandpacks apply;import,sealandpacks applyalways need--plan, and an empty expected token is an error. Nothing nested them (discover installalready cleared it, provisioning only renders).--approvedremains recorded text: proof that a person approved is still the owner's chat approval.
Removed #
- The four ability skills
qargi-build,qargi-write,qargi-reviewandqargi-testare gone: nothing loaded them. A member follows the role text that comes with the task block (roles/<role>/ROLE.md), andqargi help roles,qargi help agentand the manual page no longer name them.install.sh --userremoves the leftover links to them.
Fixed #
qargi canon auditnow attributes a session of a cloud base (<project>-worker,<project>-reviewer) to the role recorded in the local session tracker, so its cross-role writes are reported; a session it cannot attribute is counted once in a NOTE line, not guessed.qargi cloud show|check|login|unsetnow refuse--profileand--workspace-id(onlysettakes them) instead of ignoring them;qargi agent for --surfacecompleteslocalandcloud; the test that keeps the help in step with the scripts now also coverslib/provider.sh.- Read-only cloud roles now have the
writebuilt-in tool off as well asedit(a reviewer could still create files throughwrite); an unknown class now counts as read-only instead of write, andworkerandreviewerare reserved role names. qargi provision applyno longer fails on the second and later roles of a project: an empty package list became[""], which the shared environment update rejected, before the stores were attached and the instance registered.qargi memory split|merge|copy: a note whose ownSources:line is exactlyorigin not recorded (qargi memory)(written by an earlier copy or by hand) is no longer mistaken for the line qargi inserts into a note that has none. Undoing a copy or an archive (merging a split's target) now returns both kinds of note byte for byte; which one it was is read from the digest already in the origin segment, so the segment format is unchanged and notes written by earlier versions restore as before.- The tracker's
stop_reasonwas empty for every session: the session object has no such field.status,watch,send,closeandtracknow read it from the lastsession.status_idleevent of an idle session.watchcan now printbudget_reached.
Security #
- A review member could lift its own read-only limit: the guard took the ability from
RUN/ability, a file the member could write. The guard now refuses any write to a run'sabilityandrefs.before, and a member that only reviews names its class in the hook command of its definition (under the protected agents directory), which holds on every task whatever the run file says. Runqargi agent refresh NAMEfor existing members to get the new hook; a guard given an unknown class refuses every call. - A member could rewrite the installed tool through the links in
~/.claude/skills(they resolve into the installed copy, which was not a protected location) and a project's.claude/agentsdefinitions (which carry the hook that guards a member). The guard now protects the installed tool root recorded ininstall.jsonand.claude/agentsoutside the run's clone, as it already did.claude/skills. Reading stays allowed.
0.1.0 #
Added #
- Six skills behind one
qargicommand:/qargi-task(dispatch and session lifecycle),/qargi-project(the one-time questionnaire),/qargi-instances,/qargi-provision,/qargi-memoryand/qargi-verify.install.shinstalls them at user level (skills, command, manual page, completion) or per project (skills, hidden from git through.git/info/exclude), as symlinks or copies. - Named instances, local or cloud (a
Managed Agents deployment), registered outside every repository with model and
effort chosen at
init. Cloud verbs need--asked, the user's request naming the cloud instance; sessions are tracked, watched by a bounded poller, collected into a fresh directory and archived, never deleted. A session budget can only be set at creation, so the spend ceiling lives on the deployment (instances budgetchanges it). - The agent contract (one task, own clone, patches back, never push, no backlog) and per-repository profiles for gates and rules, kept under the user's config directory and inlined into the task block, so nothing qargi related is added to a project's committed files.
- Project initialisation (
project template|init|show|list|validate|set): the preliminary questions asked once per project (repository, long-lived branches, roles, per-role base branch, push policy, model, effort, packages, Anthropic skills, budget, stores), validated and stored. Never push is the default for every role; a role that pushes is an explicit answer with a branch pattern that cannot match a long-lived branch and, for the agent itself, a credential. - Provisioning with
ant apply(provision plan|apply|add-reference): plan first, create only with the user's go-ahead; model, effort, packages, skills, budget and stores come from the stored answers. - A naming convention:
<project>-<role>-<component>for agent, environment and deployment (rolesbuild,review,write,test), project-level<project>-memoryand<project>-referencestores that are attached rather than duplicated, and sessions named<instance>-<task-slug>.instances normalizerenames to match andinstances showreports deviations. - The canon (
canon/CANON.md,canon/layout.json, version 1, still a draft): the standard layout of the two memory stores. Both hold a/CANON.mdmarker, a README,shared/for every role androles/<role>/with anINDEX.mdper role; folders are named for their scope, with no numbered prefixes. Agents load the README,shared/INDEX.mdand their own role's index, never another role's folder. qargi canon check|plan|apply|import|seed-role|promote|audit: the reconciler brings a store to the current version without losing content (renames keep identity and history, conflicts go to/_conflicts/, the marker is written last, re-runs resume; the legacy layouts and earlier drafts are recognised), imports a foreign store such as the old documentation store into a role folder with relevant names, writes a role's skeleton, moves a file toshared/, and reports cross-role writes from the memory version history.tests/canon_sim.pyproves a migration is idempotent and conformant.- Provisioning creates the project's stores in the canon layout and seeds each
enabled role's skeleton (
provision/role-seeds/<role>/); the cloud agent prompt names the agent's role and the folders it may load. - A generic
writerole skeleton (provision/role-seeds/write/): the write ability (accuracy first, profile-defined checks and formats, conflict resolution, history logging), reference files for rules, workflow with the definition of done, per-release sync and review, writing conventions, styling, facts and preferences, and memory files for history and open items, distilled from a working documentation methodology. A role's ability skill is appended to its cloud agent's prompt;tests/role_seed_sim.py ROLEproves a role seed conforms to the canon. - Cross-role content of a project's reference store lives in
shared/(rules every role follows, the approved example values, architecture, source map, checked facts); a role's folder holds only that role's own. - Approvals bind to the plan: plans print a token, the matching apply needs
--plan TOKENand refuses if the plan changed; every gated verb is appended to an audit log. Sessions without a spend budget are refused. - Local dispatch has a technical barrier (
qargi local prepare|verify): a clone of a remote-less bare copy, a credential-less git wrapper, and a comparison of every ref of the real repository and its remote before and after. - The push policies
coordinatorandagentare designed but not built; the project validator refuses them instead of accepting an answer nothing honours. The unusedvault_ids,profileand credential answers were removed. - Gates have one source of truth, the repository profile; the reference store explains them.
- Canon version 2: both stores use one note format (
Kind,Applies to,Sources, andCheck/Verifiedfor the runnable kinds), one index form (| Path | Purpose | Load |), a kind vocabulary per store, freshness, size and hygiene rules;canon checkverifies all of it andmemory lintis the same check. A version 1 store migrates by adding what a note lacks and never editing a body; imports get the header too; both stores carry a_template.md. Reconciler fixes: interrupted migrations resume, maintenance creates missing role indexes, an unrecognised layout is an error.tests/run.shruns the whole suite. - Private repositories: an optional
repo.credential_filein the project answers (a token file only you can read, outside every repository).qargi credential checkproves the token reads and warns if it can also push; a cloud session is created with the repository mounted when a credential is set;local prepare --repo-url --credential-fileclones a private repository. The token goes toanton stdin or to git through an askpass helper and is in no argument, tracker, audit log or repository;tests/test_cred.shproves it with a fakeant. - The secret rule flags an assignment of a long mixed value, not prose about tokens; the
/home/user/placeholder is allowed. - Skills and tools (
/qargi-packs,qargi skill|tool ...): one library with globally unique names, attached by links at four scopes (user, user-role, project, project-role) and opted out per role withpacks_off. A skill is a directory withSKILL.md; a tool is an MCP server with an explicit allowlist and a permission policy per tool (write-like tools cannot run unattended unless marked on purpose). Import takes a local path or a pinned git URL and shows a review (files, scripts, URLs, secret findings) that must be approved with the plan token; any later change is drift and failscheckand provisioning untilsealed. Provisioning attaches a role's packs, andpacks apply INSTANCEupdates an existing cloud agent behind a plan token.tests/test_packs.shcovers it offline. - Commit rules: agents commit change-specific units (a change with its tests, docs and changelog entry), never squash or rewrite, and advise in the manifest and report how the work should be committed (
commit_advice); the coordinator reviews every patch and applies the commits separately, never squashed, checking that each one builds. qargi instances refresh-prompt NAME: rebuilds a cloud agent's system prompt from the current preamble, contract and role addendum (shared with provisioning), prints what changes, and applies behind a plan token, so an agent can always be brought back in line after the contract changes.- Patches travel with their context: agents commit as they go, put the reasoning in each commit message, and return one numbered patch per commit plus
series.mdandpatch_groupsin the manifest (related commits grouped under the change they implement), never one combined patch.qargi review check RUN_DIRrefuses a monolithic patch, a commit without a message body and a manifest that does not match the patches, and notes commits made in one batch.tests/test_review.shcovers it. qargi review checkaccepts a review run that returns a report instead of patches: a non-emptyreport.mdwith a Findings section passes, an empty run or a report without findings is refused.tests/test_review.shcovers it.- Baseline seeds for every role (
provision/role-seeds/<role>/).buildgets real generic guidance (rules, workflow and definition of done by kind of change, gates, conventions for code, tests, user-visible changes and commits, a self-review checklist);reviewandtestare new, each with its ability (a review returns a report and never edits the project; a test must be seen failing for the right reason, run three times, and never skipped or weakened), reference files, and a memory note (recurring findings, known flaky tests). Every file keeps a marked "Project specifics" slot for what only the repository can say.tests/role_seed_sim.pyruns for all four roles in the suite. - The manifest has an
artifacts[]key for deliverables that are not patches. The review ability states that a review has no patches (commits: [], the report as an artifact, a scratch CLONE) and the test ability says where its evidence lives (fail-first,passandstability-Nentries ingates[], ten runs as the floor). The cloud preamble no longer repeats the stores' own load rules. - Seeds are leaner and have one home per fact: gates, source map and test conventions are
shared/seeds (canon seed-role --role shared, run first by provisioning); a role's rules are not repeated from the contract; placeholder-only files are no longer shipped (the skill lists what to author); the per-file source line and the store-maintenance boilerplate are gone from indexes; the review checklist gains design and readability, the test conventions the test pyramid and assertion messages, the dev conventions secure-coding habits. A project statement replaces the baseline bullet it covers. - Canon token rules: a note over 10000 characters warns (an agent loads a note whole), an index over 2500 characters warns, what a role loads before it knows its task (README, indexes, notes marked
always) is at most 8000 characters per store, and an index Load cell must bealwaysor a task, never "when unsure", "if needed" or "working on <project>".canon checkreports each. - The README seeds are about 40% smaller (the layout diagram the indexes already cover is gone), so a role's up-front load fits the budget with room for its rules.
- The project is named qargi: the command is
qargi, the skills areqargi-task,qargi-project,qargi-instances,qargi-provision,qargi-memory,qargi-packs,qargi-verifyandqargi-team, the ability skillsqargi-build,qargi-write,qargi-reviewandqargi-test, the environment variables areQARGI_*, and the configuration, state and data directories are namedqargi. - Resource names follow
<project>-<component>[-<role>]: the agent<project>-<role>,<project>-deployment-<role>, the project-wide<project>-environment(shared by the roles, holding the packages of all enabled roles), and the existing<project>-memoryand<project>-reference. Provisioning,instances normalizeandshowuse the new names; an instance is still<project>-<role>and a session<instance>-<task-slug>. - The reconciler keeps the README's list of roles in step with the project's roles when one is added.
- Teams (opt-in,
/qargi-team): aleadand namedteamsin the project answers; a lead role of its own (PROJECT-lead, a platform coordinator whose members are the role agents, pinned to their versions, with the team definitions in its prompt);qargi team brief|check|contract; a team's outputs are role-prefixed andcollectsplits them per member; the build member owns what ships with the code (manual page, help, completion,docs/), the write member owns the operational docs on its branch. The lead can be this Claude Code session, a cloud lead, or a session started in the console.qargi hook session-startloads the lead's READMEs and indexes at the start of a Claude Code session inside a registered project;qargi memory getreads a note. The shared environment is reused when a second role is provisioned.tests/test_team.shcovers it offline. - Push grant for the lead (
lead.pushin the project answers; default none). Only the lead ever pushes, members never do.local: the Claude Code session leading may pushagent/*branches and open pull requests without asking each time (the autoload says so, or says to ask before every push);cloud: branches: a cloud or console lead may do the same with a fine-grained token file kept outside every repository and different from the read-only repository token. Merges and releases areask(the person confirms each one) ornever; there is no automatic merge or release, and a branch pattern outsideagent/is refused. The lead's prompt carries a Push section that grants exactly this, or says it never pushes. review check --allow-empty: a team member that returns no commits (blocked, or nothing to change) passesteam check; a single run that claims DONE with no commits still fails.qargi credential setup lead-push PROJECT: one command for the cloud lead's write token. It reads a fine-grained token from a file or stdin (never an argument), checks against GitHub that it reaches only this repository and cannot administer it (classic tokens are refused), stores it mode 600 outside every repository, creates a vault of its own with a bearer credential for the GitHub tool, attaches the vault to the lead's deployment and records the grant in the project answers. It prints a plan first and applies behind a plan token. The lead then gets a fixed GitHub allowlist (packs render,packs apply): read tools and branch, file and pull-request writes unattended,merge_pull_requestasked of the person, no delete tools and no release tool.tests/test_leadcred.shcovers it offline.credential setup lead-pushtests the token itself: a push dry-run over https for Contents write, and a branch-protection read that only a token with Administration can make. The repository answer shows your own role, so it is no longer used to judge the token.credential setup lead-pushno longer counts the repositories a token can see (a fine-grained token reads every public repository). It probes each repository the account can see with a push dry-run and refuses a token that can write anywhere but the project repository, unless--allow-more-repos.qargi lead grant|status|revoke PROJECT: the cloud lead's push grant as one guided flow.grantexplains how to create the fine-grained token, reads it hidden, and walks through three steps (token and vault, the GitHub tool, the Push rules), printing each plan and asking at the terminal; your answer is the recorded approval, and it never runs without a terminal. Steps already in place are skipped, so running it again is safe.statusshows what is in place and what is missing, including members pinned at old versions;revokeremoves the tool and the Push rules, archives the credential, detaches the vault and deletes the token file, and reminds you to revoke the token on GitHub.tests/test_lead.shcovers it offline.collectalso accepts a lead-only run.- Help, a manual page and bash completion, all generated from one description of the commands (
lib/commands.py):qargi help [COMMAND [VERB]|TOPIC],--helpon every command and verb, topics (concepts,approvals,naming,memory,push,files),man/qargi.1(qargi help --man), andcompletions/qargi.bash, which also completes your projects, instances, a project's teams, sessions and packs.install.sh --userinstalls the page and the completion and--uninstallremoves them. Inside a project's repository a missing PROJECT is filled in from the current directory (QARGI_PROJECToverrides, an argument you give wins) and a role stands for the instance (devforPROJECT-dev); outside one, a missing project says which projects exist.tests/test_help.pychecks the description against the scripts, the page against the generator, and the completion and the inference. - Tab completion for zsh (
completions/_qargi) and fish (completions/qargi.fish) beside bash, from the same description, with a one-line description for each command, verb and option in the menu.install.sh --userinstalls and--uninstallremoves them.tests/test_help.pyruns both shells when they are installed. - Tab completion is comprehensive: values come from your own state, each with a description in zsh and fish, and are narrowed to what the verb can act on (sessions by state, cloud instances for cloud verbs, projects with teams for
leadandteam, a project's roles, skills or tools by command). New: memory store, deployment and model ids; the branches of the project's repository for--base; the keys of a project's answers forproject setand the values each takes; spend ceilings and wait times; the verbs of a command afterhelp COMMAND;--option=valuein all three shells (bash puts the word back together); options already given are not offered again, and--helpis offered with them. A test fails when a new kind of value inlib/commands.pyhas no completer. qargi exportandqargi import: save and restore selected components (config, definitions, memories, state, credentials) as one archive, optionally encrypted with a password (gpg, AES-256; never in argv). Import verifies the archive (names, entry kinds, checksums, format), asks which parts and projects and how to treat what exists (merge, overwrite, replace), prints the plan, writes nothing before you confirm, backs up what it replaces, and treats the audit log as append-only. Credentials only in an encrypted archive; definitions are restored as files; memory notes are written to the project's stores.qargi discover sources|search|recommend|show|install: tool discovery for the roles of a project. Searches a reviewed catalog and your library, and with--onlinethe official MCP registry and Anthropic's skills; recommends by role and by the stack seen in the repository; installs through the existing pack gates (review, your approval of the exact content, scope choice, link, optional apply to cloud agents), or edits the role's apt list or the local agent's tools line after a confirmation. Entry IDs tab-complete.qargi watch SID [MAX_SECONDS] [--warn PCT]ends early withbudget_tight <cost>/<ceiling>when a cloud session's cost reaches PCT percent of its ceiling (default 85, 0 turns it off), so the coordinator can offer the user a raise (qargi instances budget NAME CENTS --session SID) before the session stops atbudget_reached.- A
PreToolUseguard (qargi __guard) on the members' Bash, Edit, Write and NotebookEdit calls refusesgit push,gh,ssh,scp,sftpand changes to a git remote;qargi discover installedits the tools line of the members that serve a role (or of all). The agent contract now separates how to wait for a long command locally (run_in_background) and in the cloud (detach and poll) and asks for agitleakspass before the report, which the coordinator repeats. - Cloud agents get a per-role list of built-in tools to switch off (
roles.<role>.tools_offin the project answers; defaultweb_searchandweb_fetchfor every role, pluseditfor review and the lead), rendered by provisioning as toolsetconfigsand carried to existing agents byqargi packs apply, whose plan lists them. The shared environment can also holdroles.<role>.pipandroles.<role>.go(merged likeapt), and--networking limitednow rendersallow_package_managersandallow_mcp_serversso the platform accepts it. The built-in GitHub tool for the lead no longer lists four pull-request read tools the server does not expose, and stateswrite_okfor its unattended writes. - Roles are named by their activity: the abilities
build,write,reviewandtest, pluslead. Each ability is defined by a skill (qargi-build,qargi-write,qargi-review,qargi-test), which a member loads when the task block names it (ABILITY: build — load skill qargi-build) and a cloud agent of that role carries in its prompt. The memory folders areroles/<role>/, the project answersroles.<role>, the instances<project>-<role>, a team's output prefixes<role>--, andteam brieftakes--write-doneand--review-done.qargi help roleshas the table. - Members: the local workers are agents you name. qargi ships one member definition (
agents/member.md, withSkillamong its tools) and installs none;qargi agent add NAME [--abilities LIST] [--model M] [--effort E] [--project P]renders it (all four abilities by default; a name that exists, is not kebab-case or is a built-in Claude Code agent type is refused),qargi agent for ABILITYprints the member dispatch spawns, andlist,renameandremovemanage them./qargi-projectasks for the members once. - Review is read-only as a rule:
qargi local prepare --ability Arecords the task's ability in the run directory, and under review the guard refuses an edit outside the run's output directory and a Bash command that writes into the clone (a redirection, a file command, a git command that changes the work tree or history). - The coordinator's procedure for verifying and applying returned patches is the skill
qargi-verify. qargi migrate roles [--member-names build=NAME,...] [--apply --plan TOKEN --asked "..."]: existing installations made with the earlier names (rolesdevanddocs, the agentsqargi-dev,qargi-docs,qargi-review,qargi-test, cloud agents<project>-agent-<role>) move to the current ones: registry, tracker, answers, pack links and saved definitions; the notes of both memory stores; the cloud agents and deployments (renamed by update, nothing archived) and their prompts; the old agent symlinks, replaced by members. It prints a plan with every cloud call and a token, applies exactly that plan, stops at the first failure and is re-runnable.qargi importmaps an archive made with the earlier role names to the current ones.