Skip to content

Releases: PakhomovAlexander/afactory

v0.12.0-rc.2

v0.12.0-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Oct 18:02
6ca82d7

Authority compatibility

Committed .af/ policy keeps working as is relative to 0.12.0-rc.1; no authority migration is required.

Changes

  • chore: pin af 0.12.0-rc.1 in .af/af.lock (#227)

  • Keep a long path's reason on the TUI status line (fixes a test that failed every Task gate) (#233)

  • Report slow tests and suite totals from every nextest run (#218)

  • Run only the self-optimizer's timed phase alone (#222)

  • Wait once, not per case, in looping lease and deadline tests (#204) (#234)

  • Judge the same Task writer at its later recorded time everywhere (#231) (#235)

  • Fix main: check the real nextest config against its own text, not a pinned exclusive list (#236)

  • Give non-timeout tests a load-safe wall so a loaded machine cannot fail them (#206 part 1) (#237)

  • make test now prints where test time goes after every nextest run, passing or failing:
    nextest's wall time and test count, summed test-seconds, achieved parallelism, the time spent
    in the exclusive block that .config/nextest.toml runs alone
    (ADR-0124), failures,
    a duration histogram and the slowest tests. CI also writes it to the step summary. A Task
    gate (scripts/verify.sh) records every make check step's time beside its nextest reports
    and ends its output with the same summary. The JUnit an earlier run left is removed before
    nextest starts, and a JUnit whose testcases do not add up to its declared counts gets a
    warning instead of totals; the report never changes the test step's exit status. It runs
    under Python 3.9 and reads .config/nextest.toml without tomllib. scripts/test-time-report.py compare BASE_JUNIT... -- HEAD_JUNIT... compares runs
    by their per-side medians and lists per-test changes of at least 1 s and added or removed
    tests, as a Markdown table for a pull request description. --base-nextest-config PATH and
    --head-nextest-config PATH give each side its own nextest config (each defaults to
    --nextest-config, which still sets both), so each side's exclusive block counts exactly the
    tests its own config runs alone, and the output names both configs when they differ.

  • The self-optimizer light-strategy test no longer holds every test thread for its whole run
    (#203, ADR-0124). It
    is split into two tests in crates/af/tests/it/self_optimizer.rs. Each builds its own
    repository and Store with the same af commands through shared helpers. Of the two, only
    light_cache_candidate_measures_real_latency_and_grounds_later_adoption_evidence stays in
    the exclusive override of .config/nextest.toml. It first runs, delivers and adopts the
    light candidate without asserting on it, then runs every phase from line 719 to line 1277
    of the original test in order: the cache latency comparison (8 trials, 3 s baseline) with its
    replay, delivery, adoption and ordinary cache-consuming Task (719-1090), the unknown-toolchain
    comparison and refused delivery (1092-1228), and the light adoption observed with the cache
    Task as evidence (1230-1277).
    light_strategy_generates_one_candidate_without_exposing_source_to_author_workers runs in
    parallel and keeps every other phase in the original order on one repository: plan review
    without source (186-259), the approved five-Attempt run and its replay (260-376), delivery and
    adoption observations (378-530), source invalidation (532-571), the uneconomic recommendation
    (573-669), the unexecuted binding (671-717) and, after the same cache configuration and
    toolchain removal, the unsupported recipe (1279-1322). Every assertion of the original is in
    exactly one of the two, unchanged. The original repeated its git exit-status checks inline;
    they now live in one commit helper, so both tests check the setup commits they both make.
    No sleep, trial count, budget, retry or slow-timeout changed. The previous seven-test split
    had dropped the original's restore of the baseline worker without its sleep before the
    unknown-toolchain comparison. That comparison slept 3 s on every baseline trial and took
    20.2 s instead of 8.1 s. remove_cache_toolchain now restores the baseline as main does.

  • Measured with temporary per-phase timers, since removed. Each test ran alone under
    cargo nextest run --profile ci on a 14-core macOS host after one warm-up launch of af.
    Figures are medians of three runs unless marked otherwise.

    • Main's single test took 60.3 s. Setup up to the adopted light candidate took 8.8 s:
      fixture 0.2, plan review 4.0, approval and run 3.5, delivery and adoption 1.1. Phases that
      read no comparison state took 13.3 s: replay 0.1, adoption observations 0.7,
      invalidation 2.2, uneconomic 5.7, binding 2.2, unsupported recipe 2.5.
    • The two real comparisons took 28.4 s: the cache task run, with its trials and 3 s
      baselines, 20.2 s, and the unknown-toolchain task run 8.1 s. Their configuration, plan
      and signed approval took 5.5 s.
    • The non-timed work that only reads the comparisons' state took 3.7 s: cache assertions 0.1,
      replay 0.1, delivery and adoption 1.5, the ordinary Task 1.3, unknown-toolchain assertions
      and refused delivery 0.1, and the light observation with cache evidence 0.6.
    • The previous split's exclusive test took 60.2 s: light setup 9.6, plans 5.5, cache
      comparison 20.5, unknown-toolchain comparison with the sleeps 20.2, reads 4.0. Its seven
      moved tests, each run once alone, took 37.0 s together: 10.7, 7.8, 6.0, 5.7, 2.4, 2.2 and
      2.2 s.
    • In the chosen pair, the exclusive test takes 45.5 s: light setup 9.6, cache plan 2.4,
      cache comparison 19.7, its reads 2.8, unknown-toolchain plan 2.5, its comparison 7.6, its
      reads 0.7. The parallel test takes 19.8 s.
  • The decision uses the expected wall time W(T) = E*(T-1)/T + S/T, where E is the exclusive
    seconds and S the sum of all the light-strategy tests:

    Variant E (s) S (s) W(4) (s) W(7) (s)
    Main's single test 60.3 60.3 60.3 60.3
    Previous seven-test split 60.2 97.2 69.4 65.5
    Chosen pair 45.5 65.3 50.5 48.3

    The pair beats the single test by 9.85 s at T = 4, clearing the 5 s bar, so the split is kept
    in this form.

  • setup_repairs_auth_directory_and_lock_modes_under_a_restrictive_umask stays in the
    exclusive override. Its work takes 0.37 s, but running first and alone it pays the first
    launch of a freshly linked af, which macOS assesses before it runs: 11.2 s measured
    straight after relinking, and every Task gate starts from a fresh build. Outside the override
    that cost lands on whichever parallel test launches af first, under the 15 s provider status
    probe deadline. In a before/after bench (one warm-up, then three alternating full-suite runs
    per side, 7 threads, 14-core macOS host), one of three runs without it failed that test and
    status_keeps_a_default_context_whose_status_probe_failed with "provider status probe timed
    out after 15 seconds"; no run with it failed. Its gain outside was about 1.3 s. The reason
    is written beside the exclusive override.

  • Tests that loop over cases bound by a real lease or deadline now prepare every case, wait once
    on the real clock until every case's lease or deadline has passed, and then run each case,
    which first asserts that its own lease or deadline has passed (issue #204). No production
    timing constant, real clock or assertion changed; tests whose subject is the wait are untouched.
    task_runtime::usage_recovery::worker_and_provider_cas_failure_recover_full_reported_usage_without_another_call
    and incomplete_native_observation_survives_cas_outage_and_both_admission_and_worker_recovery
    went from two real lease waits each to one (about 30.5 s to 15.5 s), and each case now also
    asserts that the Task's absolute deadline has not passed when it recovers.
    task_campaign_review::host::an_expired_review_records_incomplete_without_inventing_unstarted_gate_facts
    went from three real deadline waits to one (5.2 s), and each mode now asserts the exact
    dispatch refusal Task plan deadline expired instead of any error. In review-store,
    recording_resume_rejects_revoked_decision_or_replaced_latest_report first gained exact refusal
    causes for every case: the expired decision and the revoked decision each refuse with the
    approval conflict, pinned to expiry or revocation alone by the decision's state; the replaced
    report's resume refuses for carrying no publication failure; and its stale-report append refuses
    at the expired-pause gate, whose other conditions the preceding refusal proves. A temporary
    variant without the wait then failed: the expired-decision and replaced-report resumes refused
    with Recording recovery requires the exact expired admitted publication pause instead. The
    revoked case refuses the same way with or without the wait, because revocation is checked
    before the deadline, so its own past-deadline assertion is what catches a missing wait. Only
    then were its three deadline waits coalesced into one (9.2 s to 3.2 s). No target was left
    unchanged.

  • af browser: a message on the status line that names a long path, such as the :cd refusal
    <path>: not a directory, keeps its reason visible. The path is shortened from the left with
    ..., whole leading components first and then, when its final component alone is too wide,
    inside that component, instead of the line being cut before the reason. A quoted path with
    spaces is shortened as one path. A message that fits is unchanged (#229).

  • A live Task writer is no longer fenced when its own heartbeat records a renewal between one of
    its operations reading the clock and that operation observing its lease or writing (#231). Each
    place that compares the current writer's clock with the last recorded time now...

Read more

v0.12.0-rc.1

v0.12.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Oct 09:14
5045f1d

Authority compatibility

Committed .af/ policy keeps working as is. Machine files: the remote-checks mapping no longer takes checks (a pipeline's remote_checks chooses), and [storage] is new machine-only configuration (budget-only by default).

Changes

  • release: v0.11.0 (#183)

  • Remote Checks: the pipeline chooses where a check runs (#186)

  • Leave out a logged-out CLI default context once its kind is registered (#182)

  • Bump jsonschema, num-bigint, minisign-verify and minisign; test damaged signatures (#188)

  • build(deps): bump minisign from 0.9.1 to 0.10.0 (#177)

  • build(deps): bump minisign-verify from 0.2.5 to 0.3.0 (#176)

  • build(deps): bump num-bigint from 0.4.8 to 0.5.1 (#175)

  • Bump jsonschema from 0.57.0 to 0.58.4 (#174)

  • Add af task report, and require its block in every pull request (#192)

  • Pin af 0.11.0 in .af/af.lock (#184)

  • Charge zero and record unknown usage when a Provider reports none (#165) (#195)

  • Hold af's disk use to a machine budget (#213)

  • Remote Checks are chosen by the pipeline, not by the machine. A Task pipeline's check node lists
    checks (run on this machine) and remote_checks (run through the check's declared remote
    executor); a project that wants both gates keeps two pipeline variants, and there is no
    per-machine switch. The machine-local mapping ($XDG_CONFIG_HOME/af/remote-checks.toml or
    AF_TASK_REMOTE_CHECK_POLICY_FILE) now names only where this machine may push gate branches
    for a repository: its checks key is gone, and a file that still carries it is refused. A
    pipeline with remote checks cannot be planned without a target, and its plan carries the effect
    publish-gate and the destination github:<owner/name>, which af task plan prints on
    EFFECTS and SEND, so confirming the plan is the consent; the target is read again before
    the check Attempt pushes. A pipeline without remote_checks plans and runs exactly as before
    (ADR-0140, amended).

  • af provider status and the browser's Providers pane no longer list the Claude or Codex CLI's
    default context (claude-ambient, codex-ambient) when it has no login and a Provider of its
    kind is registered; it still shows on a machine with no Provider of that kind yet, and when it
    holds a login (ADR-0141).

  • af task report TASK_ID... summarizes how recorded Tasks ran and what they cost, as one
    Markdown block between <!-- af-task-report:v1 --> and <!-- /af-task-report -->, ready for
    a pull request description, or with --json one af/task-report@1 document
    (schemas/task-report-v1.json). The block leads with one line per pipeline the Tasks ran, its
    steps in dependency order with each step's Worker by Provider kind and model and the gate's
    check names, parallel steps of one role grouped
    (review (bugs, correctness: codex gpt-6-sol/high)), and **unknown pipeline**: not retained once for Tasks whose plan was collected or never made; then one row per Task, each
    a round, with its outcome, its review findings by severity (6 major, 1 minor, none,
    unknown when a round has no complete finding set, gate failed, or — without a review,
    and how many reviewers failed), tokens with thousands separators
    (205,295) and active time, and a Total: row of Attempts (failed), tokens and active time;
    then per round its runs, wall time, failed Attempts by reason class and charged tokens, and per
    node the Worker, Attempts, tokens, elapsed time and check results. Findings are counted once
    each as the round's reduce step recorded them, and review rounds come from the Task's log, so
    a round recorded before af task refresh still counts. Active time sums the Task's runs, the
    leases in which an Attempt started, so waiting between af task runs, a refresh, the recovery
    of a pending Attempt and a resume that only publishes a settled result are not counted; a
    figure the Store does not record is shown as unknown. Each Attempt is charged to the Worker of
    the plan it ran under, so a node that af task refresh moved onto another Worker has one row
    per Worker. It only reads the Store and never prints a Provider label, path, credential,
    prompt or Worker output: a Worker's model is copied only when it is a model identity (at most
    96 characters of letters, digits and ._:+- with at most one /, and no @, URL, drive
    letter or ..) and is unknown otherwise, and every cell encodes \ and | so a value stays
    in its cell. This repository now requires the block in every pull request description: changes
    are made through af Tasks, and the PR report workflow, which runs on pull_request_target
    from the base branch's checker so a pull request can change neither the check nor its
    workflow, refuses a description without exactly one well-formed block — a pipeline line, the
    six round-table columns Round, Task, Outcome, Findings, Tokens and Active in order, at least
    one round row and a last Total: row; placeholder, short and long rows (with or without their
    outer |), a Round, Task or Outcome cell that is empty or holds only an HTML comment, a
    separator row of the wrong width, and a block inside a code fence or indented as code, even
    right after a heading, a thematic break, a fence, an HTML block or a list item, fail —
    apart from Dependabot and release/ pull requests
    (ADR-0142).

  • An Attempt whose Provider reported no usage is charged 0 tokens and its usage is recorded as
    unknown, with its cause, instead of being charged its whole reservation: a Codex Attempt that
    failed with Selected model is at capacity no longer costs 400,000 tokens, nor does one
    recovered after its writer's lease expired while the machine slept. The settlement's
    af/TaskExecutionRecord@5 carries unknown_usage: { cause } (capacity, rate_limit,
    authentication, model_unavailable, network, lease_expired, interrupted or
    unreported); such an Attempt adds nothing to the Task's, node's or verification reserve's
    charged tokens and still counts against the Attempt limits, and Attempts with reported usage
    are charged exactly as before. A Codex Attempt that ends with an error or turn.failed
    event names its classified cause, such as Provider model at capacity (capacity), instead of
    the bare exit status. af task show, af task list, the browser's Tasks and Workers panes and
    af task report show such usage as unknown, never as 0 spend: a Tokens cell reads
    1,200 (+1 unknown), the round's details name the cause, and the JSON documents carry the
    count. A usage observation, even one that reports 0 tokens, makes an Attempt's usage known,
    and af task gc --apply keeps the count in the collected Task's af/TaskCollected@1
    tombstone, so a collected Task still lists and reports (+N unknown). Stores written by earlier releases read as before, with their recorded charges
    (ADR-0143).

  • af now keeps a bounded, visible amount of disk
    (ADR-0144). A machine-only [storage]
    table (20 GiB max_bytes and a 10 GiB min_free_bytes floor by default; AF_STORAGE__<KEY>
    overrides it, and a repository's .af/af.toml cannot) bounds warm build keys, warm Workspaces,
    review campaigns, Task Stores and installed versions, evicting the least recently used entry first
    and never one in use or used within the hour, after every af task run and af review run.
    Age-based collection (keep_days, keep_tasks, keep_campaigns) runs on af storage prune --apply, or after every run with auto_gc = true (off by default), and reaches Stores this
    release cannot read and Stores made with --state, which $XDG_STATE_HOME/af/stores.toml now
    records; every removal opens its target from its configured root through descriptors, never
    following a link and never removing a directory that changed since it was measured, and an
    installed version is checked again under the lock af self takes to change the default or a pin.
    The sweep that ends af task run is recorded on that Task as a storage_sweep observation, shown
    by af task show. Below the floor on any volume af works on, or when one cannot be measured, a
    check reports insufficient_disk, a measurement fails with insufficient_disk without running
    its command, and a Worker Attempt is refused before any token is spent. af storage shows what af
    holds and af storage prune [--apply] reclaims it. A warm check with a native toolchain mapping
    now reuses one key instead of making a new one every Attempt (the key domain moves to
    af.task-build-cache.toolchain/2, so old keys are evicted once). Every check, review gate checks
    included, gets its own empty HOME, TMPDIR, AF_CHECK_SCRATCH and XDG_CACHE_HOME, removed
    after it: write to $AF_CHECK_SCRATCH or $TMPDIR, never /tmp. Provider probes run in a
    directory af makes for them, and af removes each Claude Worker Attempt's and probe's history from
    the Claude config directory (keep_worker_transcripts keeps it). It closes a finished Task's gate
    pull request and deletes its af-gate/ branches (keep_gate_pull_requests keeps them) only while
    they still equal the Task's recorded repository, refs and commits, recording the result as
    gate_cleanup without changing the Task's result, and collects such a Task only after a cleanup
    is done.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 05 Oct 12:38
9ca98b4

Authority compatibility

Committed .af/ policy from v0.10.0 keeps working as is. Remote Checks are opt-in twice: a check's [checks.<name>.remote] table is optional, and without an operator's machine-local mapping every check still runs locally. To move a project pin to v0.11.0, run af onboard --refresh-lock --af 0.11.0. This is a public alpha: compatibility guarantees begin at 1.0; persisted pre-GA Task and Campaign state is unsupported across upgrades, so finish or inspect a Task with the release that started it.

Changes

  • release: v0.10.0 (#154)

  • Add af provider remove, and a d key for it in the Providers pane (#168)

  • Sweep the sandbox directories a killed af process leaves in $TMPDIR (#169)

  • Show TUI state in the brand's colours, as chips (#172)

  • Pin af 0.10.0, and pin every release the workflow publishes (#173)

  • Pin af 0.10.0 in .af/af.lock (#164)

  • Fix executable onboarding and first-run guidance (#156)

  • Open the TUI on a splash of three workers while it loads (#181)

  • Add private-host Provider authentication (stage 1 of #122) (#171)

  • Remote Checks: run a declared Task check in CI through a draft gate pull request (#178)

  • Make the first review walkthrough executable with reviewed, locally committed authority and
    explicit policy, Base and candidate selectors; no remote push or Provider login is needed to
    inspect a plan. Clarify empty Change Sets and source-build versus signed-release pins.

  • Select the software catalog profile explicitly in implementation entrypoints and keep tutorial
    Task requests outside committed source Snapshots. Exercise the documented Quickstart and
    generated tutorial through token-free planning in regression tests.

  • Sandbox directories are named af-sandbox-<pid>-… under $TMPDIR, and every command that
    runs review or Task work (af review run, af task start --execute, af task run,
    af provider doctor) begins by removing the ones whose process no longer exists, finishing the
    removal before it exits. A sandbox is removed by the handle that owns it, so only a killed or
    aborted process leaves one behind, and a leftover tree that a Gate built into carries a whole
    target/: one development machine held 15 GB of them. The sweep keeps every directory whose
    process is still running and every directory the kernel preserved on purpose after an
    unconfirmed container cleanup (now marked preserved beside the tree), removes a tree only
    through directory descriptors opened without following links, touches nothing else under
    $TMPDIR, and reports what it removed in one stderr line.

  • af provider remove ID... drops named Providers from the machine-local registry through the
    same locked, atomic publication add uses. Auth directories and their logins are left
    untouched, and the previous registry is preserved. It also repairs a registry made invalid by a
    deleted auth directory, runs in the invoking release from any repository, and refuses ambient
    IDs by name. In the browser, d on a registered Provider fills the : line with the command
    (ADR-0136).

  • Add explicitly permissioned, private-host Provider authentication for browser-only setup and
    reauthentication, with Codex device approval, Claude code/callback support, bounded session
    state, recipient fencing and secret-free status. Generic setup login remains terminal-only;
    login never implicitly spends a model budget or resumes a Task
    (ADR-0137).

  • Include a runnable, consent-bound personal-chat host for validated one-time browser challenges,
    native Codex headless device login and Claude 2.1.289's exact unterminated prompt; reusable
    credentials remain native and ordinary af status stays challenge-free
    (ADR-0139).

  • Preserve native authentication failure categories alongside usage diagnostics and suppress
    credential-bearing failed output before ordinary capture, retaining exact parsed usage.

  • Classify native login failures into closed response/proxy/TLS/rejection/transport states while
    keeping stderr private and bounded; retain the native lifetime guard and credential boundary.
    An absent or unstartable official CLI reports provider_cli_missing (exit 4).

  • Remote Checks: a declared code check may add a [checks.<name>.remote] table (executor = "github-pr", workflow, required job names), and an operator's machine-local mapping
    ($XDG_CONFIG_HOME/af/remote-checks.toml or AF_TASK_REMOTE_CHECK_POLICY_FILE) selects it per
    repository. Local checks run first; the kernel then builds two af-gate/<task-id>/ branches
    from the Task's Snapshots in a private repository, pushes them without force, opens one draft
    pull request between them, waits on the declared workflow's pull_request run for that head
    commit, and accepts the result only after reading refs/pull/<n>/merge back as the candidate
    tree. Every remote fact is one af/RemoteCheckEvidence@1; a remote CheckResult@1 names it
    instead of a command; every refusal is not_run with a named reason; the last 256 KiB of each
    unsuccessful job's log (1 MiB per check) is kept as the result's stdout; no record holds the
    push URL or the mapping's path. af task show prints the pull request, run, jobs, kept log
    excerpt and cleanup commands, and --json carries the evidence under remote_checks. Without
    a mapping nothing changes. This is the one operator-authorized exception to "publishing is a
    human action"; delivery still never pushes
    (ADR-0140).

  • The TUI shows state in the brand's colours: chips of ink on blue (running), green (passed) and
    pink (failed or awaiting approval) on a Task's STATE, its stage marks, the progress in the bar,
    a Provider's STATUS and the Workers pane's Attempt counts, and an error chip before every
    error row. The status line turns pink while it carries an error, and the help header's worker
    is drawn in solid pink. Text on the terminal's own ground is never coloured, so every colour
    reads on a light or a dark terminal; NO_COLOR still leaves attributes only.

  • This repository's own .af/af.lock pins 0.10.0 (it still pinned 0.9.0-rc.6). From now on
    the release workflow pins each release it publishes, and make check fails when the pin falls
    more than one release behind CHANGELOG.md
    (ADR-0138).

  • Bare af paints a splash a few milliseconds after it starts: the three workers on a conveyor
    belt, the one a Task block reaches lit in its colour, while the scope and its panes load behind
    it. The browser replaces it as soon as it has loaded, so af is never slower; outside a
    repository, where reading every Task Store takes seconds, the terminal no longer sits blank.
    q or <C-c> quits from the splash, and other keys typed meanwhile reach the browser.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 13:13
afe8b8e

Authority compatibility

Committed .af/ policy keeps working as is.

Changes

  • release: v0.9.1 (#152)

  • Fix Task writer lease under load, interrupted Workers, and unstartable Provider CLIs (#134, #135, #136) (#153)

  • Research pipelines: warm checks, measure and compare, report Tasks, port bindings, store hygiene, and two verified research Tasks (#132)

  • Ctrl-C (SIGINT) or SIGTERM during af task run, af task start --execute, af review run
    or af provider doctor no longer leaves Worker processes running after af exits. af stops
    and reaps every Worker process group, then ends by the same signal, so the shell reports 130
    (SIGINT) or 143 (SIGTERM). The interrupted Attempt is recorded as a failed, cancelled Attempt
    and keeps its charge. The Task is not finished; stderr says to resume it with
    af task run TASK_ID. A second Ctrl-C while stopping kills the remaining Worker groups and
    exits at once (ADR-0129).

  • A live Task writer no longer fences itself with Task writer lease is expired or fenced on a
    loaded machine. The lease heartbeat now observes the lease through its own Store connection.
    It renews through that connection when the work still holds the shared one with 4 s of lease
    left. That connection waits at most 1 s for SQLite's write lock and retries with backoff, so
    neither a slow Store operation nor its write lock can outlast the lease. A renewal that
    commits after its lease expired is now refused inside the write transaction. A successor or an
    expired lease still fences and cancels the old writer, and is now seen without waiting for the
    held Store
    (ADR-0128).

  • A Provider CLI that cannot start is reported as a Provider installation failure instead of
    surfacing later as an unrelated Worker or credential error. A CLI cannot start when it is
    missing, not executable, or exits non-zero on its own --version check. af provider status
    marks the context installation_failed and exits 4, and af provider setup returns
    provider_cli_missing. Task Provider admission refuses the binding before any Worker is
    dispatched or any Attempt is charged. Each report names the Provider ID, the program path, the
    CLI's own first error line and the fix it suggests. A CLI that breaks after admission fails its
    Attempt as a Provider environment failure, not a model or credential failure
    (ADR-0130).

  • A Worker's draft reply may spell its citations and repository_citations in any order: the
    runner admits both sets in canonical order (sorted, unique) before validating the reply, so an
    author is judged on what it cited rather than on the order it listed it. This repository's
    kernel/report gives its author two Attempts.

  • Store hygiene and the warm cache's two bounds (ADR-0135, package R5 of
    docs/design/research-pipelines.md; amends ADR-0131). af task list --sizes prints each Task's
    CAS bytes — those only it reaches and those it shares with another Task or Campaign record —
    and the Store's total; --json adds sizes to each entry and store to the document. af task gc --older-than DAYS --keep N previews, writing nothing, which finished Tasks beyond the
    newest N it would collect and why every other Task stays (running, unfinished,
    writer_lease, bound_by, kept_newest, kept_recent); --apply takes the Store's writer
    lock, is refused while any writer lease is live, appends one task_collected transition
    carrying af/TaskCollected@1 per Task — referencing no artifact — and then removes every CAS
    object no uncollected record reaches, by a conservative walk through every digest a record
    spells. A stopped sweep is finished by the next run. A collected Task's projection stops at
    its tombstone: af task list and af task show print collected <time> with the retained
    summary (af/task-collected-inspection@1 for show --json, a collected member on
    af/task-list-entry@2), af task output, deliver, run and explain refuse it, and replay
    never calls its removed objects corrupt; the browser lists the Tasks it can open. An append now
    rechecks, under the writer lock, that every object it references is still filed. [warm] max_bytes is now the eviction bound, applied before a check and after one whose result
    stands, and a new [warm] hard_max_bytes (twice max_bytes by default, at most 32 GiB) is
    the only bound that ends a running check with warm_cache_bound_exceeded;
    TaskCacheObservationV1 records the acting bound and af task show prints it. New schemas
    task-collected-v1.json, task-collected-inspection-v1.json and task-gc-v1.json;
    task-transition-v5.json, task-list-entry-v2.json, task-runtime-evidence-v1.json and
    code-task-policy-v1.json gain the optional members. A Store without a tombstone and a policy
    without hard_max_bytes behave as before, except that such a policy's check now fails only
    above twice its max_bytes. A reader that loses an artifact to a concurrent sweep reports the
    Task as collected, a listing projects the uncollected Tasks before reading the tombstones, an
    opening Task re-checks every Task its bindings name under the writer lock, and a collected
    Task's row under --sizes carries a zero footprint.

  • Bind any declared root port (ADR-0134, package R4 of docs/design/research-pipelines.md;
    amends ADR-0117). A Task file's inputs table may bind any root input the selected Pipeline
    declares — the one the Task file names, or else every captured Pipeline accepting its kind,
    alike — to a recorded, finished Task's result output whose artifacts verify in the CAS and
    whose type and cardinality equal the port's exactly; requirements, base and continuation
    stay refused by name, and source, history and sources keep ADR-0117's rules. Only result
    outputs bind: naming an Attempt's raw artifacts, runtime evidence or any other record is
    refused with a message saying so, and an exact { "artifact" } reference binds only
    ADR-0117's three ports. A list of one to sixteen { "task", "port" } references binds a many
    port in order, so measurements can take an experiment's baseline and candidate; a one
    port keeps its output's Snapshot ID, a many port bound from several outputs names none while
    each artifact keeps its own, and a list into a one port is refused. Every refusal names the
    port and both types before any Worker or Provider admission. The Store, the executor (for a
    many port the consuming contract declares unbound) and the Worker renderer accept such a
    Snapshot-less many input port; output ports keep the one-Snapshot rule. A port bound without
    naming the Pipeline must be declared alike by every Pipeline accepting the kind, else the Task
    file is asked to name one; a list bound to a one port has each reference judged before its
    shape; a bound many output records every artifact it holds; and source, history and
    sources are checked against the selected Pipeline's declaration at resolution. This repository's kernel/analyst and kernel/report-verifier schemas take the
    snapshot_id a bound Measurement or comparison carries.
    af/TaskInputBindings@1 records a port's further outputs in an optional also list, absent
    for every single-reference binding, and af task explain and af task show print their
    existing binding rows once per output. task-file-v1.json gains the list form and
    task-input-bindings-v1.json the also list. The builtin/report starter's Worker schemas
    admit a snapshot_id on comparison and measurements values. A Task file without inputs
    is unchanged.

  • Report Tasks (ADR-0133, package R3 of docs/design/research-pipelines.md). The built-in kind
    report selects a new installed profile: an author reads the source Snapshot, the kernel
    renders its draft and resolves its repository citations against that exact Manifest, and an
    independent verifier on the same Snapshot accepts the report. Its captured policy is
    af.report-task-policy/1, named by report_policy in .af/task-catalog.toml (a newly
    declared .af/report-policy.toml). A Task file's report_sources captures
    af/ReportSources@1 — the af.document-sources/1 shape with zero to 256 entries, 256 KiB each
    and 512 KiB in total in a file of at most 640 KiB, the empty set when absent; the sources
    port is optional, and a Pipeline that binds nothing there seals against the empty set. An
    execute-checks Worker may add beside the source, never under a name the source holds. af/DocumentDraft@2 adds
    repository_citations of { path, line? }, spelled exactly as the Manifest spells them and
    rendered as path or path:line. The installed report_seal, report_check and
    report_accept operators record af/ReportCheckReceipt@1 (with each failed citation's
    reason: absent, directory, symlink, binary, line_out_of_range),
    af/ReportEvaluation@1 and af/ReportVerification@1, every one naming the source Snapshot;
    a verifier whose checks judged another Snapshot is refused at admission. An author whose
    effects are read-source and execute-checks gets ADR-0118's shell in a clone that seals
    nothing back. A report Task allows no write-source, has no snapshot output and is refused
    by af task deliver with a message naming af task output --port report; af task show
    prints the report's title, the verifier's outcome and the cited Snapshot. af catalog init --profile report emits the credential-free builtin/report starter, and this repository's
    kernel/report Pipeline with kernel/analyst and kernel/report-verifier is staged in
    fixtures/kernel-report/ for installation into .af/. New schemas: `report-sources-v1.jso...

Read more

v0.9.1

Choose a tag to compare

@github-actions github-actions released this 01 Oct 16:53
3aae93c

Authority compatibility

Current-shape committed .af/ authority needs no edit for these fixes; updating the installed default does not change project pins. To adopt 0.9.1 deliberately, run af onboard --refresh-lock --af 0.9.1 online and review/commit the authority. Native Rust snapshots require an explicit captured request and operator mapping; absent mappings preserve cold setup. Pre-GA (0.x) Task/Campaign state has no cross-release compatibility guarantee and is not migrated; retain its original release to finish or inspect it.

Changes

  • Explain Task verification budget rejections (#145)

  • fix: clarify review timeout scope and externalize read-only gate reports (#146)

  • Keep Claude Task Workers running on Claude Code 2.1.285 and across its updates (#147)

  • feat(tasks): pinned private Rust snapshots for native checks (#151)

  • Infeasible Task verification reserves now report required and available tokens, Attempts and
    wall milliseconds, identify exceeded resources, and list each protected node's contribution,
    including Provider admission. These are declared allowances, not estimated runtime; admission
    rules and configured budgets are unchanged.

  • Keep nextest CI JUnit reports outside read-only source trees during scripts/verify.sh,
    alongside the existing external Cargo target. Unique per-run report directories preserve
    failed-run evidence; temporary store-only tool configuration is removed afterward. Direct
    make test retains its existing report location and all test/profile settings are unchanged.

  • Run make check and scripts/verify.sh as an unprivileged user, including in containers.
    The new sealed-source regression requires mode bits to deny writes; root or
    CAP_DAC_OVERRIDE bypasses that seal and fails the explicit precondition rather than
    silently skipping the regression.

  • Correct the af review run --timeout-secs help: without --file (including the default
    routed run and explicit --campaign/--pipeline) it bounds each
    reviewer Attempt (default 1800 seconds, pinned in the Campaign manifest as
    reviewer_timeout_seconds), not the whole run; with --file it caps the whole Task at the
    lower of the flag and the file's limits.wall_ms. Timeout behaviour is unchanged.

  • Claude Task Workers no longer fail with Native billing usage is incomplete on Claude Code
    2.1.285, whose final result counts requests in modelUsage that its top-level usage leaves
    out. A breakdown that no top-level counter exceeds is charged as the complete bill; a top-level
    counter above the breakdown is still refused
    (ADR-0125).

  • An Attempt refused for incomplete billing now names the cause in its diagnostic, for example
    Native billing usage is incomplete: Claude top-level and per-model usage cannot be reconciled.

  • A Claude Code or Codex update in the middle of a Task no longer refuses the Task's next model
    Worker with Captured Task Provider identity is no longer current. The Task keeps running the
    client executable it captured when it started, and moves to the installed client if the update
    removed that file; the account is still proven before every invocation
    (ADR-0126).

  • Native code checks can opt into pinned Rust from an explicit operator mapping; AF verifies and privately copies only that toolchain before dispatch, avoiding repeated rustup downloads in fresh check homes. Host Cargo/Rustup homes are not passed through, inherited Cargo subcommands remain available, and check writes are never promoted. Missing mappings preserve cold setup; invalid mappings fail closed. Trusted-local isolation is unchanged (ADR-0127).

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 12:40
e1adea5

Authority compatibility

Committed .af/ policy from v0.9.0-rc.9 keeps working as is. To move a project pin to v0.9.0, run af onboard --refresh-lock --af 0.9.0. This is a public alpha: compatibility guarantees begin at 1.0; persisted pre-GA state is unsupported across upgrades.

Changes

  • Publish the tested v0.9.0-rc.9 CLI as the public alpha, available through the standard installer
    and af self update. Includes Task execution, sandboxed Review and the terminal browser.

  • Mark the README as public alpha and document security support for the latest alpha.

  • Add advisory Task preflight for lineage, budgets, and evidence (#142)

  • Add an offline advisory orchestration helper, scripts/task-preflight.py, for original-source
    identity, declared admission/Worker allowances and explicit acceptance-evidence mappings.
    It reads native inspection exports without changing Task authority, budgets or acceptance;
    ambiguous replay, live reservations and semantic completeness remain explicitly unverified.

v0.9.0-rc.9

v0.9.0-rc.9 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 29 Sep 14:28
71f0b46

Authority compatibility

Committed .af/ policy keeps working as is.

Changes

  • release: v0.9.0-rc.8 (#126)

  • af self update: keep unverified --version out of the latest cache; make a failed update unmistakable (#128)

  • brand: the afactory design system for the README, the website and the TUI (#127)

  • Bump jsonschema from 0.56.0 to 0.57.0 (#125)

  • TUI: fast Workers open; list old-release Stores once; uix measures latency (#130)

  • tui: the bare ? help opens with the pixel worker; CHANGELOG entry for the brand (#129)

  • Halve the gate: parallel test processes, linked-once test binaries, a compile cache that hits (#131)

  • Change notes per pull request under changelog.d/; CHANGELOG.md never conflicts (#140)

  • providers: a 15-second Codex probe budget (#133)

  • TUI: Pipelines read in batches; spawn-count guard; index flags do not hide drift (#138)

  • The design system: brand/ keeps the designer's wordmark (AFACTORY with three workers) and
    derives every asset from it with brand/gen.py; brand/tokens.css and tokens.json carry
    the palette, type, spacing and radii; brand/README.md is the brand book. The README opens
    with the banner; the tagline is "agent pipelines made fast". The browser paints its status
    line ink on the brand's blue and its errors pink where COLORTERM says the terminal takes
    truecolor, and as before otherwise (#127).

  • The browser's bare ? help opens with the pixel worker, the version and the tagline above
    the key reference.

  • make check runs its tests one process per test across every test binary at once
    (cargo nextest run --profile ci), with TEST_THREADS set from the machine's cores; the
    five tests that assert against a fixed real-time budget run alone and first, listed in
    .config/nextest.toml. The longest tests are split one case per test, the eight largest
    crates link their integration tests as one binary under tests/it/, doctests are off (there
    are none), and debug information is line tables only. CI compiles through sccache keyed
    by rustc inputs, caches only the crate registry, links with lld on Linux, and lints in a
    job beside the tests
    (ADR-0124). Measured
    on a 14-core developer machine: the test step 661 s to 289 s, a cold test build 58 s to
    46 s with 122 to 48 executables and 5.7 GB to 3.5 GB, the doctest step 22 s to none.

  • The browser's Workers pane opens in a tenth of a second instead of most of one: it reads every
    committed declaration, prompt and lock with one git cat-file --batch and checks their drift
    with one git diff --literal-pathspecs --name-only, side by side, where it spawned about four
    git processes per Worker on every open (0.79s to 0.06-0.13s for 14 Workers on a loaded
    machine). A file name with * or [ is now compared literally.

  • The Tasks pane lists the Task Stores an earlier (pre-GA) af release wrote as one bar entry,
    ! N old Stores, and one note saying where they are and that af does not read pre-GA state
    (ADR-0113), instead of an error row each. A Store refused for any other reason still shows its
    own error. review_core::event::ANOTHER_RELEASE is the phrase both sides share.

  • The Codex status and subscription probes allow 15 seconds instead of 5. On a loaded machine
    (a full build, an IDE indexing) starting codex alone took long enough that af task run
    refused a healthy provider with Codex subscription probe timed out after 5 seconds, and
    provider tests failed in Task gates. Claude's structural probe already allows 30 seconds.

  • The browser's Pipelines pane reads every committed pipeline, the catalog and their drift
    with the same two batched git calls as the Workers pane, instead of two git processes per
    pipeline. Tests assert both panes spawn as many git processes for 30 entries as for 2, so
    a per-item read cannot come back unnoticed.

  • A declaration marked skip-worktree or assume-unchanged in the index is still marked *
    when its working-tree copy differs from HEAD: git diff does not compare such files, so the
    panes hash those, and only those, against the committed blob.

v0.9.0-rc.8

v0.9.0-rc.8 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Sep 12:03
871176b

Authority compatibility

Committed .af/ policy keeps working as is; a source-writing Worker that declares execute-checks gets a shell, and only then (ADR-0120).

Changes

  • release: v0.9.0-rc.7 (#118)
  • Give a source-writing Worker that declares execute-checks a shell (ADR-0120) (#119)
  • The af browser's Tasks pane (docs/design/tui.md §5.5) (#120)
  • TUI: the Workers pane (M4) (#123)
  • TUI: hand af commands off from the : line (M5) (#124)
  • Give a source-writing Worker that declares execute-checks a shell (ADR-0120): worker_access
    maps write-source plus execute-checks to WorkerAccess::WriteSourceWithShell, the Claude
    adapter grants Read,Glob,Grep,Edit,Write,Bash and Codex runs workspace-write, and the shell's
    process group dies with the Attempt. Candidate capture skips the scratch a shell leaves: paths
    under a top-level name the source did not hold, and new top-level dotfiles
    (SealedSandbox::capture_snapshot_where). A writer without execute-checks is unchanged. The
    kernel's kernel/implementer declares it and formats, lints and tests before replying.
  • The browser's Tasks pane (ADR-0121, docs/design/tui.md package M3) lists the scope's Tasks
    under running/, awaiting approval/, done/ and failed/, newest first, as
    task-id outcome progress%. The user scope adds one level per repository's Task state. A
    Store the binary cannot read is an error row naming its directory. One Task's pane shows TASK,
    PLAN and SNAP lines, one PROGRESS row per stage of the plan's graph order ([ok], [..] with
    its attempt, [!!], [ ], with recorded wall and charged tokens), the TOKENS and TIME totals,
    and one HISTORY row per event. Every value comes from af task list --json and the
    af task explain --json document (af task show --json with the plan and its graph), or from an
    artifact they name. A running opened Task is read again about once a second off the key loop.
    Enter on a HISTORY row shows its artifact, p opens the Task's pipeline, y yanks the Task
    id and R reads again. The af task show builder is split from its printer, and
    default_task_state is the one spelling of the default --state. No --json document
    changes.
  • The browser's Workers pane (ADR-0122, docs/design/tui.md package M4) lists the reviewer
    Workers and Task Worker packages HEAD commits under .af/workers/, .af/task-packages/,
    .af/packages/ and .af/vendor/, grouped by source when more than one has Workers. The
    folder is read when it is first opened. One Worker's pane shows IDENTITY (its declaration and
    its af.lock or catalog pin, unpinned or pinned elsewhere), then STATE, then PROMPT
    (reviewer.md or instructions.md as committed, with gf opening the working-tree file).
    STATE counts the Attempts in this scope's Task Stores whose invocation ran a slot that the
    recorded plan binds to the Worker at the digest the committed pin records (other digests of
    the name on one other line), a Review shard's through the node that owns it, and a
    reviewer and a Task package of one name apart: reserved, settled ok, settled failed and
    released, plus the tokens charged and the wall time. Every open reads again. A drifted file is marked *, and a failed read of
    HEAD is shown above the last good entries. No --json document changes.
  • The browser's : line runs af commands (ADR-0123, docs/design/tui.md package M5). A line
    that clap parses and the browser does not own (:q, :cd, :scope, :e and :help keep
    their behaviour) runs as a child of the running af executable, with exactly the parsed words
    as its arguments. It runs in the scope's root, inherits the environment, and carries
    AF_DISPATCHED_FROM, so it is never dispatched to another release. The browser releases the
    terminal, and the child's process group owns the foreground, so <C-c> stops the child, not
    the browser. The released screen then shows af LINE: exit N (or the signal) and waits for
    Enter. After it, the browser reads again the scope, the settings and the Tasks, Workers and
    Providers panes (without the charged probe), keeping what is opened. A line without a
    subcommand, which would open a second browser, is refused. <Tab> completes subcommands at
    every level from the clap definition, and a Task ID argument from the Tasks pane. In the Tasks
    pane, r prefills task run ID --confirm-plan PLAN, and D on a verified Task prefills
    task deliver ID --branch af/ID --worktree ../ID --confirm and a space, with the
    confirmation left to type. D on an unverified Task says why. No --json document changes.

v0.9.0-rc.7

v0.9.0-rc.7 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 24 Sep 14:13
44a8fb8

Authority compatibility

Committed .af/ policy keeps working as is; execute-checks on a review Worker is opt-in.

Changes

  • release: v0.9.0-rc.6 (#107)
  • Name the CLI crate after its binary: crates/af (#108)
  • Bump base64 from 0.22.1 to 0.23.1 (#103)
  • Bump the cargo-minor-and-patch group across 1 directory with 3 updates (#101)
  • test: stop reporting a reaped descendant as live after cancellation (#109)
  • Take the toml, toml_edit and jsonschema updates, parsing TOML documents as tables (#111)
  • Bump toml from 0.8.23 to 1.1.6+spec-1.1.0 (#102)
  • Bump toml_edit from 0.22.27 to 0.25.15+spec-1.1.0 (#105)
  • Bump jsonschema from 0.26.2 to 0.56.0 (#104)
  • Make Provider onboarding safe for agents (#112)
  • Remove pre-GA legacy, migration and compatibility code (#114)
  • Keep .af/ to declarations: layout table, undeclared-path report, Task input bindings (ADR-0112..0114) (#113)
  • Pin Opus 5.5 and GPT-6 Sol defaults (#116)
  • Fix source install adoption by af self (#115)
  • Bare af opens a vim-native TUI; review Workers may run a shell (execute-checks) (#117)
  • Bare af at a terminal opens a read-first, vim-shaped browser (ADR-0119,
    docs/design/tui.md package M1). On a pipe, bare af still prints help to stderr and exits
    2. The scope comes from config::load: the user scope outside a repository, the project scope
    inside one, or af --repo DIR. A 28-column folding bar holds providers/, workers/,
    pipelines/ and tasks/. Beside it, the Settings pane shows the layer table and the effective
    values with their origins, as af config paths and af config show --origin print them, and
    e opens a layer with af config edit. The Providers pane shows the af provider status
    columns with a bar per quota window, and R runs the bounded usage probe off the key loop. The
    Pipelines pane shows, verbatim, the af task explain --tree text of a token-free plan compiled
    as af task plan compiles one, into a scratch Store; the status line shows the highlighted
    slot's Worker binding. The Workers and Tasks panes arrive in later packages. : lines are
    parsed by the CLI's own clap definition. The terminal runs through rustix::termios, an
    existing dependency, instead of the planned crossterm. No --json document, schema or
    fixture changes. Discovery under .af/task-packages/ walks every real directory, however deep, and keeps walking below a package. Escape sequences split across terminal reads decode whole (tui::keymap::Decoder), and a lone ESC is Escape only after a read brought nothing. Outside a repository the user scope never opens a directory layer, so a broken .af/af.toml above a plain directory cannot block it. af --repo DIR with no subcommand is exempt from pin dispatch like bare af. The bar lists the pipelines HEAD commits and reads every declaration from HEAD, the authority a preview compiles, marking a working-tree file that differs; a package the kernel refuses to plan shows its declared contract; a review Pipeline shows its committed declaration. af config edit user names the user layer without reading the ladder, so a directory layer that does not parse cannot block it. e on a highlighted layer opens that exact file, so two present directory layers cannot swap; a settings refresh or an editor hand-off whose reload fails says the settings are stale and why, instead of calling them refreshed; the Worker-binding status follows a modified package's notice row; a malformed escape stream is bounded and dropped through its terminator. Git runs for the pipelines pane with a cleared environment and an allowlist, so an inherited GIT_DIR cannot redirect it; a failed read of HEAD is an error above the last good entries, not an empty catalog; entries are bound to one resolved commit and read again before a preview when HEAD moved; a package is previewed only when the committed catalog pins its name at that file; an editor hand-off refreshes the opened pane; the status line yields the breadcrumb before cutting a binding or an error at 80 columns. A preview compiles the exact commit its entries were read from (plan_tree_preview_at), and a result for a commit the pane left is dropped; drift is judged by git diff (bytes, mode, existence); a failed HEAD read is shown on an opened entry too and nothing is compiled from stale entries; a scope change drops the previous repository's entries; the terminal's panic hook restores the screen only from the thread that owns it. R acts on the bar's selected node while the bar has focus; an editor hand-off from the bar refreshes that node's pane too and the bar follows; opening an entry the new HEAD no longer commits falls back to its folder; a failed reload after an editor is reported as stale settings; an unborn HEAD is told apart from a branch naming a missing commit. The kernel's review-light reviewers get two Attempts each, so a malformed reply is retried instead of ending the round. A toplevel is an ancestor whose .git git would open, not any path so named; a cancelled provider probe is forgotten and the last complete inventory stays; a modified pipeline's bar label carries *; a reload that finds the place became another scope enters it and drops the old entries.
  • Let a review Worker that declares execute-checks build and drive the candidate (ADR-0118). A
    model Worker whose roles contain review and whose effects are
    ["read-source", "execute-checks"] now runs in the same Mode::EphemeralWrite clone that
    AF-owned preparation uses, with its readable Review inputs, instead of a read-only tree. The
    Claude adapter adds Bash to its adapter-owned --tools and --allowedTools and keeps
    --safe-mode --restricted --permission-mode dontAsk --strict-mcp-config. Codex runs
    -s workspace-write rooted at the sandbox. The model process's whole group is killed when it
    exits, so a shell child cannot outlive the Attempt; wall-clock and token bounds are unchanged.
    Nothing is sealed back. At finish every Snapshot entry must be byte-identical; anything the
    reviewer added — build output, its harness, the dotfiles tools write into HOME, which is the
    sandbox root — is discarded with the clone and is not a source edit.
    Any source edit fails the Attempt with a diagnostic naming the changed paths, and a read-only
    Worker's refusal names its paths the same way. The adapter's writable: bool became the
    kernel-derived review_runner::task::WorkerAccess, computed from the captured effects by
    review_pipeline::task::source::worker_access, so no package or .af/ policy can name a tool.
    Existing Workers derive exactly the access they had; fixtures and --json documents are
    unchanged. A command Worker declaring the same effect runs under the process-group-killing
    exit policy too, so a background child it starts cannot outlive its Attempt
    (crates/review-runner/tests/task_command_process_group.rs).
  • Kill a supervised child's process group before reaping the child, not after: a reaped pid is
    free for reuse, so the late SIGKILL could land on an unrelated process that had just been
    spawned into its own group under the recycled id — on a loaded machine, a fresh git rev-parse
    dying with an empty stderr. review-process now observes the exit without reaping
    (waitid(WNOWAIT) on Linux, kqueue NOTE_EXIT on macOS and the BSDs), routes every group
    kill through the unreaped leader, and reaps last on every path — deadline, cancellation and
    held-pipe cleanup included; the provider probes poll the same way, ending a probe's group
    before reaping it. A failed git command with no stderr now reports its exit status, so a
    signalled git reads as signalled. The two provider lock files are opened with a
    bounded retry on the spurious ENOENT APFS returns to a concurrent openat(O_CREAT).
  • Declare the .af/ layout once and keep Task files out of git (ADR-0115): every canonical entry
    under .af/ — its kind, its writer of record, whether git versions it and what it holds — is
    now one table in review_config::layout, and layout::classify answers whether any
    repository-relative path is declared or undeclared. af help config renders that table and one
    paragraph saying what never belongs under .af/ and where it lives instead, and the old claim that af init
    gitignores af.local.toml is gone — there is no af init, and no command writes a .gitignore. A test walks every production source under crates/*/src and
    fails on any .af/ path the table does not declare, which added document-policy.toml,
    packages/, artifact-reuse/, cache/ and the in-memory-only task-compat/ to the declared
    set. af task plan, af task start --file, af review plan --file and af review run --file
    now warn on stderr, after capture, when the Task file resolves inside the repository and git check-ignore does not ignore it, naming the file and $XDG_STATE_HOME/af/tasks/. The warning
    is advisory: exit codes, stdout and every --json document are unchanged, and a Task file that
    is gitignored or outside the repository draws no warning.
  • Report undeclared .af/ paths and let a project refuse to deliver them (ADR-0116):
    review_config::layout::classify_manifest groups every .af/ path of a captured Snapshot
    manifest into declared and undeclared, with a path count and a byte total for each. It is a
    pure function of the manifest and the L1 table — it reads recorded entries, never a working
    tree or a sandbox, and judges a path by its decoded bytes while keeping the manifest spelling —
    so the same Snapshot answers the same on every machine. af task plan and af task start now
    print one advisory line naming the count, the byte total and up to ten paths, and carry the
    whole group in a typed undeclared_af_paths field of the --json document; af task deliver
    records it in af/task-delivery@1 beside ignored_paths and prints it in the d...
Read more

v0.9.0-rc.6

v0.9.0-rc.6 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Sep 11:45
2de6ed1

Authority compatibility

Prerelease: committed .af authority keeps working as is and needs no migration. The sha2 0.11, rusqlite 0.40 and jsonschema 0.56 updates change no stored spelling: Attempt IDs, sha256: artifact addresses, receipt prefixes and local state directory names are byte-for-byte what earlier releases wrote, and review_core::hex::encode pins that encoding under test. The machine-local Provider registry stays version 1 — af provider setup and af provider recover run in the invoking release instead of dispatching through a repository pin, and a replacement is published by one atomic exchange, so a supported pinned reader sees either the complete old registry or the committed new one (ADR-0111). The one change that can stop a working machine: Provider admission now revalidates auth directories, so an existing symlinked, foreign-owned, or group/world-writable auth directory or registry is refused until its ownership and permissions are tightened. Refresh a consumer pin explicitly with af onboard --refresh-lock after verifying the release and its archive digests, and keep the previous release for rollback.

Changes

  • release: v0.9.0-rc.4 (#98)
  • build(deps): bump actions/checkout from 4.2.2 to 7.0.1 (#82)
  • build(deps): bump actions/upload-artifact from 4.6.2 to 7.0.1 (#81)
  • build(deps): bump actions/download-artifact from 4.3.0 to 8.0.1 (#80)
  • Take the pending dependency updates, adapting digest and SQLite identities (#99)
  • build(deps): bump clap_mangen from 0.2.33 to 0.3.3 (#87)
  • build(deps): bump rusqlite from 0.37.0 to 0.40.2 (#86)
  • build(deps): bump sha2 from 0.10.9 to 0.11.0 (#85)
  • Bump nix from 0.30.1 to 0.31.3 (#84)
  • build(deps): bump jsonschema from 0.26.2 to 0.56.0 (#83)
  • Make fresh Provider onboarding copyable (#90): make fresh Provider bootstrap one command with
    af provider setup, keep af provider add for already-authenticated contexts, isolate login
    environment and auth-directory ownership, make setup serialize by canonical auth context,
    distinguish ambient discovery labels from selectable registry IDs, preserve every Gate and
    executing release in onboarding's copyable apply command, make registry publication serialized,
    conditional, durable and atomic across pinned releases, secure its directories and files
    independently of umask, reject unpublishable auth paths before login, bind security-sensitive
    operations to stable directory handles, provide hash-validating fail-closed recovery with
    af provider recover, and make the README's Codex-only quickstart complete.
  • Skip the last non-UTF-8 path test where the filesystem refuses such a name (#106)
  • release: v0.9.0-rc.5 (#100) — tagged but never published: its macOS check leg failed, so
    everything it carried ships here instead.