Commit graph

191 commits

Author SHA1 Message Date
Lahfir
d24a86bb8e test: cover the owner, locality, replace-acl, and sweep-race branches of the windows hardening 2026-07-27 05:28:06 -06:00
Lahfir
92a3aabce0 fix: scope the com bootstrap to adapter commands and harden review-flagged tests 2026-07-27 05:19:32 -06:00
Lahfir
1e3a240020 fix: route windows session and trace directory creation through the private-file seam 2026-07-27 05:11:30 -06:00
Lahfir
51683d5541 fix: scope the temp lease per write and fold duplicated windows helpers 2026-07-27 04:28:46 -06:00
Lahfir
4e2ccc9bdd feat: install the windows private-file seam at both unskippable consumers 2026-07-27 03:51:02 -06:00
Lahfir
c95f15fa2e test: pin the inherited-acl assumption module-free on a real profile leaf 2026-07-27 03:35:26 -06:00
Lahfir
28960d2859 feat: rebuild windows private-file hardening behind the core seam 2026-07-27 03:24:59 -06:00
Lahfir
38c3338bbe feat: give the windows adapter a session that owns apartment lifetime 2026-07-27 03:00:27 -06:00
Lahfir
0cc73ea7ba ci: extend the windows lane to the full package surface 2026-07-27 02:51:31 -06:00
Lahfir
8873f3e1a9 feat: report windows permissions truthfully from a host-independent seam 2026-07-27 02:49:40 -06:00
Lahfir
359b3507cb feat: establish com apartment and dpi awareness at unskippable call sites 2026-07-27 02:44:53 -06:00
Lahfir
86a06f7b59 feat: route private-file primitives through a core-defined platform seam 2026-07-27 02:21:32 -06:00
Lahfir
553d3532b3 feat: add target-gated windows-sys bindings to the windows crate 2026-07-27 02:04:02 -06:00
Lahfir
0ab0180868 test: resolve the test binary per platform via CARGO_BIN_EXE 2026-07-27 02:03:41 -06:00
Lahfir
69371f92f3 docs: cut acl validation from the windows private-file design
Reverses the ancestor-walk decision on measurement. A plain leaf under the user
profile already inherits SYSTEM, Administrators and the user with no Users
entry, all inherited and none explicit, so the walk would verify a property
Windows already guarantees. Unix has to author its permission because the
default there is world-readable; Windows does not, because the default is
already private.

The only principal the walk would additionally exclude is another
administrator, who holds SeTakeOwnershipPrivilege and can seize any object
whatever its DACL. Building a chain-walking validator against someone who can
bypass it costs more than it buys, and the cost is specific: parsing ACEs is
what the previous layer did, and its AceSize handling is the defect that took
the layer down. Removing the parser removes the bug class rather than fixing
it, so no successor module exists and a test asserts the ACE symbols never come
back.

What survives is the control with real unix parity and a real attack behind it:
per-component reparse-point rejection. That is an integrity control, not a
confidentiality one -- a junction planted on the path redirects where the
product writes, which no permission on the intended destination prevents.

Cross-admin isolation is now answered rather than open: out of scope, for the
same reason macOS never raised the question.
2026-07-27 01:47:57 -06:00
Lahfir
2d51c680e8 docs: plan the windows toolchain, ci, and com bootstrap sub-phase
Eight units covering the CI lane extension, Win32 bindings in the platform
crate, the COM and DPI bootstrap, the adapter session, the permission probe,
and a private-file layer rebuilt from scratch against sub-phase 2.0's measured
evidence.

Three reviewers found what the first draft got wrong. It installed both the
apartment and the private-file seam from ad_init, which the FFI header labels
optional and whose own doc promises it initialises nothing -- so any host that
skipped it would have run every private write through the unhardened default
with nothing to distinguish the two states. The install moves to the site the
macOS arm already uses, where a consumer cannot skip it.

It also planned a layer that validates security state it never authors. The
deleted implementation at least built an owner-only protected descriptor at
creation; the rebuild now does too, and rejects reparse points per component
the way the unix path rejects symlinks.

The ACE validation order was still wrong in the same shape as the defect it
exists to prevent: checking the type before forming the SID pointer still
dereferences the header at an offset nothing has bounds-checked. The order is
now extent-first, with the ACE count treated as advisory rather than trusted.

The seam routes at core's five private-file primitives rather than at a list of
call sites, which covers the lock and liveness paths a list had missed and
needs no call-site edits at all.
2026-07-27 00:14:08 -06:00
Lahfir
4206c722c9 docs: move the windows runner registration to the sub-phase that needs it
Sub-phase 2.1 was to register a self-hosted interactive Windows runner that
nothing in 2.1 through 2.11 uses. On a public repository that is a persistently
registered runner sitting idle behind ten sub-phases, and GitHub's own guidance
is that self-hosted runners should almost never be used for public repositories
at all. Registration moves to 2.12, the first sub-phase whose gate needs a real
desktop, and it takes the hardening requirements with it: dispatch-only
triggering, a written fork-PR approval policy, and an explicit
ephemeral-versus-persistent decision.

The deferred RDP row moves with it. Its closure was never documentation -- it is
a measurement that only exists once a second, non-console session does, so 2.12
now owns closing it rather than 2.1 owning a promise it could not keep.

Two probe scripts hardcoded the old closure sub-phase in text they emit. The
scripts were corrected and their probes re-run, so the captures were regenerated
rather than edited by hand.
2026-07-27 00:02:23 -06:00
Lahfir
31ffd5f657 docs: correct the windows sub-phase facts research disproved
Sixteen replacements, no annotations. The document is the product's source of
truth and now reads true.

The COM bootstrap is split by consumer. CoInitializeEx is right for the CLI and
wrong for the cdylib, which fails RPC_E_CHANGED_MODE against any host thread
already in an STA and whose per-thread balance can never be released from a Drop
on another thread; the library path uses CoIncrementMTAUsage, whose cookie is
thread-agnostic. MTA is recorded as a requirement rather than a preference,
because the single-threaded apartment can prevent handler removal outright.

Two things the sub-phase asserted were not reachable. Its private-file seam named
a boundary that does not exist: every write site is in core with no adapter
handle, and core may not depend on the platform crate. The document now states
that constraint and leaves the mechanism to the plan. Its CI paragraph claimed
all runners enforce clippy, isolation, and the size cap; only the macOS job does,
and the other two lanes each run a single lib invocation.

The event invariant said handlers are drained on the dedicated MTA thread. They
are not: delivery arrives on several UIA-owned threads at once, the registering
worker among them and the main thread never, so handler state has to be safe for
concurrent delivery.

Runner registration gains the hardening a public repository requires, and gains
the measurement it already owed the ledger: it closes the deferred RDP row rather
than merely documenting session isolation. The DPI bootstrap records that
awareness cannot be verified by read-back, since the query API has no enumerant
for the value being set.

The ledger's hunk index moves with them, 37 to 40. Hunks backed by research
rather than by a probe are labelled as such, so an external citation cannot pass
itself off as a measurement.
2026-07-26 23:45:25 -06:00
Lahfir
5fd9543a79
feat: add windows platform exploration probe corpus (phase 2.0) (#111)
* docs: add the sub-phase 2.0 probe corpus plan

The plan for the Windows platform-exploration sub-phase: an eleven-area
probe corpus, its findings ledger, and the same-PR phases.md corrections
that ledger obliges. Lands on the 2.0 sub-phase branch so the sub-phase
PR reviews as one self-contained unit.

* feat: add windows probe harness with redaction gate and integrity helpers

The harness is the foundation every 2.0 probe codes against: common.ps1 owns
the R11 redaction gate, BOM-less UTF-8 capture writers, KTD9 normalization,
scratch-process lifecycle with pid tracking, the KTD5 foreground assertion,
and a Medium-integrity process launcher.

runas.exe /trustlevel:0x20000 was measured on this box and produces a High
integrity process (S-1-16-12288) with a deny-only Administrators group, not
the Medium label the UIPI probe needs. Start-MediumIntegrityProcess therefore
duplicates the token and sets S-1-16-8192 explicitly, asserting the label on
read-back so a UIPI row can never be graded against a fake boundary.

* feat: add winforms and wpf scratch fixtures with explicit automation ids

The fixtures are the interaction targets for the pattern census, identity
stability diff, raw interaction exercises, and the COM shim's event sources.

Two measured platform facts shaped them. First, the .NET Core rule that a
WinForms control's Name becomes its UIA AutomationId does not hold on .NET
Framework 4.8 -- the HWND provider supplies raw window handles instead, which
change every launch and are useless for an identity-stability diff. The
fixture installs a server-side provider per control, with a switch to fall
back to explicit Win32 control ids.

Second, Win32 UIA client-side proxies are inert on this VM: the WinForms
fixture exposes 23 controls with zero patterns, every one a Pane, matching
stock Notepad's Edit reporting as a bare Pane. The WPF fixture exposes 25
controls with a full pattern surface. Interaction probes therefore target WPF
and record the WinForms absence as evidence rather than working around it.

* feat: capture windows environment, session, and integrity baseline

Every ledger row inherits these machine facts, so they are captured
reproducibly rather than asserted: build 17763.7434, session and
interactivity, mandatory integrity label, UAC policy state, default file
ownership, display topology, and tool inventory.

Identity is recorded as a shape, not a value. The account SID keeps its
well-known RID tail (S-1-5-21-<redacted>-500) because the RID is the
load-bearing fact, while integrity and well-known group SIDs stay verbatim
for the same reason.

Three findings the later units depend on. The GAC UIAutomationClient assembly
exports 57 types, all managed System.Windows.Automation, and no IUIAutomation
-- while CUIAutomation8 is registered against uiautomationcore.dll, so UIA3 is
reachable only through a compiled interop shim. Newly created files are owned
by S-1-5-32-544 via TokenOwner rather than TokenUser, pre-answering half of
the private-file ownership question. The locale is split: UI culture es-ES
while the current culture is en-US, so OS strings are Spanish but .NET number
and date formatting is not.

* feat: probe windows private-file primitives for the 2.1 hardening design

Answers the four questions sub-phase 2.1 rebuilds its private-file layer
against, measured on the real OS with verbatim Win32 error codes. Three of
the four contradict the framing this work was planned under.

Rename: MoveFileEx cannot replace a target that has any open handle, and it
reports ERROR_ACCESS_DENIED rather than the sharing violation the plan
assumed -- FILE_SHARE_DELETE does not rescue it, because MoveFileEx issues
ReplaceIfExists rather than POSIX-semantics rename. ReplaceFile is the
operation that honors share-delete on the destination, but it is the one
that refuses an open handle on the replacement source. The two APIs have
opposite tolerances on opposite sides, so code treating error 32 as the
sharing case misclassifies the most likely production failure.

Ownership: new files take their owner from TokenOwner, never TokenUser, at
both High and Medium integrity. Integrity level is not the variable -- the
default follows admin-group membership, so an owner-only DACL check breaks
for any admin account regardless of elevation.

Locality: GetFileInformationByHandleEx(FileRemoteProtocolInfo) does
distinguish local from remote, but signals local by failing with
ERROR_INVALID_PARAMETER rather than succeeding with a local protocol value.
An out-of-range info class returns that same error, so the code is ambiguous
and a control call is required before reading it as a locality signal.

ACL: neither leaf carries an explicit ACE. The restriction is authored at
the nearest protected ancestor and merely propagated, so a leaf-only check
sees the effective grants but cannot tell whether they are anchored or
silently re-derivable, and cannot see FILE_DELETE_CHILD on the parent.

* feat: probe uia3 com for events, caching, walkers, and pattern availability

This is the stack the Rust adapter actually wraps, and it does not agree with
the managed client. Notepad's edit control is a bare Pane with no patterns to
System.Windows.Automation but a Document with Value, Text, Text2, Scroll and
LegacyIAccessible to UIA3 COM, which names the responsible EDIT proxy in
ProviderDescription. Twenty-six nodes versus three. Win32 client-side proxies
are alive here; they are inert only for the managed client, so a managed
census would have recorded false absences for patterns the adapter will see.

The scratch fixture's earlier zero-pattern reading was a fixture artifact
rather than a platform fact: a custom server-side provider suppresses both
client-side proxies and WinForms' own providers. Both fixture modes are
censused so the ledger states the mechanism instead of the symptom.

CacheRequest measures 2.73x against the documented 3-5x, but the headline
number hides the shape: building the cache makes the find phase slower, and
the entire win lands in the read phase at roughly 299x. The claim is wrong in
both directions.

Handler teardown is safe with events in flight, costing 69ms against a
backlog of 281. The hazard is elsewhere: hiding and showing a top-level
window while handlers are registered poisons subsequent removal for up to 86
seconds, superlinearly in repetitions, and cheap callbacks do not avoid it.
A watch implementation needs a teardown timeout rather than an assumption.

Pattern ids are discovered from the OS at runtime rather than hardcoded,
which caught that the annotation availability property is 30118.

* feat: dump notepad, explorer, settings, and obsidian trees with real geometry

Targets launch restored-but-not-activated, so bounds are real: 100/95.9/100/99.2
percent non-empty across the four dumps, with focus asserted identical before
and after all seven walks. Settings resolves through ApplicationFrameWindow to
its CoreWindow's SystemSettings pid, because the frame window belongs to
ApplicationFrameHost and no predicate in either script keys on a display name.

Minimizing a window does not shrink its tree -- the node count is identical at
3 -- but it degenerates geometry in two different shapes. Only the top-level
window reports an empty rect; its descendants report real dimensions anchored
at -32000, and every node still reports IsOffscreen as false. An occlusion gate
testing emptiness or IsOffscreen alone would accept both as visible.

Chromium activation is not a client-stack difference. The managed client takes
Obsidian from 9 to 119 nodes across an 8 second settle with no flag, matching
the COM shim's shape. What suppresses it is occlusion: an earlier revision that
left other targets restored on top held the count at first contact for a 16
second instrumented hold across three runs. The mechanism is not isolated, so
it is recorded as a probe-placement hazard rather than a product claim.

The managed timing cross-check reproduces the COM stack's direction but not its
magnitude, and on Notepad caching is a pessimization at 0.21x, because
client-side providers run in the client process and the cache is pure overhead.

* feat: census patterns and automation ids, and measure identity survival

Identity survival is what sub-phase 2.5 designs its Windows RefEntry against,
and the three properties behave nothing alike. AutomationId survives process
restart on every stack measured, at 100 percent across WinForms, WPF, and real
Win32. RuntimeId survives restart nowhere, at zero percent on all three. Path
survives restart perfectly and is the first thing a content change breaks.

Under content mutation the failure is worse than loss. Explorer keys list rows
by row index, so after a folder changes, 29 AutomationId keys still resolve and
5 of them land on a different file. A ref keyed on AutomationId alone does not
fail -- it succeeds against the wrong element. That silent-wrong-target count,
not the survival percentage, is what makes stable text identity load-bearing.

Measuring it needed a real refresh wait: a 4 second settle reported a wholly
unchanged tree and would have recorded Explorer identity as perfectly stable,
the exact inverse of what the window does once it refreshes at 20 seconds.

Two stack facts. WPF automation-peer binding is a one-shot race -- a client
that reads before the peer exists binds the generic HWND provider and never
re-resolves, which a 30 second poll never recovers. And the managed and COM
clients report different AutomationId values for the same window, so the
divergence between stacks is in values as well as in visibility.

The honest pattern divergence is two, not eleven: LegacyIAccessible and Text2
are the only patterns the managed stack structurally cannot name. The larger
figure was an artifact of the WPF peer race and was retracted after the fix.

* feat: probe uipi, dpi awareness, and chromium activation

UIPI is measured across a boundary that actually exists. This box runs the
built-in Administrator at High with Admin Approval Mode off, so RunAs yields
High-vs-High and runas /trustlevel leaves the mandatory label untouched; the
probe manufactures a real Medium token instead and refuses to record a verdict
unless the worker reads back S-1-16-8192. Both arms run identical code against
the same High Notepad, so the High arm is a control proving the injection
works. UIA reads succeed from Medium; SendInput does not land, and the two arms
return the identical success code -- only re-reading the target separates them.
PostMessage is denied with error 5 while SendMessage WM_GETTEXT succeeds, so
reads cross the boundary and writes do not.

The 125 percent DPI measurement is not achievable here and the reason is
measured rather than assumed: the VMware display carries no EDID and offers a
single scale step. DisplayConfigSetDeviceInfo returns success and persists the
registry value while effective DPI stays at 96, which is itself worth
recording -- that API's success return is not evidence the scale changed. The
non-zero delta defers to 2.4, which owns per-monitor scale. What does land for
2.1 is that PER_MONITOR_AWARE_V2 succeeds on 17763 with both arms verified at
the awareness levels claimed.

Chromium activation is graded, not guessed. The bundled version was read off
this installation by scanning the shipped binary's user-agent string: Chromium
142, Electron 39.8.3. Both stacks reach the same settled tree with no flag, so
the exposure claim holds and the plan's candidate contradiction is superseded.
The flag is still not redundant: without it first contact is deterministically
the pre-activation shell, and with it first contact is a race. Neither arm
removes the need to settle before trusting a first snapshot.

* feat: exercise interactions, input synthesis, and hit-testing raw

Every interaction records pre-state, action, then an independent re-read --
never the call's return value. Toggle proves why: TogglePattern flips WPF
ToggleState without raising Click, so the fixture's status sink stays silent
while the element itself changed. Trusting the sink alone would have recorded
a failure that did not happen.

Posted keystrokes are not uniformly dead. WM_KEYDOWN posted to a Win32 edit
control does register, because TranslateMessage runs in the target thread's
own pump and synthesises the character regardless of how the message arrived;
what the path cannot carry is modifier state. Against Chromium it does not
register at all, with every call still returning success. Establishing that
needed an idle control pass: the Chromium tree moved on its own between two
reads, and without a quiet baseline that drift would have been filed as a
keystroke landing.

The astral-plane payload survives typing. SendInput forces a surrogate pair
into two separate unicode events and the target reassembles it intact, read
back through WM_GETTEXT so the check is independent of both the injection path
and UIA.

Notepad's edit control is unreachable from the managed client, and not merely
unenumerated: TryGetCurrentPattern returns false for Text, Value and Scroll on
the handle-resolved element, while the COM census sees the same window as a
Document carrying all three.

Hit-testing is the only sound visibility primitive of the three tested. The
zero-size control is addressable by handle, enumerable by no walk, and
returned by no point. A minimized window reports an empty rect at the top
level while its descendants report real dimensions at the -32000 anchor, with
IsOffscreen false throughout -- so neither emptiness nor IsOffscreen can gate
occlusion, but ElementFromPoint correctly returns the occluder.

* feat: add findings ledger, session evidence, and phases.md truth-sync

Seventy-nine rows across all eleven evidence areas, each carrying the stack
that produced it and whether it generalizes, so later sub-phases can tell an
API-contract fact from an observation about this box and these app versions.
Forty-three confirm the roadmap, five contradict it, twenty-six are new edges,
and five defer to a named Phase 2 sub-phase. No row is unknown.

Six in-place corrections land with it. The CacheRequest multiplier is replaced
by its phase decomposition, because a single number is wrong in both
directions. The private-file requirements now name ReplaceFile rather than
MoveFileEx for a replace over an open destination, record that share-delete is
necessary but never sufficient, and permit locality inference only behind a
control call, since an out-of-range info class is indistinguishable from a
local volume by error code. The Chromium guidance now requires a settle before
concluding a tree is thin; its exposure claim was left standing because the
probe confirmed it. The event-teardown mitigation now names the hazard that
actually dominates removal cost.

The completeness check is a script rather than a checklist, and it is
negative-tested: blanking a scope, setting a verdict to unknown, or dropping a
closure point each fail it loudly. It asserts the hunk index is bijective
against the measured diff rather than against a number written in prose.

* fix: wake a suspended settings instance instead of failing the tree dump

The probe skipped its own launch when SystemSettings was already running, then
waited the full window for a frame that a backgrounded UWP instance never
presents, so a second harness run failed where a standalone run passed. That is
the plan's recorded process-lifetime suspension risk arriving in practice.

After eight seconds without a frame the probe now issues ms-settings: to wake a
suspended instance, and the failure text names suspension as the cause rather
than reporting only that no frame was found.

* fix: close four teardown and gating defects found by review

None of these changed a measurement; all four were failure paths that could
report success.

The COM shim killed Obsidian by process name with a raw Stop-Process inside an
empty catch, so those pids never reached the ledger. A survivor there was
invisible to both the probe's own teardown and the harness survivor check --
the one place in the corpus a leaked process could go entirely undetected.
Obsidian pids are now registered and terminated through the confirming path.

The tree dump registered its Settings pid only after frame resolution
succeeded, so a resolution timeout threw first and leaked a process the probe
had launched. Registration now happens as soon as the pid exists, and only
when the probe launched it, since killing a pre-existing instance would be the
operator's window.

The modifier-release sweep injected without a foreground assertion, the only
unbracketed SendInput site in the corpus. It now asserts and logs rather than
aborting, because it runs during teardown.

The input probe swallowed a failed teardown capture write, so the file that
proves clipboard, cursor and process cleanliness could vanish while the probe
still reported ok. It now fails loudly, and a surviving process downgrades the
status too.

Separately, a focus change is now classified rather than assumed hostile. A
target the probe launched taking focus during its own startup is the target
activating itself; Electron does this asynchronously when its renderer becomes
ready, so under load it lands mid-dump and failed the run intermittently while
passing standalone. Only a focus move to a window the probe never launched is
interference.

* chore: retrigger ci after an unrelated interaction-lease flake

The macOS lib-test job failed on
interaction_lease::tests::home_changes_do_not_change_the_physical_interaction_lock,
which timed out acquiring the physical lock inside a 100 ms deadline at a
contention count of 14. This branch changes no Rust, and the other 975 tests in
that job passed, so the failure is runner contention rather than anything in the
probe corpus.

* chore: stop committing normalized capture twins

Every capture was committed twice: once raw and once with pids, handles,
timestamps and bounds canonicalized. The twin is produced by common.ps1 on
every run and is byte-reproducible from the capture beside it -- verified
across all thirty-seven of them -- so tracking both doubled the evidence diff
for no information a re-runner cannot regenerate locally.

The twins are still written on disk and -Compare still diffs them, so
re-runnability is unchanged.
2026-07-26 22:21:35 -07:00
Lahfir
4fa7661608 feat: open the Windows adapter phase with corrected platform facts
Establishes feat/windows-adapter as the base branch for all Windows work.
Sub-phases 2.0 through 2.15 are cut from here and merge back here; main
stays the macOS-GA line and gains Windows once, at the end, when the
adapter is production-solid as a whole.

Corrects the Phase 2 Windows sections, which were written from
documentation research in 2026-04 and re-verified against current sources
and a real Windows machine:

- uiautomation 0.25 (a "0.24" requirement will not resolve to it under 0.x
  semver); windows-capture 2.0.0 - the recorded 1.5.4 was never published
- Chromium 138 ships native UIA by default, so the web-wrapper depth-skip
  is the primary lever and --force-renderer-accessibility is a fallback
- Windows 11 22H2+ moved tray overflow to TopLevelWindowForOverflowXamlIsland
- windows-11-arm runners have been GA for public repos since 2025-08-07
- Windows 10 1809 remains the API floor, now qualified with its servicing
  reality (LTSC 2019 / Server 2019 are the serviced targets)
- Notepad is two different apps across Server and Win11 client SKUs
- CI tables reflect the real test-windows and test-linux lanes from v0.6.0

Adds the no-convenience-deferral rule: scope moves between sub-phases of a
phase, never out of it. The only sanctioned deferral is proven
impossibility, evidenced by a ledger row, shipping an honest
PLATFORM_NOT_SUPPORTED. 2.14 and 3.14 are no longer deferrable stretch
sub-phases.
2026-07-26 02:42:08 -06:00
Lahfir
dde0b98dc0
chore: track planning artifacts with secret and privacy scanning (#109)
Some checks failed
CI / Format (push) Has been cancelled
CI / Rust 1.89 MSRV (push) Has been cancelled
CI / Native check (macOS) (push) Has been cancelled
CI / Native check (Linux) (push) Has been cancelled
CI / Native check (Windows) (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Test (Linux) (push) Has been cancelled
CI / Test (Windows) (push) Has been cancelled
CI / FFI Python Smoke (push) Has been cancelled
CI / FFI Header Drift (push) Has been cancelled
CI / FFI Panic Guard (push) Has been cancelled
CI / FFI Stub-Adapter Passthrough (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
Release / Release Please (push) Has been cancelled
Supply Chain / Audit (push) Has been cancelled
Release / Build (aarch64-apple-darwin) (push) Has been cancelled
Release / Build (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (aarch64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (x86_64-pc-windows-msvc) (push) Has been cancelled
Release / FFI Release Gates (push) Has been cancelled
Release / Publish to GitHub Release (push) Has been cancelled
Release / Publish to npm (push) Has been cancelled
Release / Publish Skills to ClawHub (push) Has been cancelled
Tracks docs/plans and docs/brainstorms, adds .gitleaks.toml with privacy rules, and runs a history-mode gitleaks scan in CI. Docs and tooling only - no releasable change.
2026-07-26 01:41:55 -07:00
github-actions[bot]
33bc47ca38
chore(main): release 0.6.0 (#107) 2026-07-25 23:31:43 -07:00
Lahfir
8ad66b8f21
refactor!: remove speculative Win32 private-file layer from core, add real Windows/Linux test lanes (#106) 2026-07-25 23:25:28 -07:00
Lahfir
3f3d69a863
test: widen wall-clock budgets on retry/hydration tests to stop CI flakes (#103)
Some checks failed
CI / Format (push) Has been cancelled
CI / Rust 1.89 MSRV (push) Has been cancelled
CI / Native check (macOS) (push) Has been cancelled
CI / Native check (Linux) (push) Has been cancelled
CI / Native check (Windows) (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / FFI Python Smoke (push) Has been cancelled
CI / FFI Header Drift (push) Has been cancelled
CI / FFI Panic Guard (push) Has been cancelled
CI / FFI Stub-Adapter Passthrough (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
Release / Release Please (push) Has been cancelled
Supply Chain / Audit (push) Has been cancelled
Release / Build (aarch64-apple-darwin) (push) Has been cancelled
Release / Build (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (aarch64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (x86_64-pc-windows-msvc) (push) Has been cancelled
Release / FFI Release Gates (push) Has been cancelled
Release / Publish to GitHub Release (push) Has been cancelled
Release / Publish to npm (push) Has been cancelled
Release / Publish Skills to ClawHub (push) Has been cancelled
A class of unit tests bounds real retry/resolve/hydration work with a
tight wall-clock deadline (100-1000 ms) and asserts a definite outcome
(success, or a specific terminal code after a bounded retry). On slow,
contended GitHub runners the work overran the budget and the test got a
Timeout instead of its asserted outcome, flaking the Test job while every
local run passed. Widen those budgets to the suite's 5 s idiom so the
asserted state is reached deterministically. Genuine deadline-expiry
tests (permanently-unresolvable adapters, single-shot timeout_ms 0,
deadline-value plumbing, lock-contention) keep their tight budgets - a
slow runner still expires them correctly. Test-only; no production change.
2026-07-20 01:07:58 -07:00
github-actions[bot]
24ba049ec4
chore(main): release 0.5.0 (#100)
* chore(main): release 0.5.0

* chore: sync cargo lock for release 1.89

* docs: curate 0.5.0 changelog

Drop the stale '## Unreleased' block (the sessions/#89 refactor already
shipped in v0.4.6 and was duplicated above the new entry), and expand the
foundation-contract feature entry with the headline new capabilities so
the release communicates what callers gain, not only what breaks.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Lahfir <nmhlahfir2@gmail.com>
2026-07-20 00:29:50 -07:00
Lahfir
3f322728b4
feat!: implement Playwright-grade foundation contract
Settle the Playwright-grade reliability contract in agent-desktop-core
before the Windows/Linux adapters are built, so they inherit it instead
of redesigning it. Every command now observes, waits, verifies, and
reports honestly instead of firing blindly.

Highlights: capability-supertrait split of PlatformAdapter with
not_supported() defaults; canonical role/state vocabulary with live
`is --property visible`; display enumeration (`list-displays`) and honest
`--screen` with scale factor; truthful Automation permission; `native_id`
identity spine; window-id-first resolution; serializable `LocatorQuery`
with live `find`; default-on auto-wait before every ref action; three-way
`hit_test` occlusion gate; `scroll_into_view` in core; core accessible-name
precedence; typed `ActionStep` delivery tier; `ProcessState` and
`APP_UNRESPONSIVE`; `LaunchOptions`; baseline-diff desktop signals
(`wait --event`); typed clipboard (`Text`/`Image`/`FileUrls`); mouse
modifier chords and `mouse-wheel`. Hardened through a 35-reviewer pass with
independent validation and a green live e2e gate (109/0), plus a
head-vs-main performance comparison harness.

BREAKING CHANGE: default-on auto-wait changes the timing of every
previously-untouched ref-action call (bounded 5000 ms default; `--timeout-ms 0`
restores single-shot). `ENVELOPE_VERSION` is now `2.1` (adds the
`APP_UNRESPONSIVE` code and process state in error details). FFI ABI major
is `3` (append-only struct evolution; `wait --event` is intentionally not
exposed over FFI). The legacy string clipboard API is removed in favor of
typed content. `key-down`/`key-up` fail closed until daemon-owned held input
exists. `close-app` verifies termination and the osascript fallback path is
removed. `--text` matching is subtree containment: `find --text X --first`
returns the outermost matching container.
2026-07-20 00:21:38 -07:00
github-actions[bot]
52705afbe1
chore(main): release 0.4.7 (#92)
Some checks failed
CI / Format (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / FFI Python Smoke (push) Has been cancelled
CI / FFI Header Drift (push) Has been cancelled
CI / FFI Panic Guard (push) Has been cancelled
CI / FFI Stub-Adapter Passthrough (push) Has been cancelled
CI / FFI Codegen Drift (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
Release / Release Please (push) Has been cancelled
Supply Chain / Audit (push) Has been cancelled
Release / Build (aarch64-apple-darwin) (push) Has been cancelled
Release / Build (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (aarch64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (x86_64-pc-windows-msvc) (push) Has been cancelled
Release / FFI Release Gates (push) Has been cancelled
Release / Publish to GitHub Release (push) Has been cancelled
Release / Publish to npm (push) Has been cancelled
Release / Publish Skills to ClawHub (push) Has been cancelled
* chore(main): release 0.4.7

* chore: sync cargo lock for release 1.85

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-02 12:02:01 -07:00
Lahfir
e3e1872ff3
feat: add trace viewer and replay artifacts
Adds the trace read/replay layer on the session-first foundation: `trace show` merges per-process segments into one deterministic timeline (bounded JSON for agents), and `trace export` renders a single self-contained, XSS-safe HTML viewer for humans. Opt-in `session start --screenshots` captures pre/post-action screenshots and refmap copies; command.start/end boundary events and a versioned trace.meta header make a step-by-step replay reconstructable. Redaction is hardened so raw caller arguments never leak into trace-reachable error messages. Available across CLI, batch, and FFI.
2026-07-02 11:48:06 -07:00
dependabot[bot]
e16b218653
chore(deps): bump the github-actions group with 3 updates (#88)
Some checks are pending
CI / Format (push) Waiting to run
CI / Test (push) Waiting to run
CI / FFI Python Smoke (push) Waiting to run
CI / FFI Header Drift (push) Waiting to run
CI / FFI Panic Guard (push) Waiting to run
CI / FFI Stub-Adapter Passthrough (push) Waiting to run
CI / FFI Codegen Drift (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
Release / Release Please (push) Waiting to run
Release / Build (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (x86_64-pc-windows-msvc) (push) Blocked by required conditions
Release / FFI Release Gates (push) Blocked by required conditions
Release / Publish to GitHub Release (push) Blocked by required conditions
Release / Publish to npm (push) Blocked by required conditions
Release / Publish Skills to ClawHub (push) Blocked by required conditions
Supply Chain / Audit (push) Waiting to run
Bumps the github-actions group with 3 updates: [actions/cache](https://github.com/actions/cache), [actions/setup-python](https://github.com/actions/setup-python) and [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `actions/cache` from 6.0.0 to 6.1.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](2c8a9bd745...55cc834586)

Updates `actions/setup-python` from 5.6.0 to 6.3.0
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](a26af69be9...ece7cb06ca)

Updates `actions/attest-build-provenance` from 4.1.0 to 4.1.1
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](a2bbfa2537...0f67c3f485)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: actions/setup-python
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/attest-build-provenance
  dependency-version: 4.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-01 17:19:48 -07:00
github-actions[bot]
b9e63fc21e
chore(main): release 0.4.6 (#90)
* chore(main): release 0.4.6

* chore: sync cargo lock for release 1.85

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-01 17:12:57 -07:00
Lahfir
35fa914b52
feat: make sessions the first-class trace container
Sessions now own tracing: `session start` creates a manifest-gated trace sink with per-process JSONL segments under ~/.agent-desktop/sessions/<id>/trace/, so agents set the session once instead of passing --trace on every command. Bare --session stays snapshot-namespace-only; --trace <path> still overrides for CI and one-offs.

Adds session start/end/list/gc, activation resolution (flag > AGENT_DESKTOP_SESSION > pointer), FFI verification tests, and status fields for session_id + tracing.
2026-07-01 17:11:33 -07:00
github-actions[bot]
c454f4ab1c
chore(main): release 0.4.5 (#87)
Some checks failed
CI / Format (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / FFI Python Smoke (push) Has been cancelled
CI / FFI Header Drift (push) Has been cancelled
CI / FFI Panic Guard (push) Has been cancelled
CI / FFI Stub-Adapter Passthrough (push) Has been cancelled
CI / FFI Codegen Drift (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
Release / Release Please (push) Has been cancelled
Supply Chain / Audit (push) Has been cancelled
Release / Build (aarch64-apple-darwin) (push) Has been cancelled
Release / Build (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (aarch64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (x86_64-pc-windows-msvc) (push) Has been cancelled
Release / FFI Release Gates (push) Has been cancelled
Release / Publish to GitHub Release (push) Has been cancelled
Release / Publish to npm (push) Has been cancelled
Release / Publish Skills to ClawHub (push) Has been cancelled
* chore(main): release 0.4.5

* chore: sync cargo lock for release 1.85

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-29 21:47:23 -07:00
Lahfir
ce232787b5
feat: add --wait-for selector polling flags (#86)
Global --wait-for / --wait-for-gone (-w) and --wait-timeout flags that poll the accessibility tree until a role:text selector appears or disappears, then return the snapshot. Honored by snapshot and the 16 ref-action commands; post-action waits scope to the acted-on window. Closes #84.
2026-06-29 21:45:32 -07:00
github-actions[bot]
d81ce7ba2c
chore(main): release 0.4.4 (#85)
Some checks are pending
CI / Format (push) Waiting to run
CI / Test (push) Waiting to run
CI / FFI Python Smoke (push) Waiting to run
CI / FFI Header Drift (push) Waiting to run
CI / FFI Panic Guard (push) Waiting to run
CI / FFI Stub-Adapter Passthrough (push) Waiting to run
CI / FFI Codegen Drift (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
Release / Release Please (push) Waiting to run
Release / Build (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (x86_64-pc-windows-msvc) (push) Blocked by required conditions
Release / FFI Release Gates (push) Blocked by required conditions
Release / Publish to GitHub Release (push) Blocked by required conditions
Release / Publish to npm (push) Blocked by required conditions
Release / Publish Skills to ClawHub (push) Blocked by required conditions
Supply Chain / Audit (push) Waiting to run
* chore(main): release 0.4.4

* chore: sync cargo lock for release 1.85

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-29 11:33:07 -07:00
Lahfir
94ce6c551f
feat(macos,core): harden adapter and core foundation with caller-controllable guardrails (#82)
Hardens the macOS adapter and platform-neutral core: stuck-key/clipboard/scroll/click fixes, deep-copy clipboard restore, notification-session and resolve-deadline hardening, blocked-combo guardrail moved to the adapter with a --force caller override, plus ~60 behavior-pinning tests. No breaking changes.
2026-06-29 11:32:27 -07:00
Lahfir
b00a967e0b
docs: add community health files for GitHub community standards (#83)
Some checks are pending
CI / Format (push) Waiting to run
CI / Test (push) Waiting to run
CI / FFI Python Smoke (push) Waiting to run
CI / FFI Header Drift (push) Waiting to run
CI / FFI Panic Guard (push) Waiting to run
CI / FFI Stub-Adapter Passthrough (push) Waiting to run
CI / FFI Codegen Drift (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
Release / Release Please (push) Waiting to run
Release / Build (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (x86_64-pc-windows-msvc) (push) Blocked by required conditions
Release / FFI Release Gates (push) Blocked by required conditions
Release / Publish to GitHub Release (push) Blocked by required conditions
Release / Publish to npm (push) Blocked by required conditions
Release / Publish Skills to ClawHub (push) Blocked by required conditions
Supply Chain / Audit (push) Waiting to run
Completes the recommended community profile (opensource.guide best practices):
- CODE_OF_CONDUCT.md — Contributor Covenant v2.1
- CONTRIBUTING.md — repo-accurate dev workflow, quality gates, coding standards,
  conventional-commit and command-extensibility guidance
- SUPPORT.md — where to get help / report issues
- .github/ISSUE_TEMPLATE/ — bug + feature issue forms and config (security and
  docs contact links; blank issues disabled)
- .github/PULL_REQUEST_TEMPLATE.md — summary, type, gate checklist
2026-06-28 21:41:39 -04:00
github-actions[bot]
5cb71051f6
chore(main): release 0.4.3 (#81)
Some checks are pending
CI / Format (push) Waiting to run
CI / Test (push) Waiting to run
CI / FFI Python Smoke (push) Waiting to run
CI / FFI Header Drift (push) Waiting to run
CI / FFI Panic Guard (push) Waiting to run
CI / FFI Stub-Adapter Passthrough (push) Waiting to run
CI / FFI Codegen Drift (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
Release / Release Please (push) Waiting to run
Release / Build (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (x86_64-pc-windows-msvc) (push) Blocked by required conditions
Release / FFI Release Gates (push) Blocked by required conditions
Release / Publish to GitHub Release (push) Blocked by required conditions
Release / Publish to npm (push) Blocked by required conditions
Release / Publish Skills to ClawHub (push) Blocked by required conditions
Supply Chain / Audit (push) Waiting to run
* chore(main): release 0.4.3

* chore: sync cargo lock for release 1.85

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-27 20:15:16 -07:00
Lahfir
a708fa0332
fix(macos): harden retained_handle null guard against release-only CFRetain(null) (#80)
Make the retained_handle null check unconditional so release builds no longer risk CFRetain(null); tests keep benign placeholder behavior, production returns ElementNotFound.
2026-06-27 20:14:44 -07:00
github-actions[bot]
cadbdf9602
chore(main): release 0.4.2
Some checks are pending
CI / Format (push) Waiting to run
CI / Test (push) Waiting to run
CI / FFI Python Smoke (push) Waiting to run
CI / FFI Header Drift (push) Waiting to run
CI / FFI Panic Guard (push) Waiting to run
CI / FFI Stub-Adapter Passthrough (push) Waiting to run
CI / FFI Codegen Drift (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
Release / Release Please (push) Waiting to run
Release / Build (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (x86_64-pc-windows-msvc) (push) Blocked by required conditions
Release / FFI Release Gates (push) Blocked by required conditions
Release / Publish to GitHub Release (push) Blocked by required conditions
Release / Publish to npm (push) Blocked by required conditions
Release / Publish Skills to ClawHub (push) Blocked by required conditions
Supply Chain / Audit (push) Waiting to run
Release 0.4.2 — Phase B and C of the FFI completion (Python smoke harness, cross-platform parity gates, build.rs codegen).
2026-06-26 19:15:50 -07:00
Lahfir
9023f331b3
feat(ffi): Phase B and C — Python smoke harness, parity gates, build.rs codegen (#77)
Completes the FFI plan: U9 adds a Python ctypes smoke harness proving the C ABI from a non-Rust host; U10 adds cross-platform parity CI gates for header drift, codegen drift, panic-unwind, and stub-adapter passthrough; U11 generates the command-backed wrappers from build.rs templates with a committed drift-gated output. The C ABI stays byte-identical and the surface is Windows and Linux ready with no new FFI code.
2026-06-26 19:14:14 -07:00
Lahfir
6e86f1c299
chore(main): release 0.4.1
Some checks are pending
CI / Format (push) Waiting to run
CI / Test (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
Release / Release Please (push) Waiting to run
Release / Build (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (x86_64-pc-windows-msvc) (push) Blocked by required conditions
Release / Publish to GitHub Release (push) Blocked by required conditions
Release / Publish to npm (push) Blocked by required conditions
Release / Publish Skills to ClawHub (push) Blocked by required conditions
Supply Chain / Audit (push) Waiting to run
2026-06-25 19:48:34 -07:00
Lahfir
8de8f904db
feat: complete FFI C-ABI surface (Phase A) — handshake, pipeline entrypoints, log callback (#67)
* build: make cbindgen regenerate the C ABI static-assert guards

Add [const] allow_static_const = false to cbindgen.toml so every
pub const AD_*_SIZE emits as a #define macro (a C constant expression)
rather than static const (which is not valid inside _Static_assert).

Add a cbindgen.toml trailer with all 19 C11 ABI guards (6 sizeof, 6
_Alignof, 7 offsetof). Each sizeof guard references the corresponding
AD_*_SIZE macro so the size literal lives in exactly one place: the
Rust source. The Rust source already has compile-time asserts tying each
const to its struct, so the chain is: Rust const → #define macro →
_Static_assert → C compile gate.

Rename pub const MAX_C_STRING_BYTES to AD_MAX_STRING_BYTES so the
public ABI name matches the header macro (#define AD_MAX_STRING_BYTES)
and the internal const name no longer leaks. Update all call sites and
the describe() error string.

Pin cbindgen 0.29.4 in scripts/update-ffi-header.sh so regen is
reproducible.

* test: stabilize flaky wait-resolution test under CI load

element_wait_retries_transient_ambiguous_resolution and its sibling used a
250ms budget that the happy path clears instantly but a loaded CI runner
(full --lib --workspace in parallel) can exceed before the transient
AmbiguousTarget retry resolves — an intermittent red CI unrelated to any
code change. Raise the ceiling to 2000ms; the success path still returns on
the second resolve, so the test stays fast while becoming load-robust.

* refactor: enforce cbindgen version in update-ffi-header.sh

The script documented 0.29.4 as required but accepted any installed
version silently. A mismatched cbindgen can produce a semantically
different header with no diagnostic. Fail fast with a clear install
command if the version does not match exactly.

* feat: add ad_abi_version and ad_init ABI handshake

* refactor: address review on ad_abi_version

* feat: add ad_version FFI entrypoint

* refactor: move guard_non_null inside trap_panic for convention parity

All other FFI entrypoints validate input pointers as the first statement
inside trap_panic(|| unsafe { … }). ad_version placed the guard outside
the closure; align it with the uniform convention throughout the crate.

* feat: add session-scoped FFI adapter constructor

* refactor: address review on session context

* feat: add ad_set_log_callback with tracing layer

* refactor: address review on log callback

- Tighten core visibility: revert `pub mod trace` to `pub(crate) mod trace`,
  re-export only `sanitize_trace_value` at the crate root so the FFI crate
  imports it without exposing `TraceConfig` and internal helpers publicly
- Add per-thread re-entrancy guard (`IN_CALLBACK` + `CallbackGuard` RAII
  drop) to `on_event`: a consumer callback that emits `tracing` events now
  silently drops the recursive invocation instead of risking a stack overflow
  that `catch_unwind` cannot stop
- Update module doc and `ad_set_log_callback` `///` doc to document the
  re-entrancy protection; regenerate committed FFI header via cbindgen

* feat: add ad_snapshot with refmap pipeline

* refactor: address review on ad_snapshot

Promote app_error_to_adapter to pub(crate) in commands/mod.rs so future
command files share one conversion instead of copying the match per file.
Document the tri-state *out contract (null on arg/infra errors, populated
JSON envelope on command-level errors) in both the Rust doc comment and
the C header.

* feat: add ad_status FFI entrypoint

* refactor: address review on ad_status

* feat: add ad_wait with size-pinned AdWaitArgs

* refactor: DRY ad_wait field decoding

* fix: align command_context error return with errno-style last-error invariant

The context failure arm in ad_wait hardcoded ErrInternal while
set_last_error stored the actual mapped code; callers relying on
ad_last_error_code() == return value would see a mismatch. Mirror the
execute() Err arm and return last_error_code() instead.

* feat: add ad_execute_by_ref with strict resolution

* refactor: address review on ad_execute_by_ref

- extract app_error_to_adapter to error.rs pub(crate), removing
  duplicate copies from snapshot.rs and execute_by_ref.rs
- replace 24-line tri-state ref_id decode with required_adapter_string
- use last_error_code() on validate_ref_id failure path for consistency
- fix *out dual-channel doc (null on guard/decode failure, non-null
  JSON envelope on command-level errors) in both Rust doc and C header
- rename misleading test: stale_ref_returns_error → returns_error_envelope
  (missing refmap surfaces SNAPSHOT_NOT_FOUND, not STALE_REF)

* docs: restore ABI header comments lost in cbindgen regen

Add /// docs on the Rust source so cbindgen re-emits the privacy note on
ad_last_error_details, the AdResult forward-compat note, and the behavioral
descriptions for the ad_execute_action* family.

* refactor: dedup ad_wait error conversion via shared helper

Replace wait.rs's local app_error_to_adapter_error with the shared
commands::app_error_to_adapter, giving one canonical AppError->AdapterError
conversion across every ffi command.

* fix: harden ABI assert trailer against double-include, document panic guard

Add a one-shot #ifndef AGENT_DESKTOP_ABI_ASSERTS guard around the
_Static_assert trailer in cbindgen.toml so re-including agent_desktop.h
in a single translation unit is unambiguously safe in all C standards
(C11 already allows repeated file-scope _Static_assert, but the guard
makes the property explicit and unconditional).

Add c_header_double_include.rs regression test that includes the committed
header twice via the system cc and asserts the compile succeeds, mirroring
the existing c_header_compile.rs harness pattern.

For Finding #14 (panic=abort guard): empirical probe confirms that Cargo
always sets CARGO_CFG_PANIC="unwind" in the build script for this crate
regardless of the active profile's panic setting — because the crate
declares both cdylib and rlib crate-types. A build-time check would be
silently inert. Document the invariant and the infeasibility of the
CARGO_CFG_PANIC guard in build.rs module docs instead; the release-ffi
profile enforces unwind at the profile level.

* fix(ffi): surface foreign-subscriber conflict, soften ad_init wording, direct agents to ad_snapshot

Finding #15: replace discarded try_init result with OnceLock<bool> so the first
ad_set_log_callback call with a non-null callback returns ErrInternal when a
foreign global subscriber already owns the process, rather than silently no-oping.
Re-registration and NULL-unregister paths are unaffected. Adds three unit tests
covering the new routing logic.

Finding #8: soften ad_init and AD_ABI_VERSION_MAJOR rustdoc from mandatory
pre-call requirement to recommended ABI-compatibility check; behaviour unchanged.

Finding #12: rewrite ad_get_tree rustdoc to direct observe-act agents to
ad_snapshot (refs, refmap, JSON envelope) and reserve ad_get_tree for ref-less
raw-tree consumers; removes stale "invoke the CLI" guidance.

* refactor: unify FFI command envelope serialisation and fix wait/status error contract

Extract write_command_envelope into commands/envelope_out.rs, eliminating
the ~35-line guard→serialize→string_to_c block duplicated across all five
command modules (snapshot, execute_by_ref, wait, status, version).

ad_wait and ad_status now write the error JSON envelope into *out on
command-level failures (TIMEOUT, ELEMENT_NOT_FOUND, etc.), matching the
behaviour already present in ad_snapshot and ad_execute_by_ref. Guard and
infrastructure rejections (null adapter/out/args, off-main-thread, invalid
UTF-8) continue to leave *out null — the infra/command boundary is
preserved exactly.

Add ad_wait_command_error_writes_error_envelope_into_out to verify the
unified contract: a zero-timeout element wait that cannot be satisfied must
produce an ok:false envelope in *out, not a null pointer.

* test: add unit tests to write_command_envelope proving #3 contract

* build: regenerate FFI header after round-1 review fixes

* docs(ffi): document ownership/lifetime contracts on adapter destroy and wait

Add Safety note to ad_adapter_destroy that callers must not destroy the
handle while any call on it is in flight on another thread — concurrent
destroy + in-flight call is use-after-free (confirmed reachable: destroy
is main-thread-exempt while ad_wait holds &*adapter through its blocking
wait::execute loop).

Add blocking-duration and adapter-lifetime note to ad_wait.

Extend ad_snapshot's existing partial note to cover explicit window
targeting (window_id, not yet ABI-exposed) alongside skeleton/drill-down,
and add CLI guidance for agents that need those features today.

* refactor(ffi): route ad_execute_by_ref through canonical core pipeline

Fix four coupled architectural findings in the FFI execute_by_ref path:

#2 (P1): Delete bespoke run_ref_action that bypassed CommandContext tracing
and hard-coded ref_id "<ffi>". Route ad_execute_by_ref through a new
commands::execute_by_ref::execute in core, which calls the same
execute_ref_action_with_context pipeline the CLI click/type/etc. commands
use — full trace, strict resolution, and actionability preflight.

#7 (P2): Add nullable snapshot_id: *const c_char parameter as the third
argument. Tri-state: null → latest snapshot (prior behaviour), valid UTF-8
→ pin that snapshot id, non-null invalid UTF-8 → ErrInvalidArgs. Passes
snapshot_str.as_deref() to RefArgs.snapshot_id mirroring CLI --snapshot
semantics. Update all 7 call sites in c_abi_lifecycle.rs and the extern
decl in tests/common/mod.rs (c_abi_actions.rs had no sites).

#10 (P2): Make core the single source of policy truth. Add
Action::base_interaction_policy() (delegates to may_use_focus_fallback —
TypeText + PressKey → focus_fallback, everything else → headless) and
InteractionPolicy::join(self, other) (bitwise-OR on the two capability
flags: elevate-only, never downgrade). FFI maps AdPolicyKind → core
InteractionPolicy and calls base.join(caller_ip); core::execute_by_ref
does the base+elevation. Delete the bespoke effective_action_policy fn and
its six duplicated unit tests from the FFI; repointed truth-table coverage
lives in the six FFI tests that delegate to core and the join unit tests in
interaction_policy.rs. Note: PressKey's base shifts headless→focus_fallback
vs the old FFI-only fn — intentional alignment with the full CLI table.

#6 (P1, light): Success and error paths already route through the shared
write_command_envelope helper (no reintroduction of bespoke serialization).
Add a /// note on ad_execute_by_ref documenting dispatch-before-serialize
ordering so callers understand the near-impossible ErrInternal-after-action
scenario without requiring heavy pre-validation machinery.

* build: regenerate FFI header after core-routing fixes

* test(ffi): split c_abi_lifecycle and add observe→act roundtrip tests

Finding #4: c_abi_lifecycle.rs was 1127 LOC, violating the 400-LOC cap.
Extracted focused modules each well under 400 lines:
- c_abi_init.rs        (54 LOC) — ad_abi_version / ad_init
- c_abi_session.rs     (88 LOC) — session adapter ctor
- c_abi_log_callback.rs(182 LOC) — log callback tests + statics
- c_abi_json_commands.rs(165 LOC) — ad_version / ad_status
- c_abi_snapshot.rs    (131 LOC) — ad_snapshot guard + envelope tests
- c_abi_wait.rs        (178 LOC) — ad_wait guard + error-envelope tests
- c_abi_execute_by_ref.rs(167 LOC) — ad_execute_by_ref guard tests
c_abi_lifecycle.rs shrunk to 176 LOC (null-safety + list lifecycle).

Finding #5: add snapshot→execute_by_ref roundtrip tests.
- stale_ref_returns_ok_false_error_envelope: always runs in CI; sets a
  temp HOME (empty refmap), calls ad_execute_by_ref(@e1), asserts
  ok:false + error.code when the command path executes, tolerates
  ErrInternal/null-out for the macOS main-thread guard.
- snapshot_execute_by_ref_live_roundtrip: #[ignore]; needs AX permission,
  a live app, and main-thread execution (E2E harness).

* fix(ffi): zero ad_wait *out before args guard; correct PressKey policy docs

Review of the review fixes:
- ad_wait now zeroes *out before the args null-check so a null-args
  rejection honours the documented *out-zeroed contract (matches the
  other command entrypoints).
- Corrected the PressKey policy doc wording: focus_fallback is the
  shared base from Action::base_interaction_policy, not a claim of CLI
  ref-PressKey parity (no CLI ref-PressKey action exists).

* chore: remove stray rust_out build artifact and gitignore it

* fix(ffi): thread session context through legacy ref-action path and correct doc steering

Add execute_entry_with_context to core so the FFI legacy path
(ad_execute_ref_action_with_policy) threads the adapter's real
CommandContext — built from the session id set at adapter creation —
instead of CommandContext::default(). The existing execute_entry
delegates to it with default() so all other callers are unchanged.

Rewrite the /// docs on all three native-handle / legacy-struct
entrypoints (ad_execute_action, ad_execute_action_with_policy,
ad_execute_ref_action_with_policy) to remove the false "CLI parity"
steering and describe each accurately as a low-level escape hatch with
verbatim-policy dispatch. Point observe→act callers at ad_execute_by_ref.

Add core tests confirming execute_entry_with_context succeeds, delegates
correctly, and emits trace events when a trace path is configured. Add FFI
test confirming the session-adapter path resolves to the same error class
as the no-session path (session id is wired into trace, not the error
surface).

* refactor: unify CLI ref-action policy via CommandContext::request_base

All 16 ref-action commands previously hardcoded InteractionPolicy literals
that duplicated the intent already encoded in Action::base_interaction_policy().
Add CommandContext::request_base(action) which reads the canonical base from
the action itself and delegates to request(), then replace every hardcoded
call site so there is a single policy source of truth for CLI and FFI.

Update the all_context_request_callers_are_policy_tested guard to search for
context.request_base( rather than context.request(.

Behavior is byte-identical: headless() and focus_fallback() literals were
exact mirrors of what base_interaction_policy() returns for each action.

* docs(ffi): fix Safety doc, add header preamble, document live-test deferral

Finding #9: correct the # Safety section on ad_execute_by_ref to distinguish
ref_id (non-null required; null is defined but rejected with ErrInvalidArgs)
from snapshot_id (null is meaningful — latest snapshot). The param tri-state
doc was already accurate; only the Safety section implied false symmetry.

Finding #10: add an agent-workflow orientation preamble to cbindgen.toml via
the after_includes key. The 13-line C block-comment lands after sys-includes
and before declarations (inside the include guard), covering the full
ad_init → ad_adapter_create → ad_snapshot → ad_execute_by_ref →
ad_free_string / ad_adapter_destroy loop with the macOS main-thread
requirement. Verified via local cbindgen 0.29.4 regeneration; header not
committed (orchestrator regenerates).

Findings #2/#8: expand module doc and live-roundtrip #[ignore] test doc to
precisely explain why the full observe→act loop cannot run under libtest on
macOS (off-main-thread scheduler + AX guard), how to run manually, and that
the full-loop CI proof is deferred to plan unit U9 / Phase B (Python ctypes
external-consumer harness). Strengthen the always-running error-envelope test
with an error.message assertion (guaranteed by the error contract) without
pinning error.code (which varies by load path; pinning would cause flakiness).

* build: regenerate FFI header after round-2 review fixes

* fix: correct FFI doc/test gaps found in PR #67 audit

#7: replace non-existent ad_click/ad_type_text references in ad_get_tree
rustdoc with the real entrypoint (ad_execute_by_ref + AdAction).

#6: extend cbindgen.toml preamble with snapshot_id round-trip (pass
data.snapshot_id back to pin snapshot vs NULL for latest), AdAction
construction (zero-init + set kind + kind-specific fields), and policy
semantics (0=keeps built-in base, 2=Headed allows cursor/focus fallbacks).

#2: add dispatch-before-serialize note to ad_snapshot matching the
existing note on ad_execute_by_ref — refmap is written before JSON
serialisation, so a serialisation failure leaves *out null + ErrInternal
while the refmap is already on disk.

#10: add out-of-range policy test in c_abi_execute_by_ref.rs that passes
policy=99 and asserts ErrInvalidArgs|ErrInternal + out null, with the
last-error assertion gated on ErrInvalidArgs to tolerate macOS off-main-
thread CI returning ErrInternal before the policy check is reached.

* fix: make error suggestions transport-neutral and populate retry_command

Finding #4: STALE_REF, AMBIGUOUS_TARGET, ELEMENT_NOT_FOUND, SNAPSHOT_NOT_FOUND,
NOTIFICATION_NOT_FOUND, and POLICY_DENIED suggestion strings previously referenced
CLI-only flags (--skeleton, --snapshot, --session, --headed) or bare CLI subcommand
syntax (snapshot, list-notifications), making them meaningless to FFI consumers.
Rewritten to name the operation neutrally with both CLI and FFI equivalents in
parentheses.

Finding #15: ErrorPayload::retry_command was always None; the with_retry builder
existed but was never called from from_app_error. Added retry_token_for_code() to
populate the field for mechanically retryable codes: STALE_REF and SNAPSHOT_NOT_FOUND
get "snapshot;execute_by_ref" (re-snapshot then re-execute by ref); POLICY_DENIED gets
"escalate_policy" (policy escalation, not a re-run of the same call). All other codes
remain None to avoid misleading auto-retry loops.

Tests updated: stale_ref_suggestion_mentions_skeleton renamed and tightened to assert
transport-neutral content; snapshot_ref_tests.rs skeleton assertion updated to match;
four new output.rs tests cover the populated tokens and the None cases.

* fix(trace): wire session_id into every JSONL record, improve FFI ref label, drop duplicate test

#9: thread session_id (Option<&str>) through TraceConfig::emit/emit_lazy/write_event and
insert it as a top-level field after the sanitized fields block. This makes the docs true
for everyone (CLI + FFI when both --session and --trace are provided). session_id is not
in SENSITIVE_KEYS and is intentionally inserted after sanitize_trace_value so it cannot
be shadowed or redacted. Omitted entirely when context has no session (no null key).

#8: replace the opaque "<ffi>" ref label in execute_entry_with_context with a
role/path-index-derived label (e.g. "<button/2/0/3>"). Uses only role and numeric path
indices — no content fields — so the label is safe to emit in the unredacted "ref"
trace key. Improves per-record correlation in multi-element FFI trace logs.

#14: delete execute_entry_delegates_to_entry_with_context from ref_action_tests.rs.
The test was byte-identical in assertions to failed_action_still_releases_resolved_handle
and added no new coverage. Removed rather than forced a weak rewrite.

Tests added/updated:
- context.rs: trace_injects_session_id_as_top_level_unredacted_field (session set),
  trace_writes_jsonl_without_stdout_dependency extended with session_id absence assert
- ref_action_tests.rs: execute_entry_with_context_emits_trace_events extended to assert
  session_id in every emitted record; trace_records_omit_session_id_when_context_has_none;
  ref_label_from_entry_uses_role_and_path_indices

* fix(core): make stale-ref actionability suggestion transport-neutral; regen header

Mirrors the round-3 transport-neutral suggestion rewrite for the
live-staleness path in actionability, plus the regenerated FFI header
carrying the round-3 doc/preamble updates.

* refactor: dedup ffi policy mapping and error conversion helpers

Replace the duplicated AdPolicyKind→InteractionPolicy three-arm match in
execute_by_ref.rs with a canonical to_interaction_policy() method on
AdPolicyKind. Delegate action_request() in actions/execute.rs to the same
method, replacing its own redundant match. Replace the inline AppError→
AdapterError match in ad_execute_ref_action_with_policy with the shared
app_error_to_adapter helper already used everywhere else.

* refactor(ffi): apply ponytail/simplify cuts (then_some, trim core doc)

Use bool::then_some for ad_wait optional scalars; drop the FFI-history
sentence from the core execute_by_ref doc (the FFI doc retains it for C
binding authors). Behavior unchanged.
2026-06-25 19:24:06 -07:00
dependabot[bot]
4a6d8aa308
chore(deps): bump the github-actions group across 1 directory with 3 updates (#65)
Some checks are pending
CI / Format (push) Waiting to run
CI / Test (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
Release / Release Please (push) Waiting to run
Release / Build (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (x86_64-pc-windows-msvc) (push) Blocked by required conditions
Release / Publish to GitHub Release (push) Blocked by required conditions
Release / Publish to npm (push) Blocked by required conditions
Release / Publish Skills to ClawHub (push) Blocked by required conditions
Supply Chain / Audit (push) Waiting to run
Bumps the github-actions group with 3 updates in the / directory: [actions/checkout](https://github.com/actions/checkout), [actions/cache](https://github.com/actions/cache) and [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action).


Updates `actions/checkout` from 6.0.3 to 7.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](df4cb1c069...9c091bb21b)

Updates `actions/cache` from 5.0.5 to 6.0.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](27d5ce7f10...2c8a9bd745)

Updates `zizmorcore/zizmor-action` from 0.5.6 to 0.5.7
- [Release notes](https://github.com/zizmorcore/zizmor-action/releases)
- [Commits](5f14fd08f7...192e21d79a)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: zizmorcore/zizmor-action
  dependency-version: 0.5.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Lahfir <70215676+lahfir@users.noreply.github.com>
2026-06-24 15:43:37 -07:00
github-actions[bot]
d560d59945
chore(main): release 0.4.0 (#66)
* chore(main): release 0.4.0

* chore: sync cargo lock for release 1.85

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-24 18:01:36 -04:00
Lahfir
dbb2be639e
refactor!: over-engineering audit cleanup (#64)
* refactor!: drop vestigial version --json flag and dead code

The tool emits a JSON envelope for every command and has no plain-text
mode, so the version --json flag toggled nothing. Remove it end-to-end:
CLI flag, core VersionArgs, batch path, dispatch/main call sites, and the
snapshot test. Correct the stale skill docs (they documented a non-existent
plain-string default and wrong platform/arch keys; actual keys are
version/target/os, always inside the JSON envelope).

Also: delete dead code (AppContext, WindowContext, Response::with_app and
their re-export; core BatchArgs); inline single-caller helpers
(strip_ref_bounds_when_hidden, helpers::find_window_for_pid,
permissions::render); use libc::kill instead of a hand-declared extern in
refs_lock.

BREAKING CHANGE: the version command no longer accepts --json; it always
emits the standard JSON envelope.

* refactor: inline single-caller ffi conversions, drop dead from_c

Inline direction_from_c and snapshot_surface_to_core into their sole
callers. Remove AdImageFormat::from_c (production converts Rust->C only;
the C->Rust direction was dead, #[allow(dead_code)]) together with its
unit test and fuzz line. Drop the ErrorCode/AdResult cardinality assert:
the exhaustive error_code_to_result match plus the per-discriminant pins
already enforce the ABI invariant.

* refactor: dedup keycode table, inline single-caller macos helpers

key_to_keycode delegated the same 52-entry table that
keyboard_map::key_name_to_code already owns; delegate to it, guarding the
modifier names so a modifier passed as the primary key still resolves to
None (preserving existing behavior). Inline windows_from_records,
is_window_candidate, make_output, and the normalized_role_for_element
wrapper (which let one caller classify the same element twice); collapse
accessibility_report_state to its one-line form. Swap four local
HashSet dedup sets to FxHashSet (rustc-hash, already a dependency).

* chore: mark internal crates publish = false

These crates are workspace-internal (consumed only via path deps; CI never runs cargo publish). Marking them unpublishable prevents accidental crates.io release and makes removing unreferenced pub items unambiguously safe.

* fix: finish version --json removal in ci smoke test and help text

The version --json flag was dropped earlier but two references survived:
the npm smoke-test in ci.yml (which now errored on the unknown flag and
failed the job) and the --help text that still advertised it. Point both
at plain version, which emits the same JSON envelope. Add a batch
regression test asserting version now rejects args, and drop the
misleading {"json":true} from the stop-on-error fixture.

* test: guard ErrorCode<->AdResult bijection after counter removal

Code review flagged that dropping the cardinality counters left reverse
drift (a new AdResult error variant without a matching ErrorCode)
unguarded. The counters only weakly approximated this anyway: they
compared two hand-maintained variant arrays to each other, decoupled
from the real enums.

Replace them with a stronger, leaner guard: an exhaustive reverse match
(error_code_origin) that fails to compile if an AdResult error variant
is added without declaring its ErrorCode origin, plus a round-trip test
asserting the forward and reverse maps agree -- a consistency check the
counters never performed. Document the bijection contract on
error_code_to_result.
2026-06-24 14:31:17 -07:00
github-actions[bot]
93a4bb1e2a
chore(main): release 0.3.1 (#63)
Some checks failed
CI / Format (push) Has been cancelled
CI / Test (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
Release / Release Please (push) Has been cancelled
Supply Chain / Audit (push) Has been cancelled
Release / Build (aarch64-apple-darwin) (push) Has been cancelled
Release / Build (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (aarch64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-apple-darwin) (push) Has been cancelled
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Has been cancelled
Release / Build FFI (x86_64-pc-windows-msvc) (push) Has been cancelled
Release / Publish to GitHub Release (push) Has been cancelled
Release / Publish to npm (push) Has been cancelled
Release / Publish Skills to ClawHub (push) Has been cancelled
* chore(main): release 0.3.1

* chore: sync cargo lock for release 1.85

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-20 21:40:42 -04:00
Lahfir
9f144c2caf
fix: harden macos stale ref resolution (#62)
* fix: harden macos stale ref resolution

* fix: tighten stale ref fallback resolution

* fix: fail closed for gone titled windows

* refactor: tidy stale ref resolver naming
2026-06-20 21:37:46 -04:00
Lahfir
906deec406 docs: update bundled skill versions
Some checks are pending
CI / Format (push) Waiting to run
CI / Test (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (rust) (push) Waiting to run
Release / Release Please (push) Waiting to run
Release / Build (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (aarch64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-apple-darwin) (push) Blocked by required conditions
Release / Build FFI (x86_64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (aarch64-unknown-linux-gnu) (push) Blocked by required conditions
Release / Build FFI (x86_64-pc-windows-msvc) (push) Blocked by required conditions
Release / Publish to GitHub Release (push) Blocked by required conditions
Release / Publish to npm (push) Blocked by required conditions
Release / Publish Skills to ClawHub (push) Blocked by required conditions
Supply Chain / Audit (push) Waiting to run
2026-06-20 16:02:41 -07:00
Lahfir
c0d52334c5 docs: update phase roadmap for v0.3.0 2026-06-20 13:13:33 -07:00
Lahfir
115e31945d ci: fix clawhub skill publish command 2026-06-20 13:04:46 -07:00