Skip to content

Releases

qargi 0.3.0

Choose a tag to compare

@github-actions github-actions released this 15 Nov 00:24

Added

  • Graph timestamps restrict hours to 00 through 23 consistently across Python runtimes. Direct-reader tests suppress bytecode writes, and the reader contract documents import-cache behavior and unresolved paths in invalid reports.
  • qargi backlog graph check|show|why --graph FILE inspects a closed native v1 graph against the configured open backlog. It validates declared relationships, pins reported evidence to parser source revisions, propagates unknown prerequisites conservatively, and explains proposed needs and stale evidence with deterministic reports. Complete explanation paths can have quadratic output size. Contains edges describe structure. Archives and feature lifecycle have unknown observation state. Evaluation and execution follow existing governance. The public contract in docs/reference/backlog-graph.md documents manual editing, the current lead-only guard boundary and compatibility limits.
  • Backlog graph reports malformed input paths, including configured backlog.file values containing NUL or unencodable Unicode, as input-unreadable findings with valid:false JSON and exit 1 instead of a traceback. Unread inputs retain null digests, and ordinary filesystem errors retain their diagnostics. Text reports escape non-printable controls and characters that the output encoding cannot represent while preserving printable Unicode.
  • Graph source paths use a literal leading-tilde replacement with raw HOME for repository and backlog paths. The public contract documents bounded differences from Bash replacement of & and shell stripping of NUL/trailing newlines. Source and graph paths retain symlink and parent segments so the kernel selects the intended bytes. Empty and false section values are unset, and the read-only graph can observe an absolute backlog after its checkout has gone. Confirmed structure errors name participating nodes and relations, and the public contract specifies report reasons and first-error behavior. The public graph command remains lead-only with a precise guard denial; members may run the pure Python reader directly. Fixed ISO-date cases run on the current tested runtime without certifying a Python 3.11 runtime.
  • Fast plans using --changed-files add the full-tier strict literal converter subset when CHANGELOG.md changes. An empty changed-file list excludes this subset from fast selection. Whole runs retain both the subset and the complete site step.

Changed

  • Shared project inference introduces a breaking change to leading-tilde repository matching. It treats any leading tilde in repo.path, including ~name, as the raw HOME prefix plus the remaining text, rather than expanding a named user's home. With HOME unset it uses an empty prefix in place of the former password-database fallback. This follows ordinary backlog prefix rules with the documented Bash & exception; explicit project arguments and QARGI_PROJECT retain precedence.
  • The graph v1 input contract requires Unicode scalar values in free-text by, basis and ref fields. Escaped lone surrogates produce invalid provenance reports; valid surrogate pairs and printable Unicode remain supported. This does not establish general JSON security.

Fixed

  • Graph inputs preserve terminal slash and /./ kernel semantics instead of silently opening a regular file after path normalization. Deeply nested unrelated registry JSON is skipped by shared inference, and a selected deep project configuration yields an invalid graph report. Relative graph paths from an unavailable current directory produce input-unreadable; absolute inputs remain usable with an accessible configuration and explicit project. The public shell entrypoints may still print getcwd startup warnings. backlog list, add and done require a repository marker even though graph observation of an absolute backlog does not.

qargi 0.2.14

Choose a tag to compare

@github-actions github-actions released this 14 Nov 08:25

Fixed

  • The agent test runs serially after the parallel pool following checkpoint2 8a7a675's lone timeout at 678s without assertion failures (137 steps passed; suite total 2554s). Its agent code and test match 408b8ae, whose lead DONE passed in 440.82s. This scheduling mitigation lengthens the serial tail without changing cases, limits, budgets or recorded durations; it establishes neither a root cause nor a performance guarantee.
  • Conformance case 15 checks prepare's original-root plain-path diagnostics and unchanged scratch/TMP parent entries; runtime coverage is the symlink real-path branch only, not runtime plain paths or prepare real paths. Redirect fixtures read TMP through the trusted registry reader and stop on unknown labels or move/link failures, with focused negative probes. These are fixture checks, not product containment or qualification evidence.
  • Signal cancellation tests exercise regular and symlinked runtime executables using dispatch's real path, failing promptly on an executable-path mismatch in the timed exec-child wait while retaining readiness, PID and group assertions without changing limits.
  • Both guard test selections and the local-run workflow test run serially after the suite's parallel pool to mitigate contention observed at checkpoint1 e77c682. Reduced-concurrency reproductions passed; this scheduling mitigation increases the serial tail without changing cases, limits, budgets or recorded durations, and does not establish a root cause or guarantee performance.
  • Codex spawn guidance fits the existing 393-word provider-notes budget while retaining prepared defaults, override and identity qualifications, drift refusal and sandbox guidance.
  • The running-signal lifecycle test targets the runtime child's wait after proving its readiness and PID, so prepared-profile resolution subprocesses cannot receive the injection; cancellation and process-group shutdown remain required.
  • Provider acceptance tests distinguish logical command profiles from stale in-session member rows and verify prepared high effort, unsupported override refusals, policy drift and supported explicit tier/effort overrides.
  • Command conformance uses one bound provider definition for prepare and run, retaining private request authority. Hidden-path coverage checks prepare's refusal and runtime's independent recheck after member paths are redirected into a hidden place.
  • local run retrieves the prepared-command marker with the required registry filter when its configured provider becomes unavailable, preserving re-prepare guidance without an unbound-variable diagnostic. Refusal leaves the private request intact and starts no member.
  • local run --model uses the provider's established pattern search semantics, so repeating a prepared out-of-tier model accepted by a prefix pattern works without override capability verification.
  • Command-mode agent models text and JSON now use the same profile resolver as agent for, showing logical bases, selected model IDs or aliases, requested effort levels and sources without member registration warnings or legacy roles/lead selections. JSON retains existing keys and profile metadata and adds command mode, selected, requested_effort and logical-base markers; native effort_control remains separate from the requested level. Refused entries remain visible with their reasons. The intentional never-dispatched lead row is informational, without a refusal or warning; agent for --profile lead still refuses. Empty tier aliases fall back to the ID, as command dispatch does. Member registration and alias flags apply to in-session rows. Row flags do not fail the display, but command-mode extension composition or load errors may fail the command.
  • local prepare resolves explicit and implicit command profiles before making run artifacts and retains the request in lead-owned private lifecycle authority. local run uses its requested model and effort when each option is omitted, rejects runtime/mode/catalog/profile/policy drift with reprepare guidance, and permits explicit changes only after current catalog and effective capability checks. Null effort controls remain omitted with a note; requests do not claim observed identity. RUN and saved request files are inspection mirrors. Existing records without requests retain their documented behavior without adopting member files.
  • agent for selects dispatch mode without composing extensions, preserving in-session resolution's existing handling of extension errors. Command runtimes still validate the project's enabled extensions.
  • Command-mode agent for refuses malformed non-null profiles containers and selected entries before normalization can inherit shipped defaults or choose a class fallback. An absent or null profiles container uses shipped defaults, consistent with project validation. Present profile fields follow the project rules for bases, known-role ability lists, purpose, allowed keys, model, tier and effort; null selected entries and null field values are refused. Unrelated profile entries do not block resolution.
  • agent for resolves command-runtime profiles to logical worker/reviewer bases without member files or registration, using the runtime's catalog and ignoring legacy member model/effort answers. Invalid profile models, tiers and effort levels are refused. Text output and JSON's model // model_id select the alias only when the catalog reference says use: alias and the tier has one; inline models and use: id select the ID. JSON marks command mode. Effort remains the requested level, including when the runtime has no native control.
  • local close again names the applied branch and rewrite remedy when refusing a completed authoritative apply; unfinished applies still refuse early.
  • Help, the manual and verification guidance qualify discard ownership and quiescence guarantees as lifecycle protections; legacy recovery remains explicitly weaker.
  • Lifecycle cleanup completes bookkeeping after an already removed work directory’s parent changes or its tombstone is recreated, preserving foreign entries. Replacements are reported only when observed through the original parent.
  • A process reaped while /proc/PID/stat is open can return ESRCH; lifecycle probing now treats it as PID absence, like a missing file, only while /proc remains available. Other evidence errors stay unknown and process-group completion still requires the kernel absence check.
  • Interrupted lifecycle cleanup preserves and reports replacements at original RUN/TMP names after staging or deletion, then completes original tombstone deletion and exact registry bookkeeping. Owned tombstones recover crashes before phase commit; pre-stage replacements and foreign/unowned tombstones still refuse. Internal TMP completion is recorded before RUN staging, so recovery never traverses a replacement RUN or its symlink target to inspect old TMP paths.
  • Genuinely unmarked registered legacy runs again support warned local clean --discard, and ordinary clean where the released checks permit it, including applied/stale batches. Registry-sourced TMP confinement and one-filesystem deletion remain; this weaker recovery proves no process quiescence and creates no lifecycle authority. Missing/corrupt marked state and stripped markers with private lifecycle remnants still refuse.
  • local prepare again accepts symlinked, relative and ..-containing QARGI_RUN_TMP parents. It resolves the parent before creating directories and consistently records and displays the canonical TMP path; repointing the alias later does not redirect cleanup. Hidden-path checks still inspect actual paths.

Changed

  • Clean authorizes retained evidence before deleting TMP or RUN. Prepare records private directory identities; interrupted cleanup stages owned tombstones and refuses source replacements before staging. Missing ownership evidence for marked runs requires manual recovery.
  • Process-group completion requires a kernel absence report. A changing process listing or zombie-only sample cannot authorize retry or cleanup; runner diagnostics identify busy or unknown groups without claiming live survivors.
  • A full verification history refuses before publishing a new snapshot; identical observations still consume no history.
  • Lifecycle safety guidance is generated from the canonical help source, keeping the manual and command help consistent.
  • Structural review can inspect unsuccessful attempts as diagnostic evidence; it explicitly says that inspection is not acceptance, and verification and apply still refuse their output.
  • A signal received after a local member finishes no longer marks its private attempt cancelled, so completed output can be verified.
  • Cleanup diagnostics explicitly name retained cleaned registry evidence; covering shell expectations retain branch, worktree, scrub and registry assertions under the conservative lifecycle policy.
  • Retry epochs precede ref collection, including slow remote queries; lead-written report manifests take their base commit from private prepared authority.
  • Lifecycle clean records owned atomic tombstones before deletion and keeps its cleaned registry row. Repeating clean resumes interruptions before or after registry and lifecycle-state removal without adopting orphan paths or implicitly discarding retained evidence.
  • Save staging is limited to one privately recorded directory per run and recovered after owner death without leaving a copy child. Unknown or replaced staging refuses cleanup; retained-copy limits count directories, not bytes.
  • Unfinished lifecycle applies refuse resume and cleanup after their parent dies because Git-child termination is unknown. Completed Git work marked built retains supported bookkeeping recovery; hooks still receive no lifecycle lock.
  • Dispatch installs signal handling before committing begin, records caught prelaunch cancellation as not-started, and refuses unknown pr...
Read more

qargi 0.2.13

Choose a tag to compare

@github-actions github-actions released this 13 Nov 05:28

Changed

  • Member tasks can reuse lead-supplied passing baseline evidence for the exact BASE_SHA when relevant input and check definitions match, recording its reference, SHA and provenance in deviations[] rather than claiming a run or gates pass. They run focused checks and applicable gates for each change, then DONE on final HEAD after all outcomes and again after later changes; profile gates and the lead's whole-suite checkpoint remain in force.
  • qargi feature park and automatic parking follow allow nonconflicting untracked runtime files while refusing tracked changes, parking-branch commits absent from develop, and collisions with untracked or ignored files. A failed automatic follow leaves the parking branch behind without undoing a successful integration.
  • Installation examples distinguish user and project choices, and the memory tiers example aligns its columns.
  • Documentation examples and explanatory tables fit narrower GitHub views more readily.
  • Ordinary clean retains the read-only product refusal independently of refs-only observation: unexpected patches or a bundle preserve the run even with zero extra clone commits. An unchanged internal refs verdict grants neither verification nor artifact acceptance; explicit discard deliberately recovers it and reports actual refs.
  • Inspection failures now show sanitized reasons in run, verify, review check and clean. The 0.2.13 limits stay fixed: 512 MiB copied metadata (the whole object store included), 1 MiB config, 100,000 traversal/read steps, 64 directory levels and 60 seconds for copy traversal and each Git subprocess. Ordinary prepared clones are supported only within those bounds. Ordinary clean preserves every artifact when worktree state is unknown, including when commit inspection succeeds; explicit discard remains deliberate recovery after preserving wanted work.
  • Inspection disables replacement objects, preserves the difference between valueless (true) and explicit empty (false) Git booleans, and uses a minimal format-0 config for SHA-1 while keeping explicit SHA-256 format support. Regressions include replacement refs and a same-length, restored-mtime racy-index case with a negative control.
  • When the run directory still exists, classless registered legacy discard reports the authoritative refs verdict, including unexplained real ref changes. Its internal refs-only observation neither verifies the run nor saves accepted output or grants class permissions; ordinary unknown-class refusal remains.
  • Unexpected runner failures retain nonsecret start/process and home metadata, cleanup status and refreshed-file residue for list and verify. Retry checks the original group for every dispatch outcome and refuses known or unknown survivors until quiescence can be established, without signalling a potentially recycled PID. A partial home setup whose cleanup also fails keeps an explicit cleanup note alongside the planned link names.
  • feature finish validates project identity/answers and strictly parses and enumerates the same registry snapshot. Missing/malformed answers, dangling/unreadable/malformed registries and reader failures refuse; empty exit 1 and traceback output are distinguished from valid open-run rows. General diagnostic readers remain tolerant. This removes the double-read path but provides no coordination of new preparation through the eventual merge.
  • Local runs stop remaining members of the original process group before removing authentication links and inspecting output, including on normal exit and unexpected runner errors; surviving members fail an otherwise successful run. Unexpected errors retain the original exception even if cleanup also fails, and an unreadable survivor count stays unknown.
  • The lead verbs and guard MCP policy validate the decoded JSON class against the exact supported registry strings before shell decoding and preserve malformed runs for investigation. Explicit clean --discard remains a recovery route for known registered legacy runs without a class; it intentionally drops evidence and cannot discard unregistered runs. A member's review check requires its marker to resolve to the inspected run, then checks structure without lead registry authority, verification, scrub marks or cache. Lead review checks validate class before renewing the lease. Pre-existing guard shell/write tightening still consults the member class file; those paths and their heuristics are unchanged.
  • Clone inspection reads a bounded private metadata copy in trusted lead state with a minimal configuration and an absolute Git resolved once from the lead's controlled PATH, so even a newly inserted filter cannot execute during inspection. The copy preserves index timestamps for same-length write detection and supports SHA-1 and SHA-256. Unsupported metadata layouts and inspection failures stay unknown; ordinary clean preserves unknown commit state. Copies may combine concurrent states and do not contain escaped sessions.
  • Expected setup refusals retain planned sign-in link names if partial cleanup fails, so list and verify can locate residue. Unreadable process-group probes no longer prevent TERM-to-KILL escalation; an unreadable final count remains unknown. Cleanup marks the run cleaned without granting verification, including when no prior verify was run.
  • Feature finish requires repository identity for any run path that exists or cannot be inspected. Structurally valid records for confirmed-absent run paths do not block finish solely for missing identity; the records are not deleted, rewritten or granted authority.

qargi 0.2.12

Choose a tag to compare

@github-actions github-actions released this 12 Nov 04:50

Added

  • The model catalog of the family openai (lib/providers/catalogs/openai.json), the table a runtime of that family names with models.catalog: the tiers fast gpt-6-luna, balanced gpt-6.1-terra, deep gpt-6.1-sol and frontier gpt-6-astra (ids of the pattern ^gpt-[a-z0-9.-]+$, passed as ids, with no alias), and the effort levels low to max under the same names. No tier is verified yet (verified_on is null until a real run uses it); the vendor's sixth effort level, ultra, has no qargi level and is not mapped.
  • The local runtime codex (lib/providers/codex.json, data only, no code of its own): a project whose local provider is codex runs its members with qargi local run, which starts codex exec --json --ephemeral --ignore-rules --strict-config -C CLONE [-m MODEL] [-c model_reasoning_effort=EFFORT] -o FINAL in the run's clone with the prompt on standard input, adding --sandbox workspace-write --add-dir OUT --add-dir CLONE/.git for a write run and --sandbox read-only for a read-only one (--dry-run prints it). The clone's .git is added because workspace-write keeps it read-only, so git add failed on index.lock and a build member could not commit (a live check with codex-cli 0.167.1 showed git add and git commit working with it). The cost of that grant: a write member can change its own clone's git configuration and hooks. The clone's only remote is the run's own bare copy, with its push URL disabled, the sandbox has no network, the lead takes the work from the patches in OUT, and qargi local verify compares the real repository's refs; a read-only run never gets the grant. A path of the run in a definition's argv ({clone}, {out_dir}, {run_dir}, alone or with a path after it, such as {clone}/.git) must stay inside its directory and pass through no link, or qargi local run refuses the run: a .git an earlier member left as a link would otherwise point the sandbox's writable directory anywhere. The read-only barrier is that sandbox alone (barrier.kind sandbox-args, claimed partial): it holds a member's shell commands, not its reads, and no guard hook runs. Headless dispatch, the read-only barrier (partial) and the token report are verified by live checks with codex-cli 0.167.1 on 2026-11-11 (verified_on and source in the definition name the evidence), so a review role on codex runs with the barrier's note and needs no provider accept; the model and effort overrides (accepted in a run, but no event names the model or effort used), skills and MCP stay unverified and count as not supported. The member's last message goes to a new file of the run (FINAL, below) and becomes OUT/report.md, and the run's contract addenda (lib/providers/codex/local-run.md) say so. --ignore-rules keeps every Codex execpolicy rules file out of a run (a rule can let a command run outside the sandbox), and --strict-config makes Codex refuse a key of the run's configuration file it does not know, so a baseline key a later CLI renames stops the run instead of being ignored. A Codex member gets the MCP tools of its role and the runtime's baseline in the configuration file of its run home (below), and no resumed session. It gets no qargi skill: its CODEX_HOME and HOME are the run home, which has no skills directory, its clone carries only the skills the repository commits under .agents/skills (a session's untracked links do not travel to a clone), and the baseline turns Codex's bundled skills off, so a member receives the contract and the task, and never a lead-management skill (a test runs a member with qargi-flow, qargi-verify and qargi-task linked where a Codex lead finds them and checks that it sees none). qargi provider notes answers spawn, session-model, exploration, skills-dir, identity and commands for it.
  • A command runtime is told where to write the member's last message with the placeholder {final_message}: a file in a directory of the run that qargi local run makes new for each run (RUN/final.XXXXXXXXXXXX/message.md, mode 700), never a file of OUT, so the runtime never writes into OUT itself. When the process has ended the file is taken only as a regular file with one name, of UTF-8 text, at most 1 MiB, opened without following a link or waiting on a pipe; it is published as OUT/report.md by renaming a new file (mode 644) over that name, so a link, a pipe or a second name a member left there is replaced and never written through. A link, a pipe or a second name at the private file is removed (what it points to is untouched), a directory there is left, and a file that is too large or not text is kept for you to read; none of them is published. The output says what became of it (REPORT=OUT/report.md (N bytes ...) or REPORT=none: WHY), and RUN/dispatch.json records it (report: state, why, bytes, kept, previous). On --again the report of the earlier run is set aside as OUT/report.md.previous just before the member starts: a run that publishes a report replaces it, or one an earlier rerun set aside (PREVIOUS_REPORT=replaced), and a run that publishes none, a failed or refused one included, leaves it there (PREVIOUS_REPORT=kept as ...), where neither review check nor local verify takes it for this run's report. A report.md that is not a plain file of its own (a link, a pipe, a directory, a second name) refuses the run before anything starts. --dry-run prints FINAL_MESSAGE=. The codex runtime passes it as -o {final_message} in place of -o OUT/report.md, which let the vendor CLI write through whatever an earlier member had left at that name. The codex stub of the conformance suite now writes a last message where -o says, as the CLI does.
  • A read-only run whose report is the runtime's last message ({final_message}, the codex runtime's -o) gets its manifest from the lead: qargi local run writes OUT/manifest.json in the minimal report-only format of qargi review save (status, base, commits: [], the report in artifacts) with a run object of what the lead recorded (the run's id, role, runtime, class, the model and effort qargi passed, start, seconds, exit, outcome and packaged_by), taken from the lead's registry and from RUN/refs.before as read before the member started, never from the member. A Codex reviewer, which may write nothing, OUT included, had a report and no manifest, so qargi review check failed every one of its runs. The manifest is written only when the run's outcome is ok, its report was published and OUT holds no patch; otherwise MANIFEST=none: WHY says why, and a missing report, a report without a Findings heading or a valid last verdict line, and patches in a read-only OUT fail review check and local verify as before. It claims no test and no verdict (done.exit_code null, no gates); its status is BLOCKED when the first line of the report says BLOCKED, also under Markdown marks or as Status: BLOCKED (**BLOCKED**, # BLOCKED, **Status:** BLOCKED). The manifest of an earlier run is set aside as OUT/manifest.json.previous with the report (PREVIOUS_MANIFEST=), and a manifest.json there that is not a plain file of its own refuses a read-only run before anything starts. A write run's manifest stays the member's. RUN/dispatch.json records manifest (state, why, previous). The Codex addenda and the providers page say so.
  • A runtime started by qargi local run can have a home directory of its own (dispatch.home in lib/providers/schema-2.json: {env, dir, link: [{from, to}]}): the member gets RUN/home/DIR in the variable env, and each file from of your home is linked into it as to when the run starts and unlinked when the run ends, also when it fails or times out. The file is never copied; --dry-run prints HOME_ENV=, HOME_DIR= and HOME_LINKS= with the link names only, and dispatch.json records the names, never a source path or a value. A source that does not exist is not linked (a note says so). What the runtime does to the link during the run (a sign-in it refreshes) loses nothing, and qargi never writes the source: written through the link, it lands in the source, which qargi cannot prevent (a note); the link deleted, the source is untouched (a note); the link replaced by a file of its own (an atomic replace), that file is kept as NAME.from-run (mode 600) in the run home, never written over the source (another run, a session of the runtime or a sign-in of yours may have written it meanwhile), named in a HOME_RESIDUE= line and in home.residue of dispatch.json, with a note that says whether the source is still the file it was when the run started (the same real path and the same sha256, taken in memory then and never printed or recorded); you reconcile it yourself (docs/members-and-providers/codex.md says how), qargi local list and verify name it and qargi local clean removes it. No note or record holds a value. A variable of the run home (CODEX_HOME) set in your environment to another directory is a note, without its value: the run links the sources the definition names. The directory is made and the links removed without following a link a member may have put in the run directory. provider check, provider import and local run refuse a variable that qargi sets or never hands on (HOME, PATH, GIT_*, QARGI_*, XDG_*, the loader variables and the others env_pass refuses, and LANG, LC_ALL, TZ, TERM) or that env_pass also names, a dir outside the run home, a from that is not a plain path under your home or that lies in qargi's directory, a credential store of your other tools (ssh, gpg, git, forges, clouds, keyrings), a startup file of your shell (~/.bashrc, ~/.profile and their kin), what your desktop or service manager starts (~/.config/autostart, ~/.config/systemd, ~/.config/environment.d) or ~/.local/bin (checked again where the...
Read more

qargi 0.2.11

Choose a tag to compare

@github-actions github-actions released this 11 Nov 12:24

Added

  • qargi tool import SOURCE --tool-list FILE and qargi tool seal NAME --tool-list FILE judge the tools of a tool pack by the server's own tool listing: FILE is the JSON result of the server's MCP tools/list (or the response that holds it as result), saved to a file; qargi reads the file and never contacts a server. A tool that the listing does not annotate readOnlyHint: true, or does not name, may not be always_allow or auto unless the pack sets "write_ok": true; a tool annotated destructiveHint: true and a tool the listing does not name are reported in the review as NOTE lines; the check of the tool's name still applies. The pack's record keeps the sha256 of the listing and what it says of each tool of the pack (tool_list: read-only, write or destructive per tool; qargi tool show NAME prints it). tool_list is part of the content hash and of the plan token, so it is approved with the pack and a change of it is drift; a pack without one hashes as before. A later seal without the option keeps the record's listing, and a tool added to the pack since then counts as not listed. A file that is no listing ends the verb with exit 1, and the option with any other verb is a usage error (exit 2). Without a listing the review is the name check alone, as before.

Fixed

  • The review of a tool pack finds a write-like tool however the MCP server spells its name. The check only saw a write word at the start of a name or after _, so platform.delete_knowledge, repo/push, issue-close and deleteItem passed as always_allow or auto without write_ok. A name is now cut into words at every character that is not a letter (_, ., -, /, digits) and at a camelCase boundary (deleteItem, HTTPPost), and a word that starts with a write word (create, update, delete, remove, post, push, write, merge, send, set, add, edit, close, comment) makes the tool write-like, also in a name with no separator (deleteall, sendmail). A write word in the middle or at the end of a word does not count (offset, dataset), and the words settings, setting, address, addresses, postal, postcode, postcodes, additional and closest are taken for the names of things that are read, so settings_view, address_list and postal_code_lookup, which the old check refused, pass. The rule is written once, in lib/packs_check.py, and every review asks it (tool import, seal, show, link, check, the renders). A pack in your library that has such a tool unattended now fails qargi tool check and is not rendered until the tool is always_ask or the pack sets "write_ok": true, and you seal it again.
  • qargi packs apply --target claude-code PROJECT writes the permission rule of a tool under the name Claude Code gives the tool: mcp__SERVER__TOOL with every character of a name other than a letter, a digit, _ and - written as _, so the tool platform.list_items of the pack kb gets the rule mcp__kb__platform_list_items. The rule held the name as the pack writes it, which matched no tool when the name had a dot, and a name with a slash was refused. How a runtime writes a name is data of its definition (resources.mcp.name_key in lib/providers/schema-2.json: nonword-underscore, or absent for a name left as written; a runtime without it still takes only plain names). Two tools that would get the same rule (a.b and a_b of one server) are refused by the plan, since a rule for one would hold for the other. A render that holds a tool pack with a dotted name differs from what packs check now expects: apply it again.
  • The review of a tool pack checks the names in its tools map: letters, digits and _ . - /, 1 to 128 characters, the first a letter, a digit or _, so the dotted and slashed names that servers publish are accepted and a name with a blank, a control character, a quote or a wildcard fails with the name written as a JSON string. A tool pack that is not one JSON object, or whose tools is not an object (a list), fails its review; both passed before, because the check ended in an error and said nothing. The name of the pack keeps its rule (lower-case letters, digits, -).
  • A member's calls of MCP tools pass its guard, and a member has the MCP tools of its roles' tool packs and no others. The hook of a member matched Bash, Monitor, Edit, Write, NotebookEdit, Read and Grep, and the member definition listed no MCP tool: a call of an MCP tool was not judged, so a read-only (review) run that reached a tool pack could have called a write tool. The hook matcher now also covers mcp__.*, and the guard judges a call by the policy the tool packs of the role give that tool. It refuses a tool that is in no tool pack linked to the role, in every run; a tool whose pack is missing, changed since its approval or fails its review; an always_ask tool, since a member runs unattended; a name that stands for two tools of the role; and, in a read-only run, every tool that is not a declared read (always_allow, a name that is not write-like and, where the pack was approved with the server's tool listing, readOnlyHint: true). The role is the one of the run, as the lead's registry of local runs records it; the run is found from the member mark of qargi local run or from the directory of the call. qargi agent add and agent refresh write into a member's tool list the tools of the packs that reach its roles, under the names the runtime gives them (mcp__kb__platform_list_items), without the always_ask ones and, for a member of read-only roles alone, the declared reads only. The hook command of such a member names its project and roles (qargi __guard --project P --roles build,review), because an MCP call names no path and a member a session starts works outside its run directory: a call made where no run is found must be allowed for every one of those roles, each held to its class, and with neither a run nor those names the call is refused. Every member is STALE after this release, since the hook matcher changed, and again whenever a link, an unlink or a seal changes the tool packs of its roles: qargi agent refresh NAME --project P renders it. A runtime other than Claude Code is judged by the names its definition gives (resources.mcp.permission_rule and name_key), and one that gives none has no MCP calls to judge.

qargi 0.2.10

Choose a tag to compare

@github-actions github-actions released this 10 Nov 22:58

Added

  • qargi local report RUN [--findings] [--from FILE] [--force]: a read-only member's own way to put its report into OUT when its runtime refuses the file tool for report.md. The member writes the text under another name in OUT with its file tool and runs the verb with --from NAME, or gives it on standard input; the verb writes OUT/report.md (--findings: OUT/findings.md, which review check does not read as a report) and, when OUT has none, a minimal manifest.json that names it. It reads only standard input or a regular file under OUT (a link, a path that leaves OUT, a file over one megabyte and an empty report are refused), writes only in the OUT of a read-only run that the lead's registry names, keeps a report that is there unless --force, refuses a build run and, with the member mark set, any run but the member's own. It is not review save (the lead's, which stores a final message): the guard needs no change, because local report is on none of its lists of the lead's verbs. The member contract says to use it when the file tool is refused for report.md.
  • The CI scan of qargi release verify can be told which jobs are not unmatched. A gate may carry ci_jobs, a list of CI job names or ids that run its check under another name: the scan reports each as a note, covered by gate NAME, and does not warn. The new answer ci.not_gates is a list of job names or ids that are aggregate or publish jobs and not gates; the scan leaves them out. A name listed in either that no workflow has is a warning, unless a workflow or a job name could not be read. --strict counts neither covered nor listed jobs. project validate and project set check both lists (non-empty names, each once).
  • A feature gate may carry covered_by: NAME, NAME being a release-tier gate whose command runs the same check. qargi release verify lists the gate as covered by NAME and does not run it; its result in the run record and the gates: line is covered by NAME when NAME passed, and not run when NAME failed or was not reached. qargi feature verify still runs it. project validate and project set refuse a covered_by that names no release gate (or that is on a release gate), naming the entry.
  • qargi project adopt NAME --show prints, with the plan, everything the plan token covers: the refs it starts from, the branches to create, and the full content of each file to write (the sha256 in the basis is that of the content shown). It changes nothing and takes neither --plan nor --approved.
  • qargi project adopt lists the repository's own agent instructions (CLAUDE.md, AGENTS.md) and what it does about each, in the plan (instructions in the plan JSON): it edits neither; with a CLAUDE.md and no AGENTS.md it writes an AGENTS.md that holds the Development environment section only, and says that the @AGENTS.md import line in CLAUDE.md is for the owner to add.
  • The gitflow scaffold has the parameters unreleased_heading and changelog_entry.
  • The answer methodology_accepted, a list of {gap, reason, date} (the gap written as attach prints it, for example file: CONTRIBUTING.md), records the deviations from the methodology the lead accepts. The lead sets it with qargi project set; it is validated, and project set refuses it in a member run. project attach, project adopt and marshal handoff (a == methodology deviations section, and deviations in --json) print the accepted deviations apart from the open gaps; adopt writes no accepted file and creates no accepted branch; an accepted deviation whose gap is gone reads "accepted, no longer applies". (The key is top-level because methodology is a plain string.)
  • qargi release plan VERSION [--hotfix] [--date D] [--keep] [--allow-empty-notes] [--again]: one plan and one approval for the whole release cut. It prints every step with what moves (start, push when the repository has an origin, finish, site when site.enabled) and ONE plan token (publish is not in the cut: it has its own plan); the start step is planned by release start itself, so its refusals come before a token does. release plan VERSION --plan TOKEN --approved "<words>" records the approval once (the words, the plan, the date, an audit line release:cut) in STATE/release/PROJECT-VERSION.cut.json, lead only. Then release start, push, finish and site each take --plan TOKEN alone: the words are the record's and are on that step's audit line, every step records that it ran (plan again and release status show the steps and the next command, so a cut resumes after a refusal or a death), and each step runs only while what the plan fixed holds: start finds its own plan as it was frozen, push the branch where start left it, finish the branch where push left it (the head the checks ran on) with master, the second target and --keep as planned, site the tag on the commit finish tagged. release publish refuses the cut's token (run it with its own plan and approval): the notes and the release files it creates, whose hashes exist only after the tag, are shown by its own plan. A step under the cut is the lead's as approving it is, and is refused in a member run and when QARGI_NESTED is set; a --plan that is not the recorded cut's token, without --approved, is refused naming that a cut is recorded (it does not print a plan of its own). A step that no longer matches is refused before anything moves and is run with its own plan and approval, which the cut records; the single verbs and their own tokens work as before. finish stays the push and tag gate: the approval words are the owner's, and --ignore-checks WORDS at the call is recorded as before. The qargi-flow skill, docs/flow/git-flow.md, the help and the manual page describe it.
  • qargi project unset NAME PATH removes one answer of a project. It refuses what project set refuses (the marshal backend; models.extensions and models.accept_no_barrier, also inside a whole models object, with exit 2 and the verb that takes a plan token), refuses a key that is not set, refuses the removal of an answer the project needs (validate runs first and the file is not touched), and is refused in a member run (the run directory or the member mark in the environment) with exit 2.
  • The answers backlog.section (a heading of the backlog file: only the list under it, up to the next heading of the same or a higher level, holds items) and backlog.archive (a directory that receives NAME-archive-YYYY-MM.md, or a path ending in .md for one archive file; by default the archive is beside the file) shape qargi backlog list and done. A heading the file lacks is an error. lib/backlog.py takes them as --section and --archive.
  • qargi backlog add ID "text" [--detail "..."] appends the item - [ID] text (the detail as indented lines) at the end of the list, of the backlog.section list when that is set, with an audit line. It refuses an id that is not a slug and an id already used anywhere in the file, and then leaves the file alone. qargi backlog done ID [ID...] --evidence TEXT takes several ids as one batch: one plan, one token, the same evidence archived with each id, applied together (the archive is written once, then the backlog); a missing, ambiguous or repeated id refuses the whole batch before a plan is printed. Both are the lead's, as done was: the guard refuses every backlog verb but list.
  • The answer review.reject_trailers, a list of trailer keys (letters, digits and hyphens, * for any run of them) refused besides the built-in Co-Authored-By and Generated-with under review.trailer_policy reject; project validate refuses it while the policy is not reject, and a member's guard refuses setting it. The scrub verdict for a rejected trailer names the commit, the line of its message and what matched (review.trailer_policy is reject or review.reject_trailers names KEY), and an entry that is no trailer key fails the scrub. The scrub reads the list from the run's copy of the answers in the lead's registry, as it does review.scrub.
  • qargi doctor has a project-env check: last env check <verdict> at <time> from the record qargi env check keeps in state/env/PROJECT.json (a warning when the verdict is not ENV OK), or unknown never run when none was recorded. It only reads the record and never runs the project's code; the help of doctor and of env check name each other.
  • qargi memory init repo --worktree DIR (and memory move with the repo tier at one end) writes the repo tier into DIR, another worktree of the project's repository, at the same path inside it, instead of the main checkout. DIR must be the top of a work tree of that repository; the option is refused for any other tier and for a move that has no repo tier. The plan (and its token) are of that worktree.
  • Tab completion of qargi project set offers the answers ci.not_gates, methodology_accepted, review.reject_trailers, backlog.archive and backlog.section.
  • qargi doctor has a tools check: flock (the locks of the finishing verbs and the ref journal) and timeout (the bound on a question put to a remote) must be on PATH, and a missing one warns, naming what it is for and the package that has it. flock and timeout are in the Makefile TOOLS list (make doctor has a line for each), in the bootstrap lines (util-linux, coreutils) and in the tools AGENTS.md lists.
  • The answer release.hotfix_preflight, a command line or a command as a list of words ({version} replaced, QARGI_RELEASE_VERSION set): qargi release push runs it for a hotfix, in a copy of the branch before anything is pushed, instead of release.preflight, for a project whose stored preflight needs content only the fix has or does not suit a hotfix. The plans of release start --hotfix and release push name the answer that runs. A release keeps `release.p...
Read more

qargi 0.2.9

Choose a tag to compare

@github-actions github-actions released this 10 Nov 08:48

Added

  • qargi local prepare --task-file FILE --done COMMAND [--scope PATH]... writes the whole task block to RUN/task.md for every runtime (before, only a command runtime had one, and the lead assembled it by hand): ROLE with its class, ABILITY (the role's text), TASK (the file), DONE, PROFILE (the text of the repository's profile, ~/.qargi/config/profiles/<repository>.md, when there is one), BASE, BASE_SHA, one SCOPE: line (the paths comma separated, or none), the run's paths, and SUBMODULES and MEMORY when there are any; prepare prints TASK_FILE=RUN/task.md. It refuses, and makes no run, a task file that is empty, a link, not a regular file or over 1 MiB, or has a line of its own that starts SCOPE:; a DONE that is plainly not a command line (empty, several lines, or text bash -n cannot parse; for a simple command with no operator, a first word, after NAME=value words, that is not an executable, a shell builtin or keyword, or a script: a path is looked up at BASE in the object store, not in the lead's checkout, and a file under a --scope path counts because the task may create it; a line with ;, &&, ||, |, (, $( or a redirection is accepted when bash parses it, so only its syntax is checked, which lets the isolated-suite command line T=$(mktemp -d -p /dev/shm); mkdir $T/h; HOME=$T/h bash tests/run.sh through); a missing --done; and a --scope that is absolute, empty or holds a space, a comma or ... Without the flags prepare is unchanged. tests/test_taskblock.sh compares the block of a build run and of a review run with tests/golden/taskblock-build.md and taskblock-review.md
  • qargi review check fails a patch series that touches a path outside SCOPE: the SCOPE: line of the run's task block (RUN/task.md, which a member cannot write) when it records one, else the manifest's scope[]; a rename's new name and the manifest's touched_paths[] count too. The contract tells a member to compare git diff --name-only BASE_SHA with SCOPE before each commit, to report BLOCKED naming the path when DONE needs a change outside it, and to put anything else under noticed_not_acted_on; the "Needs from the lead" instruction is gone. contract/output-format.md names the manifest's required keys (status, base, done, gates, commits, scope, touched_paths) and contract/manifest.example.json shows a complete manifest. The help of qargi review check, the manual page and the review role describe the rule as it is: a report-only run needs its manifest.json and a report.md whose last non-empty line is verdict: accept or verdict: revise, a patch outside SCOPE fails, and commits dated within two minutes of each other warn
  • qargi review save RUN [--force]: stores the final message of a read-only run (standard input) as RUN/out/report.md, and writes a minimal manifest.json (status, base, done, gates, commits: [], touched_paths, the report in artifacts) when OUT has none. It is the report path with no shell text: a reviewer whose file tool is refused returns its report as its final message and the lead stores it, instead of a heredoc that the guard reads line by line. It refuses a directory that is not a run of the lead's registry, a run that is not read-only, a run directory or OUT that is a link, an empty message and an existing report.md (without --force); qargi review check RUN/out then decides whether the report has the required form. The verb is the lead's: the guard refuses it from a member, as it refuses review apply.
  • docs/development/architecture.md: where each kind of record lives (configuration and state), the layers of the code, the life of a local run, what the guard advises and what the checks verify, and which parts are data and which are per platform. docs/development/README.md says in a sentence what each of its links is for.

Changed

  • qargi __guard, the hook command a member runs before every tool call, loads lib/guard.py as an imported module instead of running it as a script, so python reuses its compiled form (a script is compiled on every run: the 235 KB guard took more than half of each call). A harmless ls call takes a median of 48 ms instead of 105 ms, a Write call 60 ms instead of 118 ms (20 runs, minimal environment). The exit status, standard output and standard error, the options and the refusal messages are unchanged. install.sh --user compiles the tool's lib/ as you (compileall), and qargi upgrade compiles the root-owned checkout through QARGI_SUDO before it relinks; nothing is written outside the tool's own __pycache__ directories. A tool you cannot write to and that nobody compiled (a first sudo git clone install) works as before, only slower: install.sh --user says so and prints the one command that compiles it.
  • tests/startup-budget.txt has a ceiling for qargi __guard (120 ms, the median of 20 runs on a harmless call in a compiled tool, about twice the 48-60 ms measured), and unlike the role list ceiling, which only warns, it fails: a line marked enforced fails the test when the median is over its ceiling in three measurements in a row, so a loaded machine does not.
  • paths.installed_tool_root() (asked dozens of times in one guarded call) reads install.json again only when the file changed (inode, size, modification and change time), not on every call; what it returns is unchanged, and a record that is rewritten, replaced or removed, or a configuration directory that moves, is seen at once. 28 calls take 0.4 ms instead of 1.2 ms.
  • The build and write role texts point at the reference store only when the task block has a MEMORY section or a store is mounted (a local member has none; qargi role prompt prints these texts to it), and the contract says a member without one follows the task block, the contract and the repository. The six-step order of work (orient, baseline, implement, check, commit as you go, report) is a numbered list in the contract, which every member loads, and no longer only in the build seed's workflow note
  • The advisor sign-off of the qargi-flow skill is a run: write the combined diff to a scratch file, prepare a review run with qargi local prepare --role review --profile advisor, dispatch it with a full task block (TASK, the diff path, and as its DONE a command, qargi review check ../out, which the member runs from its clone: a sentence is not a DONE, local prepare refuses it), take it in with qargi review check OUT and close it with qargi local clean; before, the advisor was spawned on a bare file with no task block and no output directory
  • One review vocabulary. A read-only role returns report.md and manifest.json: # Findings (each finding with severity high, medium or low, file:line, what is wrong, the evidence, a suggested fix as a fenced diff), then # Checked (what was run with its exit status; what was only read), and a last line verdict: accept or verdict: revise. The output format, the review role, the review-findings pack and the review role seed's findings format and workflow use these words; CONFIRMED, PLAUSIBLE and "not ready, ready with changes, ready" are gone. qargi review check now fails a report-only run without its manifest.json (PREFIXmanifest.json beside PREFIXreport.md) or whose last non-empty line is not the verdict line, and warns when there is no # Checked section
  • The member contract says to record each check in log.txt when it runs (command, exit status, last lines), to report only what was run in this run, and that a check reasoned about but not run is not run with the reason, never pass; and to commit as you go, each change once its checks pass, a series committed all at once at the end being sent back. qargi review check raises its note on commits all dated within two minutes to a warning
  • The member contract no longer says "never stash: use a WIP commit", which collided with the rules against rewriting history and for commits that pass alone. It says to check BASE out beside the work with git worktree add to see how BASE behaves, to git restore an experiment, and that a one-task member has nothing to set aside; the gitflow-member and qargi-contributing packs drop their copies
  • The member contract says once how to write a report and a commit message: with the file-writing tool, never with shell text (echo, heredoc), and a commit with git commit -F FILE; a read-only role whose file tool is refused puts the whole report in its final message, and a guard refusal is reported, not rephrased. The shell-heredoc fallback and the copies of the commit-message rule are gone from the contract, the gitflow-member and qargi-contributing packs, the Claude Code addendum and its member-out note; qargi-verify tells the lead to store such a report with qargi review save
  • The qargi-task skill has the lead write the task to a file and call qargi local prepare ... --task-file F --done CMD [--scope PATH]..., then hand the member the path of RUN/task.md ("read it and follow it", with CLONE, OUT, GIT and BASE_SHA when the runtime needs them); the list of sections to assemble by hand and the step that pastes prepare's output into the block are gone. qargi-verify compares the manifest's done.command and the DONE check it re-runs with the DONE of RUN/task.md, and review check applies its SCOPE
  • make bootstrap installs and make doctor checks ssh-keygen (OpenSSH: openssh on Arch, openssh-client on Debian and Ubuntu, openssh-clients on Fedora), which the marshal's signed records and the test suite need; AGENTS.md lists it
  • A plan token is checked by default. need_plan (lib/lib.sh) no longer reads QARGI_NESTED: it always compares the token, so a verb that forgot to clear the variable can no longer skip it. need_plan_nested is the one variant that honours the variable, and qargi canon apply (which provisioning runs ne...
Read more

qargi 0.2.8

Choose a tag to compare

@rett rett released this 10 Nov 08:17

Fixed

  • qargi local clean RUN also removes the saved scrub record of the run (scrub_ok/RUN-ID in the state, written by qargi review check), so a run that was checked and never applied leaves no record behind.
  • qargi release step tells a failed write of the step record in plain words instead of printing raw jq text: when another command marked the step first, it says so and to run again with --again; otherwise it says the record cannot be written and to check that it and its directory can be written and that the record is valid JSON (commands that write the record wait for each other, so another command is not named as the cause). The exit status is unchanged.

Changed

  • qargi project attach and qargi project show name the declared gates where they showed only the test command: attach lists them (tier, name, command, feature tier first) after the test command: line of a stored project and says they replace it; project show keeps the answers as JSON on standard output and prints the same list on standard error. Without gates nothing changes.
  • qargi marshal handoff lists the declared gates in a == gates section after the features (tier, name, command; they replace test_command), and --json carries them as gates (an empty list without gates). Without gates the note is as before. qargi help and the manual page describe this output, that of project show and project attach above, and the --param gates=JSON of project new.
  • The gitflow scaffold (qargi methodology files, and the files qargi project adopt writes) renders the declared gates where it rendered only {{test_command}}: README.md, AGENTS.md, CLAUDE.md and CONTRIBUTING.md list them (name, tier, command) and .github/workflows/test.yml has one job for each gate, named after it (the name is a YAML single-quoted scalar, so the scan reads any name back; a feature-tier gate's job runs on every pull request, a release-tier gate's job only on a pull request from a release/ or hotfix/ branch or when started by hand; the jobs run in parallel while verify runs the gates in order, and CONTRIBUTING.md says so), so the CI scan of release verify finds every job matched. The markdown files write a name and a command as inline code in a fence of backticks longer than any run in them ({{gate_name_code}}, {{gate_command_code}}), so a backtick in a gate does not end the span. The new optional methodology parameter gates (one line of JSON) carries them; a template marks its parts with {{#gates}}, {{^gates}} and a {{#gate}} loop, each alone on its line. qargi project new with --url and --param gates=JSON stores the same gates in the project's answers, so the files, feature verify and the CI scan agree (without --url nothing is stored, as for every answer). A gate name or command, and any methodology parameter, may not hold a character that ends a line (U+2028, U+2029, U+0085, a vertical tab and the like, besides a newline), which would break the YAML and the markdown list. Without gates the files are byte for byte what they were.
  • The qargi-flow skill and the gitflow-member pack say that a project may declare gates, that feature verify and release verify run them in order and stop at the first failure, and that a member runs only the checks of its own change (the pack's line about the checks of the touched paths calls them the PROFILE checks, so the word gates means only the declared ones). Their word ceilings in tests/skill-budget.txt are raised by 35 and 22 words, with the reason on the line.
  • Every plan-token check except canon apply's now ignores QARGI_NESTED: the checks of branch prune, credential setup lead-push, the marshal owner verbs, project new, attach and adopt, provider accept, provision apply and the cloud instances verbs (agent update, normalize, refresh-prompt) are written QARGI_NESTED='' need_plan, as those of release, local, feature, upstream and backlog already were, so a nested environment can no longer apply a plan without its token. The verbs that clear the variable themselves (memory, tier writes, packs, project link) are unchanged, and canon apply still honours it because provisioning runs it that way. A new test, tests/test_nested_plan.sh, covers one verb of each file and checks statically that every need_plan call in lib/ is prefixed or on a short list with its reason (canon apply, and the verbs that unset the variable first).

qargi 0.2.7

Choose a tag to compare

@rett rett released this 10 Nov 08:17

Added

  • qargi doctor [PROJECT] [--json] [--strict]: a read-only check of the host, first slice. One line per check, ok|warn|unknown name: detail, and under a warning the lines that say
    what you can do (nothing is run for you). The exit status is 0 whatever it finds, or 1 with --strict when a check warns. Four checks: remove-ipc warns when logind has
    RemoveIPC on (read with busctl, QARGI_BUSCTL) and the user has no linger (loginctl, QARGI_LOGINCTL), because logind then removes what lives in /dev/shm when the last
    session ends, and prints the drop-in /etc/systemd/logind.conf.d/remove-ipc.conf or loginctl enable-linger; guard warns when qargi __guard is not on PATH or does not
    answer as the guard (and when a member of the project is missing, stale or edited); shm warns on less than 10% free under /dev/shm (QARGI_RUN_TMP) or on an orphaned qargi-run.*
    directory; window-source names where the context-window table of the project's runtime comes from and warns on a model id of the project's roles that is not in it. Without
    systemd or a tool a check says unknown. A member may run it. The orphan listing of qargi local clean --orphans moved to lib/orphans.sh so both read the same code; clean --orphans prints what it printed.
  • qargi backlog: a generic backlog kept in a markdown file that the new project answer backlog.file names (a path, ~/..., or relative to the repository; it may be outside the
    repository, a memory store for instance; project set and project validate check it). An item is a top-level list line - [ID] text (ID a lowercase slug of two characters or
    more) with its indented lines; any other top-level list line is listed as unnumbered. qargi backlog list [--json] shows the open items and flags a duplicated id.
    qargi backlog done ID --evidence TEXT prints a plan with a token, and with --plan and --approved appends the item, the date and the evidence to NAME-archive-YYYY-MM.md
    beside the backlog file (created when absent) and removes it from the backlog, with an audit log line. The plans of feature finish and release finish add the line
    backlog items named in these commits: ID, ... for the open items whose id the merged commit messages name (information only, outside the token; nothing when the answer is
    unset or the file is missing). A member's guard refuses backlog done and project set of backlog.file; backlog list only reads.
  • Declared gates: the project answer gates is an ordered list of {name, command, tier} (command a command line or a list of words, as test_command; tier feature or
    release; names unique and non-empty). qargi project set and project validate check it and name the entry that is wrong. qargi feature verify then runs the gates of tier
    feature in order and stops at the first failure; qargi release verify runs the feature gates, then the release gates. Declared gates replace test_command and
    release.test_command (a note says when one of them is not the command of a gate); the verdict line is still full suite: pass|fail at SHA, so feature finish and the
    release steps work as before. The run record lists each gate with its result (pass, fail, not run), feature status and release status show a gates: line, and a failed
    run names the failing gate. Without the answer nothing changes. A member's project set of gates is refused by the guard, as it is for test_command.
  • qargi release verify reads .github/workflows/*.yml and *.yaml of the tree it verifies (standard library only, no YAML dependency) and warns on standard error, naming each
    CI job whose name and id match no declared gate name; a job whose name cannot be read (an expression, a block scalar, a flow mapping) is a note and never a failure.
    release verify --strict refuses, before anything runs, on any such job. With no workflow it prints nothing new; with workflows and no gate declared every job is named.
  • A test (tests/test_release_unrelated.sh) covers the fallback line of release finish --hotfix when the hotfix branch has no merge base with develop but develop and master are
    related (master merges two histories): finish refuses with a line about the branch and develop only, without the advice for an unrelated develop and master.

Changed

  • docs/flow/git-flow.md says that qargi review apply runs the take-in scrub too, unless a passing review check recorded the same series (then it prints
    scrub: skipped (review check passed it for this series)), and names qargi local clean --orphans beside the other local clean forms.

Fixed

  • QARGI_NESTED no longer skips the plan token of qargi feature finish, qargi feature merge into a detached branch and qargi upstream sync: as for the release verbs and
    local clean --orphans, a caller that is itself approved has not approved this plan, so the token is always needed. Tests cover all three.
  • qargi backlog done needs its plan token under QARGI_NESTED too, as the verbs above: with the variable set it archived and removed the item with no --plan or with a wrong
    one. Tests cover both and check that the backlog and the archive stay as they were.
  • qargi marshal handoff judges the whole-suite record of a project with declared gates by the gates: the record's command is then gates: NAME=COMMAND; ..., which the note compared
    with test_command, so every feature showed as "another test command" even right after a passing feature verify at the tip. The identity is now built in one place,
    lib/gates.py (gates.py identity ANSWERS TIER..., which feature verify and the note both use), and the help of marshal handoff says what makes a run apply. Tests cover a
    gated project with a run at the tip and with a changed gate.
  • qargi doctor changes nothing on the host, as it says. It ran lib/orphans.sh and qargi agent list, and loading lib/lib.sh made the config and state directories, set them
    to mode 700 (and ~/.qargi), and created instances.json, sessions.json and agents.json: a host that had never run qargi got them from its first doctor. lib/lib.sh now skips
    that setup when DA_NOSETUP is set; the orphan scan sets it when run as a program, and doctor sets it for agent list and skips the member check when agents.json
    does not exist (see the next entry). The test starts with the data directories absent, and snapshots them, HOME and the directories of a host that has them (types, modes, sizes, times) before and
    after.
  • qargi doctor PROJECT on a host with no members file says ok ... no member is recorded on this host, so there is none to be stale in its guard check. It said unknown ... the members of project PROJECT could not be read (qargi agent list), which read like a failure; unknown stays for a qargi agent list that fails.
  • The gate results of a whole-suite run are kept per run, beside its log (STATE/suite-logs/PROJECT.feature-NAME.gates.jsonl, PROJECT.release-VERSION.gates.jsonl), and not in one
    file per project: two verifies at once truncated and appended to the same file, so a record could list a gate twice or the gates of the other run. The verdict line was never
    affected. A test runs two verifies at once and checks each record. Two verifies of the same feature still share a log and a results file (the warning about other runs stays).
  • The member guard no longer refuses a script written with a heredoc because of what its text mentions. A body that cat, tee, grep or git commit receives is
    now data in a line with other commands too (cat > run.sh <<'E' ... E then chmod +x run.sh), as it already was when every command of the line was one of those: a body
    with project set $X in a sentence was refused as the project verb with an expansion. The other commands of the line must only look at files, make, move or remove
    them (chmod, mkdir, ls, mv, cp, rm ...): a shell, make, source, a script, an alias or an export in the line keeps the plain reading, since the file could be
    run there. The lines of such a body that start a lead-only verb (qargi ..., also behind sudo, env, a shell, a keyword, a redirection or a continuation, the word
    project, QARGI_LEAD, QARGI_NESTED, QARGI_MEMBER_RUN, marshal_, $'..') stay refused, and so do the lines an unquoted delimiter lets the shell expand. A body a
    shell reads (bash <<E, source /dev/stdin) or a pipe carries on (cat <<E | sh) is read whole. Tests cover the data, the nearest real violations and the linear time.
  • The member guard no longer reads a parameter expansion with a subscript or an operator as a glob on the signing key: ${PIPESTATUS[0]}, ${arr[@]+...},
    "${files[@]}", ${!a[@]} and ${#a[@]} (so a loop over an array next to the word marshal, which was refused as a read of credentials/marshal_*) are allowed.
    A name that starts with marshal_, a glob in a default word (${X:-*}) and a recursive reader still are refused.
  • The member guard reads the elements of an array literal as it reads the words of for x in WORDS: a glob inside NAME=(...), NAME+=(...) or
    declare|typeset|local|readonly [-aA] NAME=(...) (also [k]= entries, and a relative glob after a cd into the credentials directory) that reaches
    credentials/marshal_* is refused when the array literal names the credentials directory, in plain words or through a variable the line sets (the entry below). Trimming
    the subscript off ${a[@]} (the entry above) had let that through. A word with a variable after the path of the credentials directory (credentials/${X:-ma}rshal_ed25519,
    credentials/m${X}shal_ed25519, cd credentials; cat $X) is refused too, as the name the variable builds could be the key; the entry below widens this to a glob in the path.
  • The member guard reads the signing key through a name it did n...
Read more

qargi 0.2.6

Choose a tag to compare

@rett rett released this 10 Nov 08:17

Added

  • A test (tests/test_release_snapshot_cycle.sh) runs a whole release through plan tokens on a repository whose master is an unrelated snapshot commit, with a local bare
    origin that refuses non-fast-forward pushes: release start, push (which joins the history of master) and finish merge and tag master, merge develop, push
    by fast-forward only, and leave every earlier commit reachable. It found no bug.
  • qargi local clean --orphans [--days N] lists the qargi-run.XXXXXX directories under /dev/shm (or QARGI_RUN_TMP) that no run of the lead's registry names, each
    with its age and whether it is named (a run's own directory, live or gone, is named; a link and a file are named and left). It prints a plan and a token and removes
    nothing; --plan TOKEN --approved WORDS removes only the unnamed directories older than N days (default 2, at least 1) with nothing newer inside, each checked again
    just before it goes, and logs the removal. The existing --stale DAYS --scratch DIR (finished runs) is unchanged.

Changed

  • qargi project adopt names the next step when two long-lived branches share no history (a master made as a snapshot, say), not only the problem: the first release
    joins them (qargi release start VERSION, then release push VERSION, whose plan joins the history of the first branch into the release branch, then release finish VERSION), or one merge by hand (git switch develop && git merge -s ours --allow-unrelated-histories master, which keeps develop's tree). Adopt still changes no branch.
  • qargi release start --hotfix (its plan, and release status) and the refusal of release finish --hotfix say what to do when develop shares no history with master
    and no release is open: the hotfix could not be merged into develop. The line names the way out, the next release (qargi release start VERSION, push and
    finish: the push joins the history of master) or one merge on develop by hand (git switch develop && git merge -s ours --allow-unrelated-histories master), then
    qargi release finish VERSION --hotfix. It was a refusal at finish that only said to join by hand.
  • qargi review apply runs the declared take-in scrub (the answers review.scrub and review.trailer_policy) before it makes anything, as review check does, so a lead
    cannot apply a series the scrub refuses: a failing scrub prints scrub: FAIL ..., refuses the apply and changes nothing. It skips the scrub, with the one line
    scrub: skipped (review check passed it for this series), when a passing review check recorded the same series: review check writes, in the lead's state directory
    (scrub_ok/RUN-ID, not in the member's run directory), a hash of the patches in order and of the declared scrub, and apply compares it; a series (or a scrub answer)
    changed since the check is scrubbed again, and a check that failed leaves no record. --from-saved follows the same rule. A member's apply does not run the scrub
    (scrub: not run), as its check does not.
  • qargi local clean and qargi local close --applied-by-am, when they refuse a run whose applied branch the lead rewrote by hand (the branch is not at the tip that
    review apply journaled, the two trees are the same and no rewrite is recorded), also name the fix with the real branch and tips: git branch -f BRANCH OLD, then
    qargi ref record --rewrite BRANCH OLD NEW --why .... The other refusals keep their text.
  • Where the runtime refuses a member's write tool for a report file, OUT never gets the report. The contract and the runtime's addendum now say to write OUT files
    with the shell (a heredoc) then, the runtime's notes have a member-out topic, and qargi local verify (an uncollected run, or a read-only run whose OUT has no
    report, as a note) and qargi local clean (an OUT with no manifest and no report) name that cause. A test shows the guard lets a build member write OUT files with
    a heredoc.

Fixed

  • The session-start hook's read of the open features (qargi marshal status --all --brief --no-fetch, 5 s) takes QARGI_HOOK_MARSHAL_SECS, a whole number of seconds from 1
    to 9999 (anything else is ignored; the default stays 5 and the hook stays bounded). tests/test_marshal_refs.sh failed once in a parallel suite run because that call,
    which takes 1.3 s alone on its hostile-records fixture, took 4 to 6 s under load: the read was cut off, the marshal block was empty, and with no tier files so was the
    context. The test now gives the hook 60 s and, when the hook's output is wrong, prints how long the status call took.
  • The guard refuses input it cannot decode. A hook input that is not valid UTF-8 was taken for "not JSON" and let through; it is now refused (exit 2, one line on
    stderr). Input that decodes but is not JSON is still no call to judge and is allowed, as before.
  • qargi lead grant and qargi lead revoke read each step's plan text and token from the step's --json output (the plan and token fields) and no longer parse the token out of text with sed. The steps run through bin/qargi; what is shown and applied is as before.
  • qargi project set adds "(applies to the next provision ...)" only for the answers a provision reads (the list is provision_reads in lib/project.sh: repo.url and repo.long_lived, teams, roles, profiles, budget_cents, memory stores, models.provider, lead model, effort and push, packs, cloud). Every other answer (runs.owned_refs, lead.context_window, release.*, review.*, deployment, test_command, ...) prints only what was set. deployment no longer keeps the note for a value that names a cloud deployment: no provision reads it.
  • qargi release start VERSION [--hotfix] is behind a plan token like release push, finish, fix, publish and step. Without --approved it changes nothing:
    it prints the plan (the branch, the base it is cut from and that base's head, the one commit that bumps the version, the files the commit changes, the date)
    and a token. --plan TOKEN --approved "..." creates the branch and writes the release:start line of the audit log, which start never wrote. The checks
    (the suite verdict, the preflight, the changelog and man page) run for the plan too, so a refusal comes before a token does; the token covers the head of
    the base and the date. The apply line it prints repeats --date and --allow-empty-notes. The tests, the skill, the manual page and the docs that start a release pass a plan.
  • Two qargi release step calls at the same moment (or a step and release finish) no longer lose a step: the after-steps record was written through one
    fixed temporary file with no lock, so the slower call overwrote the other. The record is now read, changed and replaced under a lock (the one the registry of
    local runs uses) through a temporary file of its own name; a step another call marked done in the meantime is refused unless --again.
  • The guard protects scrub_ok/ of the state directory like features/, runs/ and handoff/: a write, redirect, heredoc, cp, mv, tee or ln into it is refused,
    reading stays allowed. The record review check keeps there (the hash of a series that passed its declared scrub, which review apply takes as the scrub done) was
    writable by a member running as the same user, who could compute the hash and make the apply skip the scrub.
  • qargi review apply copies OUT/patches/*.patch once into a directory of its own in the lead's state directory and hashes, scrubs and applies (git am) that copy.
    It read the patches three times from the run directory, so a member process still alive could change a patch after it was scrubbed and before it was applied. A
    patch that stops being a regular file while it is copied refuses the apply; the copy is removed however the verb ends.
  • qargi release start|push|finish|publish|site ... --approved WORDS and qargi local clean --orphans --approved WORDS need the plan token even when QARGI_NESTED is
    set. need_plan returns without a check when that variable is set, so release start 1.2.3 --approved x created the branch, and local clean --orphans --days 2 --approved x removed the directories it listed, with no token. A caller that is itself approved has not approved this plan; the packs, memory and tier-write verbs
    already refused to honour it. The lead-only verbs (assets, sign, site) still refuse a nested call as before.