From 49ca96ef3fcd8a796e914e6a99c2ef27b0d424c8 Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 18 Sep 2026 08:22:47 -0400 Subject: [PATCH 1/4] chore(ats): pin vendored adapter to merged Windows fix --- packages/ats-skills-source.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ats-skills-source.json b/packages/ats-skills-source.json index e1fff19..6422268 100644 --- a/packages/ats-skills-source.json +++ b/packages/ats-skills-source.json @@ -2,14 +2,14 @@ "schema_version": "aether.ats.vendor/1", "repository": "AetherAI3/ATSv2", "path": "electron-app-v2/packages/aether-ats-skills", - "revision": "2c38586b7fb011163281fb964b2b9585398717e8", + "revision": "ecdbc5c296f28f2331aea4e031c459f1171b99ea", "files": { "LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", "README.md": "ccad3cffc19e5f9d451dfce9d708adb1c88c1ac0acaa85c5c809b04b62e64718", "SETTINGS.md": "aa4622824e2a15798e2bf3a139c55f824db346458fc48d85c5aacf0cb555dd9f", "bin/aether-ats-skills.js": "cf13b49ce174f5bb13da0daa93e38e3fbb926c44beaa7d7a7b827c8c2cba4c3d", "package.json": "1a40fd6e767e21f823c251a70ed37cb9e34448a566fe16c2b54e9680086c4f29", - "python/bridge.py": "8b3ad23101f2f36acdf1ff1e1481be2b0116947cdc664a942563ca59baf4f671", + "python/bridge.py": "4df7be96d14456d23292a8f0dbf71ceff2ed73b27f25b5ede74eabe87fd916aa", "python/memory_lease.py": "d445fc8e59adc749d67ff7dc539d45017a8e56af22420be69ff0bccc9ece1532", "src/browser.js": "b816ff29bec0d4e38f62a5fcd77d6d16aaa8b06d3a2438a595cdfd20d38f31f0", "src/browser_recovery.js": "f92324dfd104772e1f32f0b6d6eaf890a45929a1209652b0937b2c62d721ad08", From e01432d3bf06b584456e197c5b97f2527c16f8e6 Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 18 Sep 2026 08:22:49 -0400 Subject: [PATCH 2/4] fix(ats): make native memory durability check Windows-safe --- packages/ats-skills/python/bridge.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/ats-skills/python/bridge.py b/packages/ats-skills/python/bridge.py index e2bf211..6f4aed1 100644 --- a/packages/ats-skills/python/bridge.py +++ b/packages/ats-skills/python/bridge.py @@ -39,6 +39,24 @@ def sync_directory(path): os.close(descriptor) +def sync_native_file(path: Path): + """Flush a native file using a writable descriptor on every platform. + + Windows rejects fsync on a read-only descriptor (Errno 9). Native setup + files are owned by this adapter, so opening them read/write is safe and + keeps the durability check meaningful on both POSIX and Windows. + """ + reject_links(path) + flags = os.O_RDWR | getattr(os, "O_BINARY", 0) | getattr(os, "O_NOFOLLOW", 0) + descriptor = os.open(path, flags) + try: + if not stat.S_ISREG(os.fstat(descriptor).st_mode): + raise ValueError("Native memory file must be a regular file") + os.fsync(descriptor) + finally: + os.close(descriptor) + + def atomic_json(path: Path, value): temp = path.with_name(path.name + "." + uuid.uuid4().hex + ".tmp") try: @@ -320,8 +338,7 @@ def initialize_memory(request): native_config["dir"] = str(path) (stage / "config.json").write_text(json.dumps(native_config, sort_keys=True, indent=2), encoding="utf-8") for name in NATIVE_FILES: - with (stage / name).open("rb") as handle: - os.fsync(handle.fileno()) + sync_native_file(stage / name) transaction = {**transaction, "phase": "publishing", "files": {name: digest(stage / name) for name in sorted(NATIVE_FILES)}} atomic_json(transaction_path, transaction) if transaction and transaction["phase"] == "publishing": @@ -391,8 +408,7 @@ def close_verification_pool(): # The binding is the readiness commit: all native files were reopened. if not existing: for name in NATIVE_FILES: - with (path / name).open("rb") as handle: - os.fsync(handle.fileno()) + sync_native_file(path / name) atomic_json(binding_path, receipt) if transaction: if stage.exists(): From 5804d069d7381f9f98d5ce190b9bfa90c8fce01c Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 18 Sep 2026 08:22:51 -0400 Subject: [PATCH 3/4] fix(ats): surface actionable memory setup failures --- src/commands/ats_agent.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/commands/ats_agent.ts b/src/commands/ats_agent.ts index 0077a7b..fe6dea0 100644 --- a/src/commands/ats_agent.ts +++ b/src/commands/ats_agent.ts @@ -93,7 +93,14 @@ async function loadPackage(): Promise { } function verifyMemory(receipt: Record, binding: Binding): void { - if (receipt["state"] !== "ready" || receipt["persistence_verified"] !== true + if (receipt["state"] !== "ready") { + const code = typeof receipt["code"] === "string" ? receipt["code"].replace(/\s+/g, " ").slice(0, 120) : ""; + const message = typeof receipt["message"] === "string" ? receipt["message"].replace(/\s+/g, " ").slice(0, 300) : ""; + const detail = [code, message].filter(Boolean).join(": "); + if (detail) throw new Error(`ATS memory setup unavailable${detail ? ` (${detail})` : ""}.`); + throw new Error("ATS memory did not return a verified ready receipt matching this agent, directory and size."); + } + if (receipt["persistence_verified"] !== true || receipt["agent_id"] !== binding.agent_id || receipt["directory"] !== binding.memory_directory || receipt["size_gb"] !== binding.memory_gb || receipt["schema_version"] !== "aether.ats.memory/1" || receipt["backend"] !== "aether-context" From b28a3d68779c62b17b0c7977b490f114482159e0 Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 18 Sep 2026 08:22:52 -0400 Subject: [PATCH 4/4] test(ats): cover actionable memory setup failures --- test/ats_agent.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/ats_agent.test.ts b/test/ats_agent.test.ts index c234e80..39d34a4 100644 --- a/test/ats_agent.test.ts +++ b/test/ats_agent.test.ts @@ -134,6 +134,18 @@ test("setup failure preserves the created Cloud draft and never saves a ready lo }); }); +test("ATS setup surfaces a safe actionable memory-engine failure", async () => { + await fixture(async (dir) => { + let output = ""; + const hooks = createAtsHooks({ root: dir, output: (text) => { output += text; }, setup: async () => ({ memoryGb: 5, strategiesDirectory: join(dir, "strategies") }), + load: async () => fakePackage({ initializeMemory: async () => ({ state: "unavailable", code: "CONTEXT_ENGINE_UNAVAILABLE", message: "Install the pinned aether-context engine, or select its Python interpreter." }) }) }); + await withCreate(async () => { assert.equal(await hooks.createATS!(context(), "Market Scout"), 1); }); + assert.match(output, /CONTEXT_ENGINE_UNAVAILABLE/); + assert.match(output, /Install the pinned aether-context engine/); + assert.doesNotMatch(output, /token|api.?key|aek_/i); + }); +}); + test("successful setup stores an account-scoped binding only after memory and strategy checks", async () => { await fixture(async (dir) => { const calls: string[] = [];