Repository navigation
Releases: PakhomovAlexander/afactory
Release list
v0.12.0-rc.2
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 testnow 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.tomlruns 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 everymake checkstep'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.tomlwithouttomllib.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 PATHand
--head-nextest-config PATHgive 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 incrates/af/tests/it/self_optimizer.rs. Each builds its own
repository and Store with the sameafcommands through shared helpers. Of the two, only
light_cache_candidate_measures_real_latency_and_grounds_later_adoption_evidencestays 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_workersruns 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 itsgitexit-status checks inline;
they now live in onecommithelper, 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_toolchainnow restores the baseline as main does. -
Measured with temporary per-phase timers, since removed. Each test ran alone under
cargo nextest run --profile cion a 14-core macOS host after one warm-up launch ofaf.
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-toolchaintask run8.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.
- Main's single test took 60.3 s. Setup up to the adopted light candidate took 8.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_umaskstays in the
exclusive override. Its work takes 0.37 s, but running first and alone it pays the first
launch of a freshly linkedaf, 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 launchesaffirst, 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_failedwith "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
andincomplete_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 refusalTask plan deadline expiredinstead of any error. In review-store,
recording_resume_rejects_revoked_decision_or_replaced_latest_reportfirst 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
withRecording recovery requires the exact expired admitted publication pauseinstead. 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. -
afbrowser: a message on the status line that names a long path, such as the:cdrefusal
<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...
v0.12.0-rc.1
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) andremote_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.tomlor
AF_TASK_REMOTE_CHECK_POLICY_FILE) now names only where this machine may push gate branches
for a repository: itscheckskey 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-gateand the destinationgithub:<owner/name>, whichaf task planprints on
EFFECTSandSEND, so confirming the plan is the consent; the target is read again before
the check Attempt pushes. A pipeline withoutremote_checksplans and runs exactly as before
(ADR-0140, amended). -
af provider statusand 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--jsononeaf/task-report@1document
(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 retainedonce 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,
unknownwhen 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 aTotal: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 beforeaf task refreshstill counts. Active time sums the Task's runs, the
leases in which an Attempt started, so waiting betweenaf 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 thataf task refreshmoved 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 isunknownotherwise, 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 thePR reportworkflow, which runs onpull_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 lastTotal: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 andrelease/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 withSelected model is at capacityno longer costs 400,000 tokens, nor does one
recovered after its writer's lease expired while the machine slept. The settlement's
af/TaskExecutionRecord@5carriesunknown_usage: { cause }(capacity,rate_limit,
authentication,model_unavailable,network,lease_expired,interruptedor
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 anerrororturn.failed
event names its classified cause, such asProvider 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 reportshow 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,
andaf task gc --applykeeps the count in the collected Task'saf/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 GiBmax_bytesand a 10 GiBmin_free_bytesfloor by default;AF_STORAGE__<KEY>
overrides it, and a repository's.af/af.tomlcannot) 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 everyaf task runandaf review run.
Age-based collection (keep_days,keep_tasks,keep_campaigns) runs onaf storage prune --apply, or after every run withauto_gc = true(off by default), and reaches Stores this
release cannot read and Stores made with--state, which$XDG_STATE_HOME/af/stores.tomlnow
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 lockaf selftakes to change the default or a pin.
The sweep that endsaf task runis recorded on that Task as astorage_sweepobservation, shown
byaf task show. Below the floor on any volume af works on, or when one cannot be measured, a
check reportsinsufficient_disk, a measurement fails withinsufficient_diskwithout running
its command, and a Worker Attempt is refused before any token is spent.af storageshows what af
holds andaf 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 emptyHOME,TMPDIR,AF_CHECK_SCRATCHandXDG_CACHE_HOME, removed
after it: write to$AF_CHECK_SCRATCHor$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_transcriptskeeps it). It closes a finished Task's gate
pull request and deletes itsaf-gate/branches (keep_gate_pull_requestskeeps them) only while
they still equal the Task's recorded repository, refs and commits, recording the result as
gate_cleanupwithout changing the Task's result, and collects such a Task only after a cleanup
is done.
v0.11.0
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 markedpreservedbeside 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 publicationadduses. 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,don 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 reportsprovider_cli_missing(exit 4). -
Remote Checks: a declared code check may add a
[checks.<name>.remote]table (executor = "github-pr",workflow,requiredjob names), and an operator's machine-local mapping
($XDG_CONFIG_HOME/af/remote-checks.tomlorAF_TASK_REMOTE_CHECK_POLICY_FILE) selects it per
repository. Local checks run first; the kernel then builds twoaf-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'spull_requestrun for that head
commit, and accepts the result only after readingrefs/pull/<n>/mergeback as the candidate
tree. Every remote fact is oneaf/RemoteCheckEvidence@1; a remoteCheckResult@1names it
instead of a command; every refusal isnot_runwith a named reason; the last 256 KiB of each
unsuccessful job's log (1 MiB per check) is kept as the result'sstdout; no record holds the
push URL or the mapping's path.af task showprints the pull request, run, jobs, kept log
excerpt and cleanup commands, and--jsoncarries the evidence underremote_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 anerrorchip 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_COLORstill leaves attributes only. -
This repository's own
.af/af.lockpins0.10.0(it still pinned0.9.0-rc.6). From now on
the release workflow pins each release it publishes, andmake checkfails when the pin falls
more than one release behindCHANGELOG.md
(ADR-0138). -
Bare
afpaints 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, soafis never slower; outside a
repository, where reading every Task Store takes seconds, the terminal no longer sits blank.
qor<C-c>quits from the splash, and other keys typed meanwhile reach the browser.
v0.10.0
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
oraf provider doctorno 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 fencedon 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--versioncheck.af provider status
marks the contextinstallation_failedand exits 4, andaf provider setupreturns
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
citationsandrepository_citationsin 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/reportgives 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 --sizesprints each Task's
CAS bytes — those only it reaches and those it shares with another Task or Campaign record —
and the Store's total;--jsonaddssizesto each entry andstoreto the document.af task gc --older-than DAYS --keep Npreviews, writing nothing, which finished Tasks beyond the
newestNit would collect and why every other Task stays (running,unfinished,
writer_lease,bound_by,kept_newest,kept_recent);--applytakes the Store's writer
lock, is refused while any writer lease is live, appends onetask_collectedtransition
carryingaf/TaskCollected@1per 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 listandaf task showprintcollected <time>with the retained
summary (af/task-collected-inspection@1forshow --json, acollectedmember on
af/task-list-entry@2),af task output,deliver,runandexplainrefuse 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_bytesis now the eviction bound, applied before a check and after one whose result
stands, and a new[warm] hard_max_bytes(twicemax_bytesby default, at most 32 GiB) is
the only bound that ends a running check withwarm_cache_bound_exceeded;
TaskCacheObservationV1records the actingboundandaf task showprints it. New schemas
task-collected-v1.json,task-collected-inspection-v1.jsonandtask-gc-v1.json;
task-transition-v5.json,task-list-entry-v2.json,task-runtime-evidence-v1.jsonand
code-task-policy-v1.jsongain the optional members. A Store without a tombstone and a policy
withouthard_max_bytesbehave as before, except that such a policy's check now fails only
above twice itsmax_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--sizescarries a zero footprint. -
Bind any declared root port (ADR-0134, package R4 of
docs/design/research-pipelines.md;
amends ADR-0117). A Task file'sinputstable 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,baseandcontinuation
stay refused by name, andsource,historyandsourceskeep 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 amany
port in order, someasurementscan take an experiment'sbaselineandcandidate; aone
port keeps its output's Snapshot ID, amanyport bound from several outputs names none while
each artifact keeps its own, and a list into aoneport is refused. Every refusal names the
port and both types before any Worker or Provider admission. The Store, the executor (for a
manyport the consuming contract declaresunbound) and the Worker renderer accept such a
Snapshot-lessmanyinput 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 aoneport has each reference judged before its
shape; a boundmanyoutput records every artifact it holds; andsource,historyand
sourcesare checked against the selected Pipeline's declaration at resolution. This repository'skernel/analystandkernel/report-verifierschemas take the
snapshot_ida bound Measurement or comparison carries.
af/TaskInputBindings@1records a port's further outputs in an optionalalsolist, absent
for every single-reference binding, andaf task explainandaf task showprint their
existing binding rows once per output.task-file-v1.jsongains the list form and
task-input-bindings-v1.jsonthealsolist. Thebuiltin/reportstarter's Worker schemas
admit asnapshot_idoncomparisonandmeasurementsvalues. A Task file withoutinputs
is unchanged. -
Report Tasks (ADR-0133, package R3 of
docs/design/research-pipelines.md). The built-in kind
reportselects 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 byreport_policyin.af/task-catalog.toml(a newly
declared.af/report-policy.toml). A Task file'sreport_sourcescaptures
af/ReportSources@1— theaf.document-sources/1shape 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; thesources
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@2adds
repository_citationsof{ path, line? }, spelled exactly as the Manifest spells them and
rendered aspathorpath:line. The installedreport_seal,report_checkand
report_acceptoperators recordaf/ReportCheckReceipt@1(with each failed citation's
reason:absent,directory,symlink,binary,line_out_of_range),
af/ReportEvaluation@1andaf/ReportVerification@1, every one naming the source Snapshot;
a verifier whose checks judged another Snapshot is refused at admission. An author whose
effects areread-sourceandexecute-checksgets ADR-0118's shell in a clone that seals
nothing back. A report Task allows nowrite-source, has nosnapshotoutput and is refused
byaf task deliverwith a message namingaf task output --port report;af task show
prints the report's title, the verifier's outcome and the cited Snapshot.af catalog init --profile reportemits the credential-freebuiltin/reportstarter, and this repository's
kernel/reportPipeline withkernel/analystandkernel/report-verifieris staged in
fixtures/kernel-report/for installation into.af/. New schemas: `report-sources-v1.jso...
v0.9.1
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 testretains its existing report location and all test/profile settings are unchanged. -
Run
make checkandscripts/verify.shas an unprivileged user, including in containers.
The new sealed-source regression requires mode bits to deny writes; root or
CAP_DAC_OVERRIDEbypasses that seal and fails the explicit precondition rather than
silently skipping the regression. -
Correct the
af review run --timeout-secshelp: 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--fileit caps the whole Task at the
lower of the flag and the file'slimits.wall_ms. Timeout behaviour is unchanged. -
Claude Task Workers no longer fail with
Native billing usage is incompleteon Claude Code
2.1.285, whose final result counts requests inmodelUsagethat its top-levelusageleaves
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 withCaptured 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
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
andaf 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
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 withbrand/gen.py;brand/tokens.cssandtokens.jsoncarry
the palette, type, spacing and radii;brand/README.mdis 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 whereCOLORTERMsays 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 checkruns its tests one process per test across every test binary at once
(cargo nextest run --profile ci), withTEST_THREADSset 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 undertests/it/, doctests are off (there
are none), and debug information is line tables only. CI compiles throughsccachekeyed
by rustc inputs, caches only the crate registry, links withlldon 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 onegit cat-file --batchand checks their drift
with onegit 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_RELEASEis 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) startingcodexalone took long enough thataf task run
refused a healthy provider withCodex 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-worktreeorassume-unchangedin the index is still marked*
when its working-tree copy differs fromHEAD:git diffdoes not compare such files, so the
panes hash those, and only those, against the committed blob.
v0.9.0-rc.8
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-checksa shell (ADR-0120):worker_access
mapswrite-sourceplusexecute-checkstoWorkerAccess::WriteSourceWithShell, the Claude
adapter grantsRead,Glob,Grep,Edit,Write,Bashand Codex runsworkspace-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 withoutexecute-checksis unchanged. The
kernel'skernel/implementerdeclares it and formats, lints and tests before replying. - The browser's Tasks pane (ADR-0121,
docs/design/tui.mdpackage M3) lists the scope's Tasks
underrunning/,awaiting approval/,done/andfailed/, 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 fromaf task list --jsonand the
af task explain --jsondocument (af task show --jsonwith 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.
Enteron a HISTORY row shows its artifact,popens the Task's pipeline,yyanks the Task
id andRreads again. Theaf task showbuilder is split from its printer, and
default_task_stateis the one spelling of the default--state. No--jsondocument
changes. - The browser's Workers pane (ADR-0122,
docs/design/tui.mdpackage M4) lists the reviewer
Workers and Task Worker packagesHEADcommits 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
itsaf.lockor catalog pin,unpinnedorpinned elsewhere), then STATE, then PROMPT
(reviewer.mdorinstructions.mdas committed, withgfopening 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 oneotherline), 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
HEADis shown above the last good entries. No--jsondocument changes. - The browser's
:line runsafcommands (ADR-0123,docs/design/tui.mdpackage M5). A line
that clap parses and the browser does not own (:q,:cd,:scope,:eand:helpkeep
their behaviour) runs as a child of the runningafexecutable, 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 showsaf 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,rprefillstask run ID --confirm-plan PLAN, andDon a verified Task prefills
task deliver ID --branch af/ID --worktree ../ID --confirmand a space, with the
confirmation left to type.Don an unverified Task says why. No--jsondocument changes.
v0.9.0-rc.7
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
afat a terminal opens a read-first, vim-shaped browser (ADR-0119,
docs/design/tui.mdpackage M1). On a pipe, bareafstill prints help to stderr and exits
2. The scope comes fromconfig::load: the user scope outside a repository, the project scope
inside one, oraf --repo DIR. A 28-column folding bar holdsproviders/,workers/,
pipelines/andtasks/. Beside it, the Settings pane shows the layer table and the effective
values with their origins, asaf config pathsandaf config show --originprint them, and
eopens a layer withaf config edit. The Providers pane shows theaf provider status
columns with a bar per quota window, andRruns the bounded usage probe off the key loop. The
Pipelines pane shows, verbatim, theaf task explain --treetext of a token-free plan compiled
asaf task plancompiles 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 throughrustix::termios, an
existing dependency, instead of the plannedcrossterm. No--jsondocument, 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 loneESCis Escape only after a read brought nothing. Outside a repository the user scope never opens a directory layer, so a broken.af/af.tomlabove a plain directory cannot block it.af --repo DIRwith no subcommand is exempt from pin dispatch like bareaf. The bar lists the pipelinesHEADcommits and reads every declaration fromHEAD, 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 usernames the user layer without reading the ladder, so a directory layer that does not parse cannot block it.eon 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 inheritedGIT_DIRcannot redirect it; a failed read ofHEADis an error above the last good entries, not an empty catalog; entries are bound to one resolved commit and read again before a preview whenHEADmoved; 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 bygit diff(bytes, mode, existence); a failedHEADread 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.Racts 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 newHEADno longer commits falls back to its folder; a failed reload after an editor is reported as stale settings; an unbornHEADis 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.gitgit 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-checksbuild and drive the candidate (ADR-0118). A
model Worker whoserolescontainreviewand whose effects are
["read-source", "execute-checks"]now runs in the sameMode::EphemeralWriteclone that
AF-owned preparation uses, with its readable Review inputs, instead of a read-only tree. The
Claude adapter addsBashto its adapter-owned--toolsand--allowedToolsand keeps
--safe-mode --restricted --permission-mode dontAsk --strict-mcp-config. Codex runs
-s workspace-writerooted 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. Atfinishevery Snapshot entry must be byte-identical; anything the
reviewer added — build output, its harness, the dotfiles tools write intoHOME, 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'swritable: boolbecame the
kernel-derivedreview_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--jsondocuments 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 lateSIGKILLcould land on an unrelated process that had just been
spawned into its own group under the recycled id — on a loaded machine, a freshgit rev-parse
dying with an empty stderr.review-processnow observes the exit without reaping
(waitid(WNOWAIT)on Linux, kqueueNOTE_EXITon 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 spuriousENOENTAPFS returns to a concurrentopenat(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 inreview_config::layout, andlayout::classifyanswers whether any
repository-relative path is declared or undeclared.af help configrenders that table and one
paragraph saying what never belongs under.af/and where it lives instead, and the old claim thataf init
gitignoresaf.local.tomlis gone — there is noaf init, and no command writes a.gitignore. A test walks every production source undercrates/*/srcand
fails on any.af/path the table does not declare, which addeddocument-policy.toml,
packages/,artifact-reuse/,cache/and the in-memory-onlytask-compat/to the declared
set.af task plan,af task start --file,af review plan --fileandaf review run --file
now warn on stderr, after capture, when the Task file resolves inside the repository andgit check-ignoredoes not ignore it, naming the file and$XDG_STATE_HOME/af/tasks/. The warning
is advisory: exit codes, stdout and every--jsondocument 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_manifestgroups 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 planandaf task startnow
print one advisory line naming the count, the byte total and up to ten paths, and carry the
whole group in a typedundeclared_af_pathsfield of the--jsondocument;af task deliver
records it inaf/task-delivery@1besideignored_pathsand prints it in the d...
v0.9.0-rc.6
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, keepaf provider addfor 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 ofumask, 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.