Sync coding-agent knowledge base

This commit is contained in:
vczh
2026-07-28 01:27:08 -07:00
parent ecdb39c2f8
commit 0ef0273ac4
3 changed files with 268 additions and 4 deletions
+265 -2
View File
@@ -388,8 +388,271 @@ Do not panic or wait indefinitely. Once a native window is suspected, switch to
## Linux Specific
(to be editing...)
Use the GacUI automation service for all GacUI-owned windows. On wGac, normal
and hosted applications expose `Controls` and `IO`; the native remote renderer
exposes `Dom` and renderer-side `IO`. Linux computer use is the fallback for an
OS-native modal window, not a replacement for those endpoints.
A native portal dialog runs a synchronous nested modal loop while the portal
backend shows a window from another process. wGac keeps Wayland events, timers,
and queued MiniHTTP main-thread work advancing during that loop, but the native
dialog still belongs to the portal process and is absent from GacUI
`Controls`. Inspect and operate it from a separate process with AT-SPI. Avoid
driving controls in the underlying GacUI window until the modal call finishes,
because doing so would re-enter application code.
On GNOME Wayland, wGac's XDG Desktop Portal file chooser is normally exposed
through AT-SPI by `xdg-desktop-portal-gnome`. The accessible application and
PID therefore need not match `Test_FullControlTest`. Locate the window by its
application-supplied title and inspect its descendants. The Full Control Test
uses `The Title`; do not assume that title for another application.
Hosted Full Control Test uses `FakeDialogService`, so its dialogs stay in the
GacUI `Controls` tree and must be operated through `IO`, not AT-SPI.
Install the AT-SPI Python introspection packages if they are unavailable:
```bash
sudo apt install python3-gi gir1.2-atspi-2.0
```
For an unattended visual check on Wayland, call the XDG Desktop Portal
`Screenshot` method with `interactive=false`. This captures the complete
desktop without opening a rectangle picker or requiring user input:
```bash
/usr/bin/python3 - "/tmp/gacui-linux-desktop.png" <<'PY'
import os, sys, uuid, gi
gi.require_version("Gio", "2.0")
from gi.repository import Gio, GLib
OUTPUT = os.path.abspath(sys.argv[1])
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
token = "gacui_" + uuid.uuid4().hex
sender = bus.get_unique_name()[1:].replace(".", "_")
request_path = f"/org/freedesktop/portal/desktop/request/{sender}/{token}"
result = {}
loop = GLib.MainLoop()
def on_response(connection, sender_name, object_path, interface_name, signal_name, parameters):
code, values = parameters.unpack()
result["code"] = code
result["uri"] = values.get("uri")
loop.quit()
subscription = bus.signal_subscribe(
"org.freedesktop.portal.Desktop",
"org.freedesktop.portal.Request",
"Response",
request_path,
None,
Gio.DBusSignalFlags.NONE,
on_response,
)
returned_path = bus.call_sync(
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
"org.freedesktop.portal.Screenshot",
"Screenshot",
GLib.Variant("(sa{sv})", ("", {
"handle_token": GLib.Variant("s", token),
"interactive": GLib.Variant("b", False),
})),
GLib.VariantType("(o)"),
Gio.DBusCallFlags.NONE,
-1,
None,
).unpack()[0]
if returned_path != request_path:
raise SystemExit(f"Unexpected portal request path: {returned_path}")
timeout = GLib.timeout_add_seconds(15, lambda: (loop.quit(), False)[1])
loop.run()
GLib.source_remove(timeout)
bus.signal_unsubscribe(subscription)
if result.get("code") != 0 or not result.get("uri"):
raise SystemExit(f"Screenshot failed: {result}")
source = Gio.File.new_for_uri(result["uri"])
target = Gio.File.new_for_path(OUTPUT)
source.copy(target, Gio.FileCopyFlags.OVERWRITE, None, None)
print(OUTPUT)
PY
```
Require a zero exit code and inspect the saved PNG. The desktop portal may
return a temporary source URI, so keep the explicitly copied output file as
the evidence artifact.
Run inspection from the same logged-in graphical session as the application.
This helper finds every accessible object with the requested title and prints
its complete subtree, including action names:
```bash
/usr/bin/python3 - "The Title" <<'PY'
import sys, gi
gi.require_version("Atspi", "2.0")
from gi.repository import Atspi
TITLE = sys.argv[1]
def children(n):
try: return [n.get_child_at_index(i) for i in range(max(0, n.get_child_count()))]
except Exception: return []
def walk(n):
yield n
for c in children(n): yield from walk(c)
def showing(n):
try: return n.get_state_set().contains(Atspi.StateType.SHOWING)
except Exception: return False
def actions(n):
try: return [n.get_action_name(i) for i in range(max(0, n.get_n_actions()))]
except Exception: return []
def describe(n, depth=0):
try: print(f"{' '*depth}{n.get_role_name()} name={(n.get_name() or '')!r} actions={actions(n)}")
except Exception as e: print(f"{' '*depth}<unavailable: {e}>")
for c in children(n): describe(c, depth+1)
desktop = Atspi.get_desktop(0)
found=[]
for app in children(desktop):
for n in walk(app):
try:
if n.get_role() == Atspi.Role.DIALOG and (n.get_name() or "") == TITLE and showing(n): found.append((app,n))
except Exception: pass
if len(found) != 1: raise SystemExit(f"Expected one visible dialog {TITLE!r}, found {len(found)}")
app, dialog = found[0]
print(f"owner={(app.get_name() or '')!r} pid={app.get_process_id()}")
describe(dialog)
PY
```
Read the reported names, roles, hierarchy, and actions before interacting. A
GNOME portal file chooser exposes `Cancel` and `Select` or an operation-specific
accept button as push buttons with a `click` action. When the goal is only to
unblock a test, prefer the least destructive named action, normally `Cancel`.
The following helper clicks one exact push-button name inside one exact titled
window:
```bash
/usr/bin/python3 - "The Title" "Cancel" <<'PY'
import sys, time, gi
gi.require_version("Atspi", "2.0")
from gi.repository import Atspi
TITLE, BUTTON = sys.argv[1:3]
def children(n):
try: return [n.get_child_at_index(i) for i in range(max(0, n.get_child_count()))]
except Exception: return []
def walk(n):
yield n
for c in children(n): yield from walk(c)
def showing(n):
try: return n.get_state_set().contains(Atspi.StateType.SHOWING)
except Exception: return False
def dialogs():
result=[]
desktop=Atspi.get_desktop(0)
for app in children(desktop):
for n in walk(app):
try:
if n.get_role() == Atspi.Role.DIALOG and (n.get_name() or "") == TITLE and showing(n): result.append(n)
except Exception: pass
return result
ds=dialogs()
if len(ds) != 1: raise SystemExit(f"Expected one visible dialog {TITLE!r}, found {len(ds)}")
candidates=[]
for n in walk(ds[0]):
try:
if n.get_role() == Atspi.Role.PUSH_BUTTON and (n.get_name() or "") == BUTTON and showing(n):
clicks=[i for i in range(max(0,n.get_n_actions())) if n.get_action_name(i)=="click"]
if len(clicks)==1: candidates.append((n,clicks[0]))
except Exception: pass
if len(candidates) != 1: raise SystemExit(f"Expected one visible clickable {BUTTON!r}, found {len(candidates)}")
if not candidates[0][0].do_action(candidates[0][1]): raise SystemExit("AT-SPI click rejected")
deadline=time.monotonic()+5
while dialogs() and time.monotonic()<deadline: time.sleep(0.1)
if dialogs(): raise SystemExit("Dialog remained visible after click")
PY
```
After the action, require the native dialog to disappear. Then resume the
GacUI `Controls`/`Dom` and `IO` checks and verify the expected application
state. If the dialog remains, enumerate the desktop again: a validation error
or a second modal window may have appeared.
Do not use X11-only tools such as `xdotool` as the primary Wayland procedure.
Do not guess screen coordinates when semantic AT-SPI actions are available.
For a different desktop portal backend or a non-portal crash dialog, enumerate
the complete AT-SPI desktop first and identify the actual owning application,
title, prompt, and named actions before choosing a response.
## macOS Specific
(to be editing...)
`CocoaAutomationService` handles GacUI windows in a normal application,
`CocoaAutomationServiceHosted` handles hosted mode, and
`CocoaAutomationServiceRenderer` handles a native remote renderer. Use their
HTTP `Controls`/`Dom` and `IO` endpoints for all GacUI-owned UI. Computer use is
needed only for OS-native modal dialogs.
Full Control Test without `--hosted` deliberately uses native AppKit dialogs.
A native modal dialog blocks the application's UI thread, so an automation
request that needs that thread can time out until the dialog is dismissed.
Stop polling the GacUI endpoint when this happens and inspect the target process
from a separate `osascript` process.
The terminal, IDE, or agent host running `osascript` needs Accessibility
permission under **System Settings > Privacy & Security > Accessibility**.
Do not work around a permission error by repeatedly polling the blocked
application. Grant permission to the actual host process, then retry the
read-only inspection.
First list the target application's top-level windows:
```bash
osascript -e \
'tell application "System Events" to tell process "Test_FullControlTest" to return name of every window'
```
An `NSOpenPanel` or `NSSavePanel` can be a separate top-level window rather
than a sheet. Its title can also be the application-supplied dialog title, not
`Open` or `Save`. Inspect every window and its recursively nested controls:
```bash
osascript <<'APPLESCRIPT'
tell application "System Events"
tell process "Test_FullControlTest"
repeat with targetWindow in windows
log "WINDOW: " & (name of targetWindow)
try
log entire contents of targetWindow
end try
end repeat
end tell
end tell
APPLESCRIPT
```
The resulting object descriptions include the exact hierarchy needed for the
next command. For example, the Full Control Test open-file panel exposes
`Cancel` and `Open` under its first splitter group. Prefer the least destructive
action when the goal is only to unblock the test:
```bash
osascript -e \
'tell application "System Events" to tell process "Test_FullControlTest" to click button "Cancel" of splitter group 1 of window "The Title"'
```
Do not assume that hierarchy for another macOS version or dialog type. Re-read
`entire contents`, identify the button by its visible name, and use the exact
reported containment path. For a message dialog, choose the button after
reading its prompt. For file, color, and font panels, use the named controls
reported by Accessibility; canceling is preferable unless the scenario
requires accepting a specific value.
If a visual check is necessary, take a screenshot without interacting with the
dialog:
```bash
screencapture -x /tmp/gacui-native-dialog.png
```
After dismissing or accepting the native dialog, require the top-level dialog
window to disappear, then return to the GacUI automation endpoint and verify
the resulting application state. Hosted Full Control Test uses
`FakeDialogService`; those dialogs remain inside the GacUI control tree and do
not require this native-dialog procedure.
+2 -2
View File
@@ -11,9 +11,9 @@
- Fix behavior at the owning state instead of patching symptoms [7]
- Make `Stop()` drain asynchronous work before returning [6]
- Validate expectations against implementation and existing tests [5]
- Verify and localize portability on every target OS [5]
- Use `WString::IndexOf` with `wchar_t` (not `const wchar_t*`) [4]
- Use `collections::BinarySearchLambda` on contiguous buffers (guard empty) [4]
- Verify and localize portability on every target OS [4]
- Use `vl::Exception` for expected semantic failures and `CHECK_ERROR` for invariants [3]
- Extract abstractions only for real shared behavior [3]
- Do not assume async callback owners are heap allocated [3]
@@ -304,7 +304,7 @@ When a bug reproduces on one machine but not another, do not assume hardware spe
## Verify and localize portability on every target OS
Run the relevant tests on every target operating system whose behavior is being claimed, and report only the platforms actually exercised. Use contrasts between passing and failing platforms to narrow investigation toward the failing platform's implementation before changing shared code, while retaining cross-platform regression verification.
Run the relevant tests on every target operating system whose behavior is being claimed, and report only the platforms actually exercised. Use contrasts between passing and failing platforms to narrow investigation toward the failing platform's implementation before changing shared code, while retaining cross-platform regression verification. Instructions may be prepared for an untested operating system only when they are clearly labeled untested; never report that platform as verified.
## Use reentrant POSIX date-time conversions
+1
View File
@@ -203,6 +203,7 @@ Propose any solution you can think of and write them down in the document, you m
- Code files for manual editing, you need to carefully resolve all conflicts.
- Code files that is auto generated, you can just accept the remote change, and redo the testing process to regenerate these file and ensure all test cases passed. `git commit` all local changes during the process.
- After the rebase is done and all test cases passed, `git push` it again.
- If source files are changed in commits rebased into the repo, you are required to run related tests again to ensure the rebase is correct.
- Unless explicitly instructed:
- If the request is separated by multiple tasks like `## Task X`, you are going to make one commit and immediately push after each task.
- Otherwise, only one commit and push is needed for the entire request.