From 179af2e014b33972fc0fe02888dd21797c1efe9f Mon Sep 17 00:00:00 2001 From: AkinoKaede <26006258+AkinoKaede@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:56:23 +0000 Subject: [PATCH] docs: update OpenWiki --- openwiki/.claims/architecture/desktop-ui.json | 60 ++++-- .../architecture/engineering-invariants.json | 80 ++++--- openwiki/.claims/architecture/module-map.json | 38 ++-- .../architecture/native-app-directories.json | 96 +++++---- .../architecture/platform-runtime.json | 90 ++++---- .../shared-module-directories.json | 56 ++--- .../architecture/system-boundaries.json | 44 +--- .../concepts/agent-runtime-and-assistant.json | 126 +++++------ ...rce-localization-and-product-surfaces.json | 106 ++++----- .../concepts/persistence-and-secrets.json | 102 +++++---- .../concepts/ui-design-principles.json | 26 +-- .../integrations/mcp-server-and-bridge.json | 52 ++++- .../operations/build-test-and-release.json | 44 ++-- .../storefront-commerce-release.json | 24 +-- openwiki/.claims/quickstart.json | 44 ++-- .../behavioral-boundaries-and-fixtures.json | 22 +- .../workflows/desktop-drag-and-drop.json | 70 +++--- .../port-forwarding-pool-and-dispatch.json | 24 ++- .../workflows/settings-and-cloud-sync.json | 46 ++-- .../workflows/ssh-session-lifecycle.json | 64 ++++-- openwiki/.last-update.json | 4 +- openwiki/.page-manifest.json | 204 +++++++++--------- openwiki/architecture/desktop-ui.md | 54 ++++- .../architecture/engineering-invariants.md | 29 ++- openwiki/architecture/index.md | 10 +- openwiki/architecture/module-map.md | 18 +- .../architecture/native-app-directories.md | 38 +++- openwiki/architecture/platform-runtime.md | 89 +++++--- .../architecture/shared-module-directories.md | 30 ++- openwiki/architecture/system-boundaries.md | 61 ++++-- .../concepts/agent-runtime-and-assistant.md | 71 +++++- ...merce-localization-and-product-surfaces.md | 53 ++++- openwiki/concepts/index.md | 8 +- openwiki/concepts/persistence-and-secrets.md | 47 +++- openwiki/concepts/ui-design-principles.md | 12 +- openwiki/integrations/index.md | 2 +- .../integrations/mcp-server-and-bridge.md | 44 +++- openwiki/operations/build-test-and-release.md | 43 +++- openwiki/operations/index.md | 2 +- .../operations/storefront-commerce-release.md | 20 +- openwiki/quickstart.md | 12 +- .../behavioral-boundaries-and-fixtures.md | 50 ++++- openwiki/testing/index.md | 2 +- openwiki/workflows/desktop-drag-and-drop.md | 27 ++- openwiki/workflows/index.md | 6 +- .../port-forwarding-pool-and-dispatch.md | 61 +++++- openwiki/workflows/settings-and-cloud-sync.md | 124 +++++++---- openwiki/workflows/ssh-session-lifecycle.md | 68 ++++-- 48 files changed, 1521 insertions(+), 882 deletions(-) diff --git a/openwiki/.claims/architecture/desktop-ui.json b/openwiki/.claims/architecture/desktop-ui.json index dbc8534f..d3f262f9 100644 --- a/openwiki/.claims/architecture/desktop-ui.json +++ b/openwiki/.claims/architecture/desktop-ui.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:633662bc406c2f1cada88105b6b8554fa2b3eeae597a9c8fe35596966e1399ef", + "pageVersion": "sha256:17272c6d1e7e71dddbd782cb168a96b94f69ad656911ba6e0a31d0f4c55ad75e", "claims": [ { "id": "claim_5a53c5a47b7445f7a7b6f8865297a805", @@ -11,7 +11,7 @@ "version": "repo-lines-v1:sha256:b555bddfeefb996e592d9ccd0ceb8bf1add6fab74debb13d95057628e5ccdd32:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI0YTU0NzRjYTQyZGYyYjU5YWQ4MzRlZTEzYjFiNTczYTlhNzkxOWM1ODg1NDZjOTg4MWI5Nzc5MWMwZTliODEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ4ZjE0OGYxNWFlNTBkMmE3MjZlMGRhYmNhMWI2NWQ0NzVmNTYyYmI5YTlmZWMxZmVjMWEzM2EyYzdjZDIwYjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZkMjk5MDMyMTk5YjM3NTAwZDdiYmRhMTQ4OGI1N2YyNmFjYzUyYjgwZTA1MmQ2MzAzNmZkNmRiZGIyNzMyN2IifQ" }, { - "resource": "repo://TFM/App/RootView.swift#L235-L254", + "resource": "repo://TFM/App/RootView.swift#L226-L245", "version": "repo-lines-v1:sha256:22f5a2fdad1de0ae2cc39153e1f11d73f65d1efbd6c349129bb5040308c6f9d8:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBhZDFjODhkMzAwYTM1MWU4M2RhYTc0YzdkYmQ4NmNlMDk1NDNjYWM4YWNlOWE3OWNkZWE2NTA4YjAxNTc5MTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkxOWJmOTM3N2ZiZmU5NWM3NWIxNThlY2NhMjMwMTRlZmMwN2ZkNzZjNmI5Zjk4NjAwNGZlNDcxNDk2OGRiNmIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjkzODYxNjc2YTBjZTEwMDk4YTM5MjEzZDU4ZjcyZDBjZDRlNzk2MjE5MzM1MGNiNmMwY2M3ZjgyOGU0MjhjODIifQ" }, { @@ -22,11 +22,27 @@ }, { "id": "claim_a3a9d8910c524019afd270300d91ffd1", - "statement": "RootView owns one NavigationSplitView and window toolbar, overlays status on the active workspace without docking it, and places Vault, Assistant, Metrics and Settings in front of a persistent workspace deck.", + "statement": "RootView owns one NavigationSplitView and window toolbar, overlays status on the active workspace without docking it, and places Vault, Assistant, Metrics and Settings in front of a persistent workspace deck. RootView retains its window model while AppModel and MainWindowCoordinator register models and native windows weakly without owning abandoned scenes.", "evidence": [ { - "resource": "repo://TFM/App/RootView.swift", - "version": "repo-file-v1:sha256:d0c901e4e4a0bf405d36a1fe66ffd8777529c97165e0eb42013812cd3371ed64" + "resource": "repo://TFM/App/AppModel.swift#L55-L58", + "version": "repo-lines-v1:sha256:f4e7ac9bdb1cbefb0ce9ef00c74ff28b5d46f0df9164befe722936ce692454c5:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDU1MTNkZWZjZmQ4ZjdjYzY5OWNjNzk1OGYxZGVjYmVjMTFlZmM2NjA2YzJjNjUxM2M5M2M5MTY1MTVkYzI0MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg5M2Y0MGUzNzgxZWI4OTZjODJkMDQ2YTA4MDY3YTExZTM1Y2RmMmYxZGZhZjI2ZmVhOTBiOGRiNjY3NjQxMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOTk5YmRlZmVlYWNhNjM0MGUwMjkwZGM4NmU4ODljODlkNWUxODlhYTViYTQ0YjU4NWIzYjcwNzM0ZjRhNjJkOCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYjg2ZDYwNTBhNTYxMmVlZmQyMmM3MDMwYjUzNTRhNWZhYjVkMGI2YmRkNzhhNWQzZTI5Y2FjYmUzMzMyN2M2ZCJ9" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L720-L737", + "version": "repo-lines-v1:sha256:bf7aeacb888a39112b5e4c4c7fec0a8b42abf7c0b927c52a963d4822b414bfce:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZiMDNmZDkyNGM2ZDY3NmQ2Y2Y4YzA1MGUxZWFjZDI4ZDMwNTE4NzU3ODIwMTViNWNiMTAxYzY3MTI2MzU1Y2QiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI3OGFmZWJkYzQ2Njc5MzZiOTQ2NDFhMWQ1YWFmMzQ5Y2QwNjUzOGVjY2IzODBmYmJhZGRiOGU4NGVlODJlMWUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI1NmZjODJmYjZlYmU4YjJhNzY1ZjU0OTZkMjUzMTIzNDIwMTM2MDBlZjE0MTRiNWMwZDQ2OGYyZDFkYmE1MjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ3NzhjMzRiYzhmODZhYmNhMDMzMTllMmM2YjcwZGZkN2FiYWFjYzYxZDRiYzg0NTJiZTMxMjMxZDg0ZTBhMDMifQ" + }, + { + "resource": "repo://TFM/App/RootView.swift#L18-L25", + "version": "repo-lines-v1:sha256:56bfba2d721bb0dadcddc26b2e9cc5a5a05ba95f329adee18297444f69e8ade7:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYTljMmRkNjhhNWFlYmY3NDZiYzU4YmQwYzNkZmEyZjg5MWVkZmVjY2NhNGY0NTliODBjNjI3MGU0ZDBkNTJhZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDAzNGNkNzYzNDdjN2E5ZjlkOGZjYjk2ZWM2YWUwMDkwNzg5YTc2MjhlZWI5OTJkMzcyYzNhNmM0NTM4ZjUzMiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNzQ4YjgwZGQxN2Q4OTUyYWZkMzQxNmFiZmQxOTg3ZTIwMGQzNGZhZjU5MzBmZjc2OTgyOWNiNjQ4N2I2YjkzNiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOGI5ZGJjMWYwNDlhMjA0OGVhYTY2ZTc5NWM0OTM4OTNiODZkM2NkMjk1MTMzYTk4NDAzNGY2MzkzMjY4MzA2NSJ9" + }, + { + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L24-L41", + "version": "repo-lines-v1:sha256:db7c791c93b252a337c82e0d880cbf043021afd049e16abe78a5f8afc58010d1:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRkYzhmOGM4NDkyMzkxMzgyNTIzMDA3MTEwYWJiMGFhNjYxOWNhMjAyZjkyOWJhNzMzZTA3Y2M4MGNhMGQ5YzAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU0ODY5NTFkNTQ4OTVkN2U2ZGRlZTBkMGQ5NjExMWRkMTRkOTcxZTNmY2U4ODI2MmFjNTM2NWM4N2IxNzEzYjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA0Yjk5NTFjODA4NmQ2YmI0YjUxNWM4MmJhZWZlOGI4NmE3MGYyZWJjNjQ4OWIyZTA5ODI3ZjgwYjU2MzQ3YTEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIzNDg4MGIyMzFmYTc5MGUyZGU5YjgzNGY4YWI1YmM3ZmMxY2JkNzI5M2UwMDc0OWNmNjQwZjNiNzJiZDgyMDgifQ" + }, + { + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L337-L345", + "version": "repo-lines-v1:sha256:a61664b3f30079446ef806efc825ef5e98de095e9d37c62e262effec99b62988:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZWQxZDNkNjMyYzkxOGRmYWJjMmRmZWJlNDQyYzEzYjcwMDZhODE3OWZmODkwNjI0OTlmMGZlMzM3NGNiMWRmMSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYzFiMWEwNGJkMzRlMjVmZTg0M2FmOThmNzVmODk2NTQwNDRlZTE3YTE4MGMzZWViZGQ4YWE1MjYzNTE0YTc0YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMzE1MWMwMjM1ODY5OTQwMWFkODVlOTFmNmM0ZTVhZDNhZTdmZTk0YTdhY2M5NzRhODkxNDgzNmU3MjNkODc0NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNjY3NDQ3ZjFjZjNlOWQ4ZGNkODUyMzM4YmRhMDUwNzFlMmY2M2Y4MWQ1YTQ2MzZmMmU2MDUzZTk3ODljZjhjYSJ9" } ] }, @@ -49,7 +65,7 @@ "statement": "The persistent workspace deck retains all open workspace views, gates hidden interaction/accessibility, parks hidden layouts at identity-keyed cached usable sizes, and prunes closed IDs rather than relaying every container resize to hidden split trees.", "evidence": [ { - "resource": "repo://TFM/App/RootView.swift#L391-L409", + "resource": "repo://TFM/App/RootView.swift#L382-L400", "version": "repo-lines-v1:sha256:b633b5e83bd4ab48cc86823138859bc2895d3eab20f6d75a4a3812c3a5c2333f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYwMjA0MzJkYzQ3NGFmN2VmY2NlYmMwZTQ1ZDAzYzIxYmM3OTY0ZWMzZWY5NDE2Zjg5NmRlMDVlNmM0ODAzMzQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM1Y2E5MDNhMWE1YmZlMTQ0OTM3NWE4OWFmMWZlZTZkODgyN2JjMDRmYWVkYjczMWNlYmUyMzA0MmU5NWJhMGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjExZGM3ZDc4ZWRlZDE0NzliZDk3YTZjN2UzYWQ0OGQxNzFhNjRhZTNlMTQ1MGNhNjBlZGRlYzY0YTViOTFlZTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijk3YjZlZTM3OGExYTEyM2Q2ZGEzMDJkYTM5MTk5MjEwYmE0MWMyNmJlZjNmYWFiOGRlZjY2YWVmM2I2MzVkOTgifQ" }, { @@ -148,7 +164,7 @@ "evidence": [ { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/Features/Sidebar/WindowSidebarView.swift", @@ -166,7 +182,7 @@ "evidence": [ { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/Features/Workspaces/Pane.swift", @@ -202,7 +218,7 @@ "evidence": [ { "resource": "repo://TFM/App/RootView.swift", - "version": "repo-file-v1:sha256:d0c901e4e4a0bf405d36a1fe66ffd8777529c97165e0eb42013812cd3371ed64" + "version": "repo-file-v1:sha256:f8fd87e3753f006035489ddd4feb30ed89c01c72f0a47ea5dfccf1da3c9a00b4" }, { "resource": "repo://TFM/Design/DesignTokens.swift", @@ -251,7 +267,7 @@ "statement": "Closing a registered main window cancels its pending scene requests and unregisters its model; closeForWindow records recoverable descriptors through an app-owned callback, disconnects live workspaces and clears window-local drag, presentation and transcript state. Restored windows without pending requests keep their system-restored frames.", "evidence": [ { - "resource": "repo://TFM/App/AppModel.swift#L717-L730", + "resource": "repo://TFM/App/AppModel.swift#L725-L738", "version": "repo-lines-v1:sha256:030bcb979c5ac0bc3030da17f298dbbfbed66e765de9a2a4416b27ce4764a989:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyZGM1ZjhiMjc2YWM4MjBlNzdjMWZkYWIwYzU1NGFkMGE5Yjk5YzNiMGIzYmI2N2U3NjM4NmIwNDM0NjQzNTQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVmNDBmN2JhZmM3ODlhMzZkYjEzYzc3ZmJhY2ViYTZmYTcxOTE1YTc2YTkyYTAyNDBkZTBjYzY4Y2JiN2Y1NDUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMyYzU5NzJkMDQ5NDFjYjgyODk0NTM4MzExMWFjZGVmYmY0YTdlZGU5OWU4N2RjNDdhNzk3ZWJhODcxZGU4YTEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRjZjgxOTQwODBkMTUyYWZkZWRmMTYzMDBlODcyMDMwOTc1YzIxYjNmZWZhODAwMGE2YmFmMWFjMDkwYTBjODEifQ" }, { @@ -428,7 +444,7 @@ "evidence": [ { "resource": "repo://TFM/App/RootView.swift", - "version": "repo-file-v1:sha256:d0c901e4e4a0bf405d36a1fe66ffd8777529c97165e0eb42013812cd3371ed64" + "version": "repo-file-v1:sha256:f8fd87e3753f006035489ddd4feb30ed89c01c72f0a47ea5dfccf1da3c9a00b4" }, { "resource": "repo://TFM/UI/Components/WindowToolbar.swift", @@ -518,11 +534,11 @@ "evidence": [ { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" }, { "resource": "repo://TFM/Features/Workspaces/WindowModel.swift", @@ -557,7 +573,7 @@ "version": "repo-lines-v1:sha256:51217b2f0be3a1ce57752ed0414450df847c01ba56c202a58a235935ceebbd12:eyJzZWxlY3RlZExpbmVDb3VudCI6NDAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFmMDhhOGI4NmMyMTkzOTMzMWNlMmRlNzQ4Y2MwMjQyODI5NmExNWUyOWUwMTJmOWI1YjI5YmE3NTQ5MDViOGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiNjNmMmQxNjhlZjBmMzNlZDk5MGM0ZWM3NWI5ZDg4MDE2MDJmNWRiNzNjMjg5NzRmOGRjOGNmNWE2MDQyNDkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU5OGYxYTA0YmVlNzFjM2NmYzJiNDcxOWU2ZmQ5MGM5NmY0N2E5MWU0ZDJmZGU3ODgzNTNjMDM3MDQ2NmY5ZGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY0YzQ4OTFlYWUxNGZmNmIyNjEwNDY0YmYxNzY0MmYzZGZkZDUwMmEzYTQzYTJkNDY3NDZmOGI3MWVjZTU1MjgifQ" }, { - "resource": "repo://TFM/App/RootView.swift#L388-L454", + "resource": "repo://TFM/App/RootView.swift#L379-L445", "version": "repo-lines-v1:sha256:f9142bb42053328d2586346af9d41e9ae2b697cdfe844bbbeed8c8680cb790e3:eyJzZWxlY3RlZExpbmVDb3VudCI6NjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJiYWZiMjUxZTRmNzMzODE1NTFhYmE3NDc3ZTY0ZWJkZTJkNmI2MWY2MTMxN2RlNDk2YmJhZWNhODBiMDM3MGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk4N2ZmNWE5NGZlMDQzODEyY2ZiYjUzNDgxMDFkM2U4N2ViOWQ4Y2M2MzA1ZTAyNWQ5MjI4MzFiNDYzMzFlNTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI1NmIzNWUzMGQ5NjIxOThlOTczZTcwODE3MDM5ZWRmMWZiNDY0Y2YxNTZkNGM3ZDhiZTMyODc5NzUwOWU0MGMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM3ZTdlOGE2Njc2OWFkMDFkYWQ2MjEzZTliMDFkZTg1MmEyOTMzZGE5ODdiNzc5NWY1ZmVjMWUyZmM4YWZmNWIifQ" } ] @@ -571,7 +587,7 @@ "version": "repo-lines-v1:sha256:01317cf9a01bf9aa52a42563214174945e07cce98eab8512010ae999a709216e:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNTkwOWE1M2E2MThkMDI4ZTFkMzAwMGMwYTdhYmNlZWNiZTVmYjM3ZjRlNTExZWZlNDY2NDNmYWQ2ZTY2ODcyZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNjdlZWUyYjY5MjE1ZjkxMjI4ZWJmOGNhNzgzMDBhMzI0ZTc1NGMyYzVjMjMxNmMyMzdmOWE4YjBmYjAwNjlhNiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNGIzM2RiOTVhYjMzYzU1OGYzMzNiZGVkY2U1ZDU2NzgxZDU4OTg1YjUyZjE4MDYzYzZiNDM3OWE2YTMwY2ViZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiODVkMjdjNzM3MGI1OWIyMmM4MjQ3NTI1YmU0NWViYzYyZDhiYTFhYTczNjBlNGYzYTYzYjY1YTMzNzUyYmJmNiJ9" }, { - "resource": "repo://TFM/App/RootView.swift#L468-L522", + "resource": "repo://TFM/App/RootView.swift#L459-L513", "version": "repo-lines-v1:sha256:5611911378d074f4c7cbe3f2b7e6050490a5605a255832f95473c5417d9e9fbf:eyJzZWxlY3RlZExpbmVDb3VudCI6NTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI2YWI4YmE1ODJjM2UyZGVkZmMxZjRkN2M0ZDk2ZjAyYmMxMDE5ZDYwZjJiN2UyMzIxNDRkZjhmZDA2ZTdjMDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjllYzdlYjk1MjJhNWE2ODFjODE4ZDc1NGZhZDFjMmI5OWIzMWVlZTZjYzU0ZjlkYzZjMjU2ZDgxZjBhNDQzOWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU2MGUxMTU4YWYxMTk4ZmIyMmE1ZGNmYzk0MTczZTk3YTkxYTg1NWVhMWJhYmE5MGFjYzM5MDU0ZGVlZTlmNzAifQ" }, { @@ -654,7 +670,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/Features/Sidebar/WindowSidebarView.swift", @@ -667,7 +683,7 @@ "statement": "When the selected combinable workspace is being dragged, WindowModel derives displayedWorkspace as an eligible adjacent merge target without changing selectedWorkspace; RootView activates that deck child and Navigator highlights it, while clearing the drag restores the selected view and no eligible neighbour leaves it displayed.", "evidence": [ { - "resource": "repo://TFM/App/RootView.swift#L392-L406", + "resource": "repo://TFM/App/RootView.swift#L383-L397", "version": "repo-lines-v1:sha256:388f36d6cc7bb6c0266d1bb0a3673d2837a5d1f6bf5f33f4df87310c37ce0e19:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZkM2M2NTA2OGYyNzNjNjFkYjM5NWRjMzQzZmYzNjZkYTU3MWZhYjI3MDg2YjBjZWYwZWM0NmUwNmFjMjA0YWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJiODlkNTljMjE3MGEwZWFmOWY0NDc2YmVjMjRjOThlYjdlYTRiMWNlYTY4ZDQ5MmQ2OGNkNzAzOGZkNDE0YmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY1M2UxY2Y2MjVlZTU5YzU0NGNlNTFhYTUwMTcxNDk1MDllY2VjNDgzNmY4MmEzYjEwMTljNGEzNTg3NDNhNTAifQ" }, { @@ -690,7 +706,7 @@ "evidence": [ { "resource": "repo://TFM/Platform/MainWindowCoordinator.swift", - "version": "repo-file-v1:sha256:44e9fa73eb630219c6c875e671030342eba949bb814550dce3279680964cc47b" + "version": "repo-file-v1:sha256:0166b9eab07357665f202205a39a6de25af79bfdda707632e8324cb33f129314" }, { "resource": "repo://TFMTests/MultiWindowTests.swift", @@ -736,7 +752,7 @@ "evidence": [ { "resource": "repo://TFM/Platform/MainWindowCoordinator.swift", - "version": "repo-file-v1:sha256:44e9fa73eb630219c6c875e671030342eba949bb814550dce3279680964cc47b" + "version": "repo-file-v1:sha256:0166b9eab07357665f202205a39a6de25af79bfdda707632e8324cb33f129314" } ] }, @@ -871,7 +887,7 @@ "version": "repo-lines-v1:sha256:dbf67f7def33928b4ed071f71d650cf5f27f66c21bf9113071508b0fef3af688:eyJzZWxlY3RlZExpbmVDb3VudCI6NTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5MDQ0M2NjMzYwNWJkYjZlOTY0NGM3NWM2Y2U0Yzk5Nzc1NTU5ZDU2ZDk2YjY2OWVlMjQ5NjlmMjFmOTU4NDkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRiYmI4OTUwMzEzMmUxYmJmMGQ5ZDRmNDNjMWUxODRhOWU1ZWVjZDllNTk5NWI3Njg3N2RhYWM0Y2QzMjg1NDMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRjNTU1MWVmMGYwNjg2YWU5YmQ5ZGVhNDM5YjljMzFiNzQ0OWJmZTA3OTcwNmEzMTZhNTU0NmE5ZWFlYWVkMWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc0NzBkOGY3MmI0YjAwNGMzZmE5NDU4MGMzOTY0ZmJmZmViMTkxMmRlZTJlNWEzYWFkZTVkYWJlMWI3Mjc2NjgifQ" }, { - "resource": "repo://TFM/App/RootView.swift#L286-L338", + "resource": "repo://TFM/App/RootView.swift#L277-L329", "version": "repo-lines-v1:sha256:bfdefff633cfcbba6c6fedcaf090d9f8039d4bf7396a32ea81cb884d2b03ca4d:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkxNWJiOGMxMzI4ZmNiYWM1ZDc5ZmU0Y2Q1NTdiOGM0ODJjYzhkYTczOTFiYzMyYjg2MDc5MWJhOGQxZDBhZmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE4N2RlYjYwNDRmOTM5MzMwZWViNjIxZGE5YTg4N2Y5NzUwOTJmODg3Y2M0OWI4NzgzNzcyYjI3NTU2NGFhMDQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImZmMGQ3NTk1MGRmMGZmOWEzZWU5NjYzOWRjYzhmZjVlYWVkYjQwZDY4MmJlYTkyNjIyYjUyMGRkODg3ZDkzZTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAyMjc5NjRkNmQ4OGIwNWViMGZiZDZjMzY3MTYzYTdmNzY5MjljNmVlN2E5Y2UxZDRkZjRmN2Y0MWIzNGUxNGUifQ" }, { @@ -914,7 +930,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/architecture/engineering-invariants.json b/openwiki/.claims/architecture/engineering-invariants.json index c6e7b8de..144312c6 100644 --- a/openwiki/.claims/architecture/engineering-invariants.json +++ b/openwiki/.claims/architecture/engineering-invariants.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:f781a4372018996ce5220a6c2cd1060320eae0b95a9bfe975bcbcc8811ad1609", + "pageVersion": "sha256:7c497b5410b0b636d3960e0d37b238f16eb679a2e3513576c8764a3211da4707", "claims": [ { "id": "claim_72f9329a158e418cbf0fd6394f82815e", @@ -38,7 +38,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" }, { "resource": "repo://TFMTests/ArchitectureBoundaryTests.swift", @@ -60,7 +60,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/Features/Workspaces/WindowModel.swift", @@ -336,7 +336,7 @@ }, { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" } ] }, @@ -349,12 +349,12 @@ "version": "repo-lines-v1:sha256:45383d212fb6991f01b24f3ea84538b0f619b50ed950673b4c79037e4de1652d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU3ODhmN2QzZWVhNGY0NTY0NzdmM2QwMjliNTAzZDI5YmU4YjExOTNiNjJjZTZlOWMzZTk5ZDQ1NDczMjViNDYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4MzBkMzQxYjc5NmIyZjQyNTk4YTk3M2UwMTJkOGIyZTZjNTE2MDkwYzliMDQ0YWMwZDhkYTIyZjU2MzI4MzciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L1239-L1367", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L1183-L1311", "version": "repo-lines-v1:sha256:1b95aa25bd123077e3dd13d754c23e5779523f27f0bbbd0269014621dfd41091:eyJzZWxlY3RlZExpbmVDb3VudCI6MTI5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIzNmMxMTM5ZTBjOGMxM2Q5MDU2ZTIxYTE4NTEyZjA1OWY0MmI5MzNkNDI3OWU4MWE1NzUyM2NkNDFlYzEyZTk5IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1OTA5YTUzYTYxOGQwMjhlMWQzMDAwYzBhN2FiY2VlY2JlNWZiMzdmNGU1MTFlZmU0NjY0M2ZhZDZlNjY4NzJmIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIxY2FmYzE2N2VlOTliNmY1YzgxZTA2Yzg5MzYzYmNhMGJkM2VlODVmZGMwZDVmZDNkMDZiNGMwYTIwNTkxZTgwIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmYjA0NTczZTY0ZjNmYjg2YTQ1OTZjYmRmYzZmOTNkYTIxYzE1OGJiNjNiYTgyMjg1OWNkMzNmZTNhNTM0OWRlIn0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L404-L545", - "version": "repo-lines-v1:sha256:680aa38bb50d0592cdb4f50146d1eaf40a23646f30e5411b6fe9ec29d916cda7:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQyLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI1NTIyYzQyY2QxOWM2ZDAzZDgwOGM0NTI3MjA1NTI4MjViN2VhMWM1YjQ5MWIyMTg1MTFjYWM3NDE1Y2Y5ZDliIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJhMGU2YmJkMjI2NGYyMzJkYTlhOGNlYzkxZWQzMDk1ZmEwODgzYjU3MjU5YzUzODY1MjM4NjVmYjM2MWYwMDY5IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhMDk3ZjZkNTBkOTY0NWU2MmNhODlkOWQ1MTJhM2U4N2ZiYThkNzkzZTRkMjk2NzlhNWZmMmRiNDMzYWU2ZjlhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIxOTYxN2Y1M2MzNjY1NjllOWI3ZGRhMDk5M2ExOWJjNzBhZTQ2ZjIxMzA3OGY1YWUzNDIyYzFiNTdiYzc0YTMwIn0" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L404-L524", + "version": "repo-lines-v1:sha256:e13f05a7a88b81edfaa2ce0c3e22465b210c214019e60765745602b87d4e3aa8:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIxLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI1NTIyYzQyY2QxOWM2ZDAzZDgwOGM0NTI3MjA1NTI4MjViN2VhMWM1YjQ5MWIyMTg1MTFjYWM3NDE1Y2Y5ZDliIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJhMGU2YmJkMjI2NGYyMzJkYTlhOGNlYzkxZWQzMDk1ZmEwODgzYjU3MjU5YzUzODY1MjM4NjVmYjM2MWYwMDY5IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhMDk3ZjZkNTBkOTY0NWU2MmNhODlkOWQ1MTJhM2U4N2ZiYThkNzkzZTRkMjk2NzlhNWZmMmRiNDMzYWU2ZjlhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIxOTYxN2Y1M2MzNjY1NjllOWI3ZGRhMDk5M2ExOWJjNzBhZTQ2ZjIxMzA3OGY1YWUzNDIyYzFiNTdiYzc0YTMwIn0" } ] }, @@ -372,11 +372,11 @@ }, { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" }, { "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift", - "version": "repo-file-v1:sha256:a2020171e8e508b6aea665dd867d1fd1bea1c8e8079afbb1543369ade17f2c37" + "version": "repo-file-v1:sha256:370b9d4b8a8a3b9ddbb29e774d178ff9ba6347e2f9cd570326fc7e65b3d53e63" } ] }, @@ -416,7 +416,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, @@ -534,19 +534,31 @@ }, { "id": "claim_2d1af0011b7d4417a6745a61faa74bcf", - "statement": "At desktop termination the app rechecks unuploaded remote edits and lets the user cancel; accepted termination runs an idempotent shutdown that fences new Assistant admissions, cancels captured preparations and stops stored conversations before joining their drains, runs MCP/SFTP stops concurrently, and closes Agent background commands and connections after drains while retiring other app-wide services.", + "statement": "Desktop termination first offers an application-scoped confirmation if registered nonclosed windows own any Workspaces, even hidden ones, then rechecks unuploaded remote edits and allows cancellation before cleanup. This is distinct from pane close and per-window native close vetoes. Accepted termination runs an idempotent shutdown that fences Assistant admission, cancels captured preparations and stops stored conversations before joining their drains, runs MCP/SFTP stops concurrently, and closes Agent background commands and connections after drains while retiring app-wide services.", "evidence": [ { - "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "resource": "repo://TFM/App/AppModel.swift#L48-L52", + "version": "repo-lines-v1:sha256:0040c11192f484a9fb37242cf51df33fb5073489845788cd25375cc750bd1444:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYTYyOWE4OTEyZGFmZjY3MGNkN2ViOTFjZmM0Yzk1ZmM1NTVkYTU5OTQ3YjQzZDM1OGIxNzUyMzEyMWE5MjQ4NyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZiMDQ0OTE0Njk3MjJlODM2M2E5NGU0MDQ0NTM4ZTAxM2EyYTIxN2UzNmIxZTU2OGJhNjBhY2I3YjRjOGYyZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOWJiOWRiNWFmZWE2Njc5MmIwMjlkNmE2MGI1ZjM0YTFkMzQ4ODhiNjU2MjkyNTA1ZjJhZmJhM2M0ZWE1ZDI1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZjA4ZTZjYWE2YWU1NDdjN2MwYjNkYjIyY2Q0NWNlYzY5ZjBiZDEwNGE3ZmZlNDA3ZDMxODgzYWY0NzIxZTM1MyJ9" }, { - "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "resource": "repo://TFM/App/AppModel.swift#L962-L1022", + "version": "repo-lines-v1:sha256:4f8f0f61f9b03bf5b6cfc96cddd4ae1820792080fa4b443c97d2cf2d6c7a78f2:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiNmE1OWU3Nzg2NmU1MGRiZDYxZjA5ZmQ1YWM2MjQwN2Q0ZGVlNjdkNGIyNTJhNDExYzRhZTEwZWNmMWQwNmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNkYmExMmRjNjgzMWEzMWU5NzdjMTMzMDU0NWZiMzZhMTFjYjE2MDQ5YjQ5ZGYwN2MzYjAyNjM4YjIwNzA2YzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUzODNlN2M2MmI5ZmI0ZGU3YjNjNTc3ZmFkNjNiOTYxMGRlZTcyNmI4NzljYzZjN2M5ZjYzNTljOTU5YWEwODQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkYjkxZWUwZThhZmIwMWVmNzYyMmM2ZTg0NzA2MDM2YmVmMzAwNTc5YWU1ZjcyYmQzOTViMmY3MjllNmRjOWUifQ" }, { - "resource": "repo://TFMTests/AppTerminationTests.swift", - "version": "repo-file-v1:sha256:61e14261971e275798d4cde5275e5c3e0dc0d5552beace53b81e1ed3842358eb" + "resource": "repo://TFM/App/TermindApp.swift#L467-L510", + "version": "repo-lines-v1:sha256:25d575a1909deae410a6954ec410050cf33584e768e20922597bab7bda1a69c8:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg2MmZlMmQ0YmFjM2JhZjAxN2NiYmY5MmFiMTcwZTQ0NWExMWQxNzYyODBkZDEyOTdjZmM2ZDQxZjI2Y2NlMGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlMjAyMDc0MmMzM2IxYjkxZWIyMjYxNmJmYTg4NWMxNjQ2NThlM2Y5YzAzZDNkODZhYzllMjgwMGRkYmNhMzcifQ" + }, + { + "resource": "repo://TFM/Platform/WorkspaceCloseConfirmation.swift#L4-L76", + "version": "repo-lines-v1:sha256:9bcaf9b0b51ef76b69d76b071a8da6b4d5584958292ba982fcf49ed117e43ec1:eyJzZWxlY3RlZExpbmVDb3VudCI6NzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjYTg2ZTlmMmEwMzRmNDRkZjkxZDMyMmEwNDBjOWZjYTA0ZWI4ZmJiMTc1YWQyZmJmZDVhODNhMDA3NmEzODIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQyNTJmOGY1OTA5ZGFiZTFkMDJhYzM4MjVjOTAxMzZkMjk2ZTNiYjZkMTlkMWU1YTdlNmY5MWYxZmJmOTIwZmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY3Yzc5MTU2YTY1ZWJjN2M0NGE1OTNkZTgzZDk5Nzg5N2I1YmY3NWQ3OWQ3OWY4MWM2YjJiNzYzYjZjMTFhNzYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoyLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM3OTRkMDJhODEyMTk0NGQxNzZkMjY1ZTliYjljYzU5ZjZhYjg5MGJjYWRmOGFlZmRlYmRiYmI0ZjJlYTg0NDQifQ" + }, + { + "resource": "repo://TFMTests/AppTerminationTests.swift#L12-L160", + "version": "repo-lines-v1:sha256:f41d0645028a61e547c352effd14fc70cf156752a8cc9775e507a38110790443:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQ5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI0Y2YwOWViMGM1NzQ0ZmI5NzMzMWI5OGZmM2ZiZjA1YTYwMmJhM2Y3ZjY3ZTVkYTViZTkwOTUwMzNmNjI1ODI1IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ZDJkYjMzNzM2OTA5ZDMxM2M0MTE0NGE0Mzc2Y2U0MDk0YjJhZmU0OGI2MmI3NTY0NDEwMDNhNjUzZjgzNzNiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5NjYyM2QxMTVkMDFmNmYzYjViYWQxOTU4MDgzZGJmNGNlNDllYTQyMWU2Njg2ZjU4ZTg0OWZlOTU4Yjc4ZmJlIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlOGVhZjNlMWRiMjg0ZjZhNmZkNGUwNjZhZTJmYjY1NGFlNjhmZGZjZjNlN2U0ZGNhNGMyZGQyMTRhNDliOWQwIn0" + }, + { + "resource": "repo://TFMTests/WorkspaceCloseConfirmationTests.swift", + "version": "repo-file-v1:sha256:ecaef1ced74bfac4055edc6c3496204a2f5e864932da272ab3c51363bfa6081c" } ] }, @@ -559,7 +571,7 @@ "version": "repo-lines-v1:sha256:b6a884010a3a8be3a6f465af0a85d69ba9541866b9e1ff17aab730481c270ef4:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZkNjgzYjc5NGVkOWI1NjQ2Y2Q2ZTZjMGEyYmMzYTk4YTFjN2NiYmMzMDUxZWZiN2E0NWQzYWI5MWI0MDRhZmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMxYmJkYmE3MTE3NjI3OTU5NzBjZWQxMDc4MzhjY2EyMGY2OGJkYmM0OWFiODdiNTE5YzcwZmEwOTdiZGU0OWIifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift#L493-L499", + "resource": "repo://TFM/App/TermindApp.swift#L498-L504", "version": "repo-lines-v1:sha256:da0dbd9ed3dbdbb6fb68c253c08ff49d722493631a12e7346149a8d0cf5efad9:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZTM3MDQyNjgyNDFmY2E3ZTJhMGM3YWYwZWIxYmViMTAwNGJkYmYyZmM4ZDE0YzM1M2YzZDlkY2U3YjdjMDhjMSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZiMDQ0OTE0Njk3MjJlODM2M2E5NGU0MDQ0NTM4ZTAxM2EyYTIxN2UzNmIxZTU2OGJhNjBhY2I3YjRjOGYyZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZjY0ODRlOWE3YWJkMGFiMjA3NTgyNzIzYzk5MjM5MzEyYjI3NDY2YTczNzg1ZDFkOGE3YzlkOThmOWUyZTc0OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOWI4YmVkNWZiMDViNTUzOTYwYWIwNWVhNThmMjZkMDkwZDYwNjU1MTk4OTNkY2Y5OWVmZDllNzU0ZDdjZWE4YyJ9" }, { @@ -578,7 +590,7 @@ }, { "resource": "repo://Frameworks/Resources/Localizable.xcstrings", - "version": "repo-file-v1:sha256:7a75d3a7750b4cb0cbe469c5b9339562aad54f8aeaecf676c6862e9ed51388b5" + "version": "repo-file-v1:sha256:568a304613f20338e661657b0616fcd3d76defeadc112844615eed4e2accfa96" }, { "resource": "repo://Frameworks/TermindPresentation/Strings/AppStrings.swift#L1-L6", @@ -632,7 +644,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" }, { "resource": "repo://TFMTests/VisibleWindowTestTrait.swift", @@ -766,7 +778,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" } ] }, @@ -850,7 +862,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, @@ -910,15 +922,23 @@ }, { "id": "claim_dea83da779614245a050202d39c28021", - "statement": "History edit persistence refuses to remove a suffix containing tool messages or invocation cards and rejects a selected user turn with agent-message provenance, so editing cannot silently erase executed tool work or collaboration-origin messages.", + "statement": "History editing permits removal of executed tool history: truncation atomically deletes suffix-owned invocation, result and review rows while preserving prefix-owned work; legacy invocations without recoverable source or call ownership belong to the suffix at or after the edit boundary. Selected user messages with agent-message provenance remain ineligible for editing; transcript truncation does not undo remote tool effects.", "evidence": [ { - "resource": "repo://Frameworks/TermindApplication/Agent/AssistantConversationOperations.swift", - "version": "repo-file-v1:sha256:d55c34f3ed990638f514b681cc1b47ca858db56da4794f1250986a0e2ef63cfe" + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L1132-L1173", + "version": "repo-lines-v1:sha256:a4bcfff92d242673a486f5cbbd5db0e8e601d38f847b643363023840920b2801:eyJzZWxlY3RlZExpbmVDb3VudCI6NDIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI1MmFjMDJlY2QwNWE5M2M1NGI3NDdjNDIxM2IxZDdmMjY5OGM4ZTRlMjBiYjNhMGI2ZGZiYjZjMTc4MDE0OGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU3YzRmZjVmY2EyMGUzODk0Zjc3ZmY2NDk0Y2FmNmVkZDU2NzFiZjE3N2YyMmVmMDE3NDBlNzNhMTYwMTc3OWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBmMDMzM2U2ZTFlZjc5ZTY1MDliN2ZkNTA5MmZiMDFmMzY5NDQxODk0YzNmZGVjMTliOTU3NTY4NmJjNzc5ZDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU1YjAwYjk5MjY2NDI4ZmU1NTI1NDYwZDE0YzNhMjZlNDI5Yzc3Yjk1ODUyZmEzNzEyYzEzYmNiMTMwM2I3MTAifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift", - "version": "repo-file-v1:sha256:a2020171e8e508b6aea665dd867d1fd1bea1c8e8079afbb1543369ade17f2c37" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L410-L519", + "version": "repo-lines-v1:sha256:8865d5309b54e8cfe1b6aa65ca065a420de72266ddd05db0b9dfef303e2c2364:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJhMTFlN2IxNGQyMWMzOGM3ZmM1ZTJkZTg3YTIwNDVhMDAyOGM5MjQ2MTUyMjc1ODhjZjcwOTVhOTNlZGQ2OWNhIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5MDg2NTI3OTliNTFlNzllMWE4Nzk2ZWQzMzQzOWQ1ZjUxMDU2M2IwOTg2NWQ4MjhiY2ExYmQzMGE2NzE4ZmJhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI1ZmI4NGY5MTE3YTNlMmZjM2FjZDgwMjk5YmU1OGZjYTlhNjNjMDEzMTNmNzM3ZjRjNGQ0MWE1MzA2MjQyZDUwIn0" + }, + { + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L629-L687", + "version": "repo-lines-v1:sha256:5014db9ccb6a0cbf40e8f138d14954028a7d6d199831d2ae6a4c0b9faa328b4d:eyJzZWxlY3RlZExpbmVDb3VudCI6NTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNlNzYyM2U4YWEyYzQ1ZTRjN2YzMWM4NjFlY2UwZTljOGI2NTIzM2FjMzUyYWI2MDY0OWUyM2U1MTI2MDQwYWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBlMTk0NzllZWYyMjk1MzI3ZjQ1ODc4OGY4OTUwYjg1MGE0NTM2ZjE1NWE1MmUxYmZmYjk3MTIxZWU0MTUzNWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE3NDJmODZmOTJlMzg5MTljZjQzOGQ5MjdlYTUwZDMyZDkwMjljZjgxOGJmYTFkNWYzYWZiYTk1NGQxMmNlNzMifQ" + }, + { + "resource": "repo://TFMTests/AgentPersistenceTests.swift#L834-L887", + "version": "repo-lines-v1:sha256:1b9e32696d543044018585e7028658d71f028d3ebbdbbe35868f5ea3731b89ee:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY4ZmRiMWE0ZmJiNDNlODMyMWI3MmU0Y2Y1ZmI2MDViMzJjMGRhMDlmMDVjZjQ2NjRkOTk1ZWFhYWZmZjk3YjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImZlYTA1ZTJjZGFhMGVhMGNjMzUwNWM0YmI5NWI3NTU2NDk5OTk4N2UwNTc4MjZkMjc4MTA2NzMyMGQzN2U0NjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjIwNGY5NTNlYjgwMzA5MzBiODgzMGFiYjczZTE3Zjk4NzVjMzY3MGRkMzJmYmUwYTMwNDllNzUzODg5YjlkNmIifQ" } ] }, @@ -1010,7 +1030,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" }, { "resource": "repo://TFMTests/DatabaseStartupGuardTests.swift", @@ -1154,7 +1174,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/architecture/module-map.json b/openwiki/.claims/architecture/module-map.json index 6adf033e..605fdb82 100644 --- a/openwiki/.claims/architecture/module-map.json +++ b/openwiki/.claims/architecture/module-map.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:895556453e1d1159e77f9b5e3877d6bc4b8a7ea715f35b72d332478e66cc1dc9", + "pageVersion": "sha256:79ee4201d94b6450f1485e22a3f95940db40a12347c05e4f1cd13a9db25b70b5", "claims": [ { "id": "claim_34230e88036241baad093fb8de5bac8e", @@ -122,37 +122,33 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, { "id": "claim_5015b59262b040c49bab3e5fe71d8a7f", - "statement": "The reusable device directory follows the normal shared-module boundary: TermindDomain owns DeviceRecord, TermindApplication owns the repository port and observable model, Persistence owns the SwiftData implementation, and each app supplies platform-specific display-name acquisition.", + "statement": "The reusable device directory follows shared-module ownership: Domain owns DeviceRecord, Application owns the repository port and observable model, and Persistence owns the SwiftData implementation. Native composition supplies the installation ID from VaultSyncJournal and platform-specific display-name acquisition: desktop MacDeviceName prefers trimmed nonempty kern.hostname with Device.current.safeDescription fallback, while mobile supplies Device.current.safeDescription directly; MacDeviceNameTests covers priority and nil, empty, and whitespace fallback.", "evidence": [ { - "resource": "repo://Frameworks/TermindApplication/FeatureModels/DeviceDirectoryModel.swift", - "version": "repo-file-v1:sha256:32ea6647615bc902aecd75944fed455ab6fd747dc89162a9ed219079aa70bc2d" + "resource": "repo://TFI/App/MobileAppModel.swift#L235-L246", + "version": "repo-lines-v1:sha256:ba16dd982874ef1a6a4a6db41454133db4741a73f68951521a340ab54ca2ccb8:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlhNWM1ZWQxNTlkYWMxNWJkZmQzNzM1YWZhYmJhNTEzOWQwN2EzOGFkNzBhYmZjNmM5NTg1YjcwOTVmYzFlOTAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJlODRkMTQ0ZGU1YjZlY2NlODY0MWZmMDQxODBmOWEwZWQ3MjkwYTRiOTU3ZWZhNDVlM2U1YzkzNGI5ZWU3YWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUwMTNjNjUxZTFjODc3NGU1NWNlOWVmZmFjNmFkN2U5MTk5YmY3ZDAwYjgwZDMyOTE3NWNmMTc4M2U3MWMxNDEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE1MDNhZTlmZWY1YTRiZjM5ZDIzZmQ0MDRmYWFiYTkyMDY2MmY0ZmEzODUzMjkxOGVlMGI2MmE0OWNhY2EwZjIifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Repositories/DeviceDirectoryRepository.swift", - "version": "repo-file-v1:sha256:68f529231c8aa605e9668027e609139eee4b28e51794f0a8efbb64b49e291716" + "resource": "repo://TFM/App/TermindApp.swift#L121-L133", + "version": "repo-lines-v1:sha256:e3f3a93a8d8c8b7f254a37fd15f305aec5de03b14a33f32368e668fcd96d56ad:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY2MjY3Mzc5NDMyMmNiZGZkZGFiNGVmYjgwNWQ0NjJiNDllNWRhNWUwNjJmM2ZjMGEyMjkzZWZhYmM2NzlkN2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE5OTNiZmU2YTljMjM5OTQ4MmVmNjljNzBlNDllYTUxZTEyZTE3NGI1M2RhZmRiOGE3ZGI2MmVmZDk2YjdiNmEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ4ZDkxMmJhMDYxY2JhNDY2OThlZGNhYTlkMjA0ZTc4OTBhOTY3MGE2ZGNlNzM2NDUxMDJmZjI4YWQxOTAzM2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBmMTdkY2RkMmJlZGZhYzUxMGRhYzY4ODEyNDY2YTAwYzg2MDdmNzIxYjk5OTBjMzdjZmI0MzEzZGE3NTI0MjkifQ" }, { - "resource": "repo://Frameworks/TermindDomain/DeviceRecord.swift", - "version": "repo-file-v1:sha256:7bd4537277fda9c7e6e70318290ee3f17ce0271433ebff5c3ac0a28394f1d1d0" + "resource": "repo://TFM/App/TermindApp.swift#L216-L218", + "version": "repo-lines-v1:sha256:3a94d41db2d065bd91683653fd74ad6256d57459b5daa8038d86338ccdee8066:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZjQxYjRmYjdlYWU5ZjViMjNlYmQ5NTRhNDc3ZTAyNzk4NTMwMmYzMWZlYTg1NWU5NDYzNmEyZTY1OGVjNTcxNSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg5M2Y0MGUzNzgxZWI4OTZjODJkMDQ2YTA4MDY3YTExZTM1Y2RmMmYxZGZhZjI2ZmVhOTBiOGRiNjY3NjQxMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOTk5YmRlZmVlYWNhNjM0MGUwMjkwZGM4NmU4ODljODlkNWUxODlhYTViYTQ0YjU4NWIzYjcwNzM0ZjRhNjJkOCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiODQ4Yzc4ZGJlN2M3YzRkY2Q2M2JhNDU0OGRlYzNiYWY0Yzc3YThhZTdjMjQyNWEwYjZiNjFiMjBhM2M3NzQyZiJ9" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataDeviceDirectoryRepository.swift", - "version": "repo-file-v1:sha256:6e7d5f4abe1a0727bae40cc3363e3f1261af7cf38f849021a89a93a9a515a348" + "resource": "repo://TFM/Platform/MacDeviceName.swift#L5-L28", + "version": "repo-lines-v1:sha256:62f447159497ac95fea92426571d2fba677916732a330e0ff261089f87a51840:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIzZWIyNmFmMDY4MGI1ZTY3MGVmYjZkZGRhNjRmNGEyMzVmNDhmNTE2N2NlMWY4OTYwMDQ0MDU0MGE1OWRkMzIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzZmYwZWIwMDFiOWQ5Njc3YTg1ZmE3MWFlMTZiMzFiNTExNWJkOGY4MWYwOGMyMWEzYjYwNmZjNTU3ZmY0NzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVlNGQzYWNkOTllYjdjOGNhNzFkNzZjODJjMTIxOTcxNzU5YjBlOTFmODVlNzhhNWQ1NDJlOWUzNWQ4ODFhODUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoyLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM3OTRkMDJhODEyMTk0NGQxNzZkMjY1ZTliYjljYzU5ZjZhYjg5MGJjYWRmOGFlZmRlYmRiYmI0ZjJlYTg0NDQifQ" }, { - "resource": "repo://TFI/App/MobileAppModel.swift", - "version": "repo-file-v1:sha256:f83323dded23ec74be6130fa9c8ad66d188b5d0cce9cf7f82b226f4f9d58769c" - }, - { - "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "resource": "repo://TFMTests/MacDeviceNameTests.swift#L6-L30", + "version": "repo-lines-v1:sha256:f88a0b05a8e586cf8c56dcd0d1fb84f5a58a6e345ef5d637f378c41a2d80dd3e:eyJzZWxlY3RlZExpbmVDb3VudCI6MjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUxZjkyYWFiZTUxNmQ0NDUxNGJhNmNjMmNjYTlkZWQ1MTkwZjZkNGE2NDllNDNkOTA2ZDU4MTJjNzYyNzliODEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc1NTJjOTcxMTBhMDM1ZjNkYjQ2MWIzM2VjZmQ1MzAwNzk5ODMxZWJlYjdlMmEzOThkMzJiY2ZmNTJlYjFlNWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" } ] }, @@ -337,7 +333,7 @@ "version": "repo-lines-v1:sha256:9b976a3c79403a28666d0d6abfced224760f174922afd517599f296933ac3225:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE3Y2E3MWJkZmM2MGVhMzNkYWQ0YWI2NmNkYjBjNmM5MjUzOGY1NDA0NjRkYzY1OGExMTkwYjUyZTc3MGUyMmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJiNDUwODY5MDY5MjI5ODFhMmRlMDMzM2UzMDI2NDVjYzU4OWQ1NGRhYjM2YzdiMDI5ZTMxZDI4ZGQzMDMwNjgifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L444-L491", + "resource": "repo://TFM/App/AppModel.swift#L452-L499", "version": "repo-lines-v1:sha256:c92479faef57fe5ee7e7f7a65a7348e3ac5d60690eec7919ceb80d380e606dee:eyJzZWxlY3RlZExpbmVDb3VudCI6NDgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY3MDMyZjUzOTcxNDA0YjMxMjM2MmZkYmQ5MzlmOGRlNjYzMGRiNGVhNTQ1Mjk5MTUxNDVlMjNmY2RjNTc0MGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdhNmVkNDJiNDliZDBjNGNkMDNiZmMwMzRkNzZiMzI2NjliZmYwOGYwOTE5OTRkMDUyNTIwODllYmEyNTEwNWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlYWIxYTM2OTk4NGYxYTNmZTIxOWU5Y2MzZGI4YzY3MjI1OGU1ZGM3OGUxYTdlNmQ5OWE4MDcyYjA5Y2ZlMjkifQ" } ] @@ -377,7 +373,7 @@ "version": "repo-lines-v1:sha256:7745f04b3ae9d77c299f8d2440f93f2ef0fae6883447537cf3cb95331a267b2b:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOWQwM2UzMzA5NTU3MmU0MWQxNDc4OTRhNzI3MDdhMjI3YWRkMmNmMWEyMWJiMGQ3MjJhMDE4MjljMDIyNmYzOSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMzQ3NTM5YjRhMjFjMDI2YjU1NWViMTQ2YTQxNWQ1ZjJkOWVhNTViNDgzNDdhN2I0MzE5NGE4NWJkNzQ1YjUxMCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYmE5NGQyODBhNWVjNDFiNTdmYzI5NzZhYWU2YjNjYzhlMGQ1NjMyMGZmZDU0ZDhmZDMxZjc5NjU1MWQ5MGE4ZCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMWVmOWJhNGVkMmRmNjUwZmQ3MTU2N2FhZmFjZTY3NWY1Y2JlNWU5Mjk5ZmFjZmRkYmUzNWYyYWJiOWRjMWQ0NCJ9" }, { - "resource": "repo://TFM/App/AppModel.swift#L391-L391", + "resource": "repo://TFM/App/AppModel.swift#L399-L399", "version": "repo-lines-v1:sha256:02863e2c6f87ae951cd24dcc45790e9af500d4c28875ceab5e7aca5a1c372c5c:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDI4NjNlMmM2Zjg3YWU5NTFjZDI0ZGNjNDU3OTBlOWFmNTAwZDRjMjg4NzVjZWFiNWU3YWNhNWExYzM3MmM1YyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDI4NjNlMmM2Zjg3YWU5NTFjZDI0ZGNjNDU3OTBlOWFmNTAwZDRjMjg4NzVjZWFiNWU3YWNhNWExYzM3MmM1YyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZDQ3MDc2NjBhYjkwMmJkZWRiYWZiZmY5YWYwNGNiYTMzZTVkZTZjNzBmOTRmODdkNTM4MTZmMjYyMTExZGFiNCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZDQxYjUyZTU0ZTdlM2JmZGE5NmQ0ZmFlZTYyMTg1ODc2ZDYwOWJmNDMwNDAyZTg2ZTYwMTE5OWZiMTUyNGI1MSJ9" } ] @@ -606,7 +602,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/architecture/native-app-directories.json b/openwiki/.claims/architecture/native-app-directories.json index 60512328..11845b40 100644 --- a/openwiki/.claims/architecture/native-app-directories.json +++ b/openwiki/.claims/architecture/native-app-directories.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:4cf9745c57e23b170637ade78db1cfc5d66eaf6a77c614727911d4608c73b3b4", + "pageVersion": "sha256:8c105fdaaedeb4598b200ee265c613034c4ec9808e0d71e89eb0df8f953c73a6", "claims": [ { "id": "claim_8e9cf2adb50847f98fcd4516cb0ed521", @@ -151,7 +151,7 @@ "version": "repo-file-v1:sha256:82359e793dbed7dd57ea6d965a2d8e6beb6a25fc339dc1c36a6886dcebe999c1" }, { - "resource": "repo://TFM/App/AppModel.swift#L52-L66", + "resource": "repo://TFM/App/AppModel.swift#L60-L74", "version": "repo-lines-v1:sha256:02a9431c92fd453a171bc3fee15fc2fc5e630324d5e097e052d12f0d62cde906:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNiYjg2Nzg5ODQ0NmUwZTg4ZmM5NjM1ZjI4NTFkMDhmZThjYzE5ZGIxNzFhNDQ5M2JmOGM0NTVkMTc1M2FhMDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRlODc1NGJmOTViNjY2ZDNiY2MyM2FjNzViODJiMTA2Y2IwYTljNTFhYmUzOGNmNDUzYWJlZGIxMTc1ZDI1YjEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE4MTczOGVlYjU5NGUzM2Y0MzcwOTY1MmQ5NzgzMTVhN2U3MmZmYmI2ZDUwYjI5ZjQwZTE3ZWQ3YWJlMmFhYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImZiZmZkODAyOWYzYjQ4ZjE3M2E2MGU0YWY0Mzc3ZTY1ODQ4MGZlNTRiZDUzNTViNDFhZDVlNmFkN2ZhMTRmMWYifQ" }, { @@ -160,7 +160,7 @@ }, { "resource": "repo://TFM/Features/Settings/SettingsPageView.swift", - "version": "repo-file-v1:sha256:1f9c2221300c6e82d4a539f738405066b0d50137bd597304b70a16e0fe2b0b71" + "version": "repo-file-v1:sha256:14ee756a3acf0aa5377e772147f5953d3d7bd79d251ef2523c6b1ddc8f56b784" } ] }, @@ -186,7 +186,7 @@ }, { "resource": "repo://TFM/Platform/MainWindowCoordinator.swift", - "version": "repo-file-v1:sha256:44e9fa73eb630219c6c875e671030342eba949bb814550dce3279680964cc47b" + "version": "repo-file-v1:sha256:0166b9eab07357665f202205a39a6de25af79bfdda707632e8324cb33f129314" }, { "resource": "repo://TFM/Platform/MetricsMenuBarController.swift", @@ -208,7 +208,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" }, { "resource": "repo://TFM/Platform/MacDeviceName.swift", @@ -218,11 +218,23 @@ }, { "id": "claim_66d56131f7cf483b9a9eecb7bf7ce47d", - "statement": "The desktop app creates one shared AppModel and SwiftData container while its WindowGroup constructs a separate WindowModel per window; the AppDelegate handles native application activation, reopen, and termination, including confirmation of outstanding SFTP edits.", + "statement": "The desktop app creates a shared AppModel and SwiftData container with separate WindowModels per WindowGroup window. AppDelegate handles activation, reopen and application-wide Quit including workspace and SFTP-edit confirmations. Native window dismissal is separately vetoed by WorkspaceWindowCloseDelegate, attached by MainWindowRegistrationView; it respects and forwards the prior delegate and confirms nonempty open window models. Cancellation preserves sessions, commands use performClose, and coordinator cleanup waits for actual native close.", "evidence": [ { - "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L434-L471", + "version": "repo-lines-v1:sha256:af1e65ec0f843bb9cbe46ae31102534bd2717c12bbd78adb3778db6e8f86bdcc:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkyODZhNmM2N2VhNDQ3M2I4YTkyZjk3ZWViMTAwOTNhYjZjYTUwNGRlNTkxMzQwZjY5YTI1NDEyYzJhOTNhZDEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBiYzYwOTEwMGY2MTYwOTk4Y2NmNjE1NjQ1OTMxMDZlYmY2NjJjMWI5ZThhZjgxNTgzYzZjMjU3MzdhZTIzY2IifQ" + }, + { + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L589-L627", + "version": "repo-lines-v1:sha256:58bd501000ff30861f2b1db89f4474e1576904db42c1eecf415e671866dbb265:eyJzZWxlY3RlZExpbmVDb3VudCI6MzksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlZTkzZTAzMjRhYzVlYTE2MjczOTFlYTI1OGNkYTcwNDcyYTRmYjRhMTdjOGQ1OWUyODg2N2YzYzJmNzk3YmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY5Mzk1ODI2MmNkMDg1ZGE4ZjQwZDMxZjU2OTBhZjE1NDg3ZTZiOGU3YzFhNDZlNzQ0NDk2MDg5NmUwODI1MGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoyLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM3OTRkMDJhODEyMTk0NGQxNzZkMjY1ZTliYjljYzU5ZjZhYjg5MGJjYWRmOGFlZmRlYmRiYmI0ZjJlYTg0NDQifQ" + }, + { + "resource": "repo://TFM/Platform/WorkspaceCloseConfirmation.swift#L33-L78", + "version": "repo-lines-v1:sha256:328950e35b60ce7789f0de515913e9a849709fdd2687b7373bb28f9f4c45de9e:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFjY2I5ZDIxNWFmNmY4YWMxYjZjZWFlNjE1N2ViMmQxZDA5ZDY0ZmY0YzNkNjA2MTAxYjU0ZjAyZmIxN2YwMTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + }, + { + "resource": "repo://TFMTests/WorkspaceCloseConfirmationTests.swift#L56-L109", + "version": "repo-lines-v1:sha256:c0d1e4726a36089748f7a5d28c5e3781d8fe91cf54d7ad13b6cd833ad54b5c79:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjlhZjA5YzllY2YzNzIyMDkzZjQyYWJlYjE1ZDY2M2I4MjE4MDJkOTlmMGU1OWI3YWE2OWViOWZjNDIxODFlODMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA0NDcyOWIyNjAyNjJhYTYyZmQwNzgzYTU3ODg2ZmQ5MjE5MTFiNGIxOTJhNWRiMmNlZTg5YmM2ZTMyNzJmZWQifQ" } ] }, @@ -398,11 +410,11 @@ "evidence": [ { "resource": "repo://TFM/App/RootView.swift", - "version": "repo-file-v1:sha256:d0c901e4e4a0bf405d36a1fe66ffd8777529c97165e0eb42013812cd3371ed64" + "version": "repo-file-v1:sha256:f8fd87e3753f006035489ddd4feb30ed89c01c72f0a47ea5dfccf1da3c9a00b4" }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" }, { "resource": "repo://TFM/Features/Sidebar/WindowSidebarView.swift", @@ -424,11 +436,11 @@ "evidence": [ { "resource": "repo://TFM/App/RootView.swift", - "version": "repo-file-v1:sha256:d0c901e4e4a0bf405d36a1fe66ffd8777529c97165e0eb42013812cd3371ed64" + "version": "repo-file-v1:sha256:f8fd87e3753f006035489ddd4feb30ed89c01c72f0a47ea5dfccf1da3c9a00b4" }, { "resource": "repo://TFM/Features/Settings/SettingsPageView.swift", - "version": "repo-file-v1:sha256:1f9c2221300c6e82d4a539f738405066b0d50137bd597304b70a16e0fe2b0b71" + "version": "repo-file-v1:sha256:14ee756a3acf0aa5377e772147f5953d3d7bd79d251ef2523c6b1ddc8f56b784" }, { "resource": "repo://TFM/Features/Workspaces/WindowModel.swift", @@ -442,19 +454,27 @@ }, { "id": "claim_7bd0ca0cf2ce40878d78e4eb9f4b0a06", - "statement": "Desktop Settings assembles category forms over app-owned settings and services rather than owning MCP or sync lifetimes; remote MCP editor presentation is Pro-gated, and the local MCP server has a separate Settings route.", + "statement": "Desktop Settings assembles category forms over app-owned services rather than owning MCP or sync lifetimes; remote MCP editing is Pro-gated and local MCP server configuration has a separate route. Language selection persists through AppLanguage.store, writing SettingsKey.appLanguage and explicit AppleLanguages or removing the override for Follow System. TermindApp captures the launch locale for main and authentication scenes; Settings displays a selected-language next-launch notice instead of automatically quitting or relaunching.", "evidence": [ { - "resource": "repo://TFM/App/MCPServerController.swift", - "version": "repo-file-v1:sha256:b7869ad46e75f3e804ea3098d7a58e6567da687414b4a552aff77f6e10795b3e" + "resource": "repo://Frameworks/TermindApplication/AppLanguage.swift#L83-L104", + "version": "repo-lines-v1:sha256:7c33892f4ddf7ac2650b9f09681beea6ba6acf53f454052f249207417d2f0eda:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgyNjNiZTNmYmZlMDdhMTIwNTdiNzIyZTZmOWUwOGM1NWM1MDRmMDE5ZjZjNmRhMWI3ZjhmYjFmODZkM2RkYTgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjVlMmM3MzgzYmM4ZGMxNDJiNGFjNTg5NjljNDdlOTEyODRjZTc2YWU4M2MyNzMyMTdkNTYyMzRjYzk1ZjIzNDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" }, { - "resource": "repo://TFM/Features/Settings/MCPSettings.swift#L14-L79", - "version": "repo-lines-v1:sha256:116dcfbca4d3966c1f2ca5f7cd1a7b4861236e6a5359cde365ad5ca08e1fdfcb:eyJzZWxlY3RlZExpbmVDb3VudCI6NjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEyMTQ1MDA2MTczNGI2NTZhNTJjMmVjOWE4N2E1YWQyODFiZjQzN2I1YjI1MTcwZDFlOTJlZWUwMGJjNTYyM2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc5NDUyMDA3NDIxZjVkMDUxZDljMDZhODJmNmI0NWZkMTNiMWM1MmNlOGEzNmI3OWE3MzdkZTkyOTllZjNmNWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAxOTcwZmQ2NjY0N2E3MGY0YWU0Mzg1OTI5MTM4ZmU0MmZhYTQ3N2UyMGU0ZDZkYWUwN2M2MjU4MWZmNjNiZGIifQ" + "resource": "repo://TFM/App/TermindApp.swift#L237-L271", + "version": "repo-lines-v1:sha256:ca76099aeb8b3f62a3cab3b4ad6f5a13201779bfe9b125c9fba25dc66fd44578:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNmODI0YzlmYjRjY2M1MWYzMjAwNzJiNmNjNzQ2YWVkZWU2MmQ3YmNhZGY5NDI4MmJjMjdjZjNkMjc5ZWQ4YTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcwYWNiNzk5ZWEyMjY2MWRjODkyYmE4MGY5ZWIyN2RiOTAwYjVkYWI2Y2E3MzU5NzBkNWYyZTdkYTZmOTdlOTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI0YjM2YmEyMWUzM2RlNTRiZWIwNDc5NWEzZWQxNmJkMzJmYWRjZDBmMDBjYjUwYmFhNzcyMDVmNmZkOGY1OWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBkMDhkODM2ZTY4Mzc5OTU3YzIwM2Y1MzRjODNmYTkzZDZmNWFkMjRkYzVlZDBiOTY4ZTUyZWM2ZDM4YzU4ZTgifQ" }, { - "resource": "repo://TFM/Features/Settings/SettingsPageView.swift#L116-L207", - "version": "repo-lines-v1:sha256:81f98d4d45e018deef4a8d972f925354f02078e6fa04c1f6ad751917ba650e09:eyJzZWxlY3RlZExpbmVDb3VudCI6OTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYwMjA0MzJkYzQ3NGFmN2VmY2NlYmMwZTQ1ZDAzYzIxYmM3OTY0ZWMzZWY5NDE2Zjg5NmRlMDVlNmM0ODAzMzQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImYzZTZlMjQ4MzhhNTIxMzQxNzA1OTIxOGEwN2UzNWUyZjFjNmNkZTk0Mjg3NDZhZGNlOWVhNmI4ZTFmN2Y2M2UiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgwNWFjZjVlYWEzYTQ3ZTc1YzVkNzNiYjk5NDYxZmY4MTI2ZTc0NjgyODQ4MTI1Nzc4NDk4MjQ3YjZjNmZiOWEifQ" + "resource": "repo://TFM/App/TermindApp.swift#L39-L52", + "version": "repo-lines-v1:sha256:0cdc4175b0298fa994299b5fabbffccaaf1e0e47f36f6cb10443d798df0544fb:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA4M2JiZTU3NmU5NDE0ZWIyYzE0MDE5OGMxMjk1NTQxMzRkYTlmZjIwNjk0ZjliZDM1ODgxNTI1ZTI3ZTA4OWMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdjZDE3ODg3ZDhiOWFjYjJlZjc4ZGRkZmJiNTJkMWEzM2UwZWI2MDRlMmU1NDczMjI5OGZhNmUyMjgyMjc3N2YiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjgxODc1OGVhNGJkODc2YzM2MDJkYzg0YTJiODYyNGMyMDg1NGY1MWQ3MmZhZTVkYzA3MmZlZDkyNzdiOGMyMWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA3OWQ5YTZjMjc5YzZiNjliODNlNThmZmE5YzcyM2ExZmM5Y2QzMTJkMGRiYjIwYjVmMmViOWE0NDkzNDVmMDAifQ" + }, + { + "resource": "repo://TFM/Features/Settings/SettingsPageView.swift#L398-L456", + "version": "repo-lines-v1:sha256:58fe885f444d8f8086236f688d9b5af82443d0ac003d36411594f31db051c5ce:eyJzZWxlY3RlZExpbmVDb3VudCI6NTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQxMDVjM2E4ODE5MTk2MzUxZDEyZWM4ODUwMGY5YWMwODhjMTc2YzBiMzY1ZDA1MGJkZmFkY2JiNzVlNWY1ZmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMyMzQ1MDExMjEyZjIyMmJmZTAyNjQwMGNiNjM4NjcwMjcwNjY3M2RhOTViMGNkZGJjMDdiY2QwNjI2MDgzOWEifQ" + }, + { + "resource": "repo://TFMTests/AppLanguageTests.swift#L94-L105", + "version": "repo-lines-v1:sha256:90d6d1abe103675b9f5fc46c735d660f8ca5232e77152cf519ba2b9112b85ba0:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI1NjlmMTMyNDk2MmJkOWU1ZGYxNjRiZjdiZDA4Zjg4YmRhNzBkYTE1ZDFhODg2OGM2ODJjNDUyOTQyYjA0M2YiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg2ODI1MzgwYTc5YzlhMTUxOTJiNmQzMGVjNTkyZTQ5ZWQyMTZhOWQ3ODI3YTQ0NjZjYTAyNWNhMTI0MTkyNDEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE3ODcxODQ2ZDhlYzM4OWNlZjk4ZWM0ZmI5M2NhOTUzMWMyN2ZjMzBmMTVlMTlhNmZlNDA0Y2UxNGVlOWQ5MGIifQ" } ] }, @@ -482,23 +502,23 @@ }, { "id": "claim_9e6e3f94002a4a178ed7306862b3933c", - "statement": "Desktop composition creates shared app state and a per-window model; a store-in-use failure presents a dedicated warning and exits while other startup failures abort, and AppDelegate defers quit to recheck pending SFTP edits, cancel on refusal, or save window size and await cleanup with a one-time three-second termination reply.", + "statement": "Desktop composition creates shared app state and a per-window model; store-in-use startup warns and exits while other failures abort. AppDelegate defers Quit, first confirming open workspaces across registered windows, then rechecking and confirming pending SFTP edits; either refusal cancels. Approved termination saves window size and awaits cleanup with a once-only reply after completion or a three-second timeout.", "evidence": [ { - "resource": "repo://TFM/App/TermindApp.swift#L204-L265", - "version": "repo-lines-v1:sha256:08f7a061668941e085b7de2de1000e31b9dde8fc9dc0ac2a9b1d991028fbfacc:eyJzZWxlY3RlZExpbmVDb3VudCI6NjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJiZmM1YzhhYWZiYjg3NzE0MjI1ZjhlZWQ3OGNhNzU5MDZhM2U3OGU2MGE1OTEzNDhhOTAwNGJjZmM1OGFmOWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVjZThjZDVkNTA2ZmYwYjY4ODQ3MTFjMmMxNTI1YjdiY2UwMmY0NDMxMDU2YTk3Y2E0ZDQ1NWY3N2Y5MTg4MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRlMTUxMzI4MTYxM2QzMGVlNWZkMWYwN2M3NTg2NDgxMGQyYzU1Yzc2ZTk4NWU1NTY1MGJkMGEwNmNiNDViMDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdiOTZhMjE0MmNhYWZkMTdiNGEzYzMwYjI3MDMzZWIxMDMxOWZjNzEwOTFhMTc5NzY4YWQ3YTVjNmU3NmE2YjAifQ" + "resource": "repo://TFM/App/AppModel.swift#L48-L52", + "version": "repo-lines-v1:sha256:0040c11192f484a9fb37242cf51df33fb5073489845788cd25375cc750bd1444:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYTYyOWE4OTEyZGFmZjY3MGNkN2ViOTFjZmM0Yzk1ZmM1NTVkYTU5OTQ3YjQzZDM1OGIxNzUyMzEyMWE5MjQ4NyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZiMDQ0OTE0Njk3MjJlODM2M2E5NGU0MDQ0NTM4ZTAxM2EyYTIxN2UzNmIxZTU2OGJhNjBhY2I3YjRjOGYyZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOWJiOWRiNWFmZWE2Njc5MmIwMjlkNmE2MGI1ZjM0YTFkMzQ4ODhiNjU2MjkyNTA1ZjJhZmJhM2M0ZWE1ZDI1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZjA4ZTZjYWE2YWU1NDdjN2MwYjNkYjIyY2Q0NWNlYzY5ZjBiZDEwNGE3ZmZlNDA3ZDMxODgzYWY0NzIxZTM1MyJ9" + }, + { + "resource": "repo://TFM/App/TermindApp.swift#L209-L253", + "version": "repo-lines-v1:sha256:45515d770a0970697df887ca92915f412a9ff926b8877a8e847cfef77a42ebc8:eyJzZWxlY3RlZExpbmVDb3VudCI6NDUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMzM2ZiYzJjM2EwYjdhOWE0ZTQ2YjNmZGE2YmYzNzg4ZTE3MjUzZDkxOTNjZjZhYmY4MmI1OWNmZmE4YjIxYWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI5NTM4NThiMDRkNmQ1NmU0ZDVjMTM0MTU4ZjQwODQ0ZDVkZWQzMzM5YjFmMmFmZmYxZDM3MGM1ZTg1MjdmNTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjMwNTI4YjdkNmIyYWFiNGU5ZWI4ZWUwZDMzMDJhYTY0ODhiYjBiZmQ4NWExM2EzMTE3ZmFhYjU5Yjk0NGY5NmMifQ" }, { "resource": "repo://TFM/App/TermindApp.swift#L290-L314", "version": "repo-lines-v1:sha256:850e12a59f116f944049eaf0226a5d7979e0610d2e89b9adf9e7b220b02dc96c:eyJzZWxlY3RlZExpbmVDb3VudCI6MjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMzNzRjMTE1MjIyZjljYzUwMWEyMjliZmUyMDk1YTNkZWZmMTFiMDg2MjFmOGFiMmI5MzgwZWEzMjQ1ZWM5MmEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNhMTE1ZmUxY2Q2ZDFkNTg2ZTIzYWM3NTJkODUyNzQwZWQ2YmE5ODNlZmU2NDg0NTMwNDM0YmM4MTBiOGQ5ZGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMxYmJkYmE3MTE3NjI3OTU5NzBjZWQxMDc4MzhjY2EyMGY2OGJkYmM0OWFiODdiNTE5YzcwZmEwOTdiZGU0OWIifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift#L45-L80", - "version": "repo-lines-v1:sha256:ce619523b0b8d2b586172ca222314c01e61c8d1f34f47ab70be5d9cd076a684c:eyJzZWxlY3RlZExpbmVDb3VudCI6MzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiY2M2MTFhZjIzYTkxYzNiNzI3ZDBhY2U0ZGUyMjI0MmZjNDkwNmM3MTk0YTYzNDkxOWI2NWQ3MDNkZDAwYjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1NWUzNDQwZWZmODhjNGJiYTg5MGEyNzcwZTIxYzE4MmVhNzEzNGUzMWY5MDE3OGI5YTI1YTU2YTFlZWQ2YmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5MGQ3NmNjNzA0YzU1NzliYjc4YTU5ZmQ4OTgyMDkzOGYzZjU1YmRhODVlZjZhZThkMDhhODM5Zjc2NzcyZGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjMxNWU1MzZjNGQ3N2U0MTM0YmYyMDg2YThjZjRmN2Q2ZGQwMWFjNTg1ZGNhZDI1MDJiNGVmMDllMWJmMzE4YWUifQ" - }, - { - "resource": "repo://TFM/App/TermindApp.swift#L467-L499", - "version": "repo-lines-v1:sha256:209641db46bb153b0476bd566ee4d717edc93bb27063d85095bf219df72b7583:eyJzZWxlY3RlZExpbmVDb3VudCI6MzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjliOGJlZDVmYjA1YjU1Mzk2MGFiMDVlYTU4ZjI2ZDA5MGQ2MDY1NTE5ODkzZGNmOTllZmQ5ZTc1NGQ3Y2VhOGMifQ" + "resource": "repo://TFM/App/TermindApp.swift#L467-L504", + "version": "repo-lines-v1:sha256:8ac2140fc31e6a705706a4d7f26f471bfa6949022eee7b4265dcdd9f759040f8:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjliOGJlZDVmYjA1YjU1Mzk2MGFiMDVlYTU4ZjI2ZDA5MGQ2MDY1NTE5ODkzZGNmOTllZmQ5ZTc1NGQ3Y2VhOGMifQ" } ] }, @@ -507,7 +527,7 @@ "statement": "RootView keeps workspace content mounted in a persistent deck beneath non-workspace destinations, so switching to Vault, Assistant, Metrics or Settings hides but does not destroy session views.", "evidence": [ { - "resource": "repo://TFM/App/RootView.swift#L391-L439", + "resource": "repo://TFM/App/RootView.swift#L382-L430", "version": "repo-lines-v1:sha256:f0b2ae44180a4334c03b0e087292f318df3a66f475b3fbbdfc60784351930cb0:eyJzZWxlY3RlZExpbmVDb3VudCI6NDksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYwMjA0MzJkYzQ3NGFmN2VmY2NlYmMwZTQ1ZDAzYzIxYmM3OTY0ZWMzZWY5NDE2Zjg5NmRlMDVlNmM0ODAzMzQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjExZGM3ZDc4ZWRlZDE0NzliZDk3YTZjN2UzYWQ0OGQxNzFhNjRhZTNlMTQ1MGNhNjBlZGRlYzY0YTViOTFlZTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjY2MTcxMDUwNWVkNzc3MzZhNzZlZmNmOGM1YWMxNzUxNzczYjg1Mzc0NzhjN2U2YmJlM2Y4YTNiNDA4ZmNjMjkifQ" }, { @@ -558,19 +578,19 @@ }, { "id": "claim_5d672aca514b4e7ea517539bb64a3b28", - "statement": "Desktop app delegate can launch hidden at login and controls reopening of windows independently of a Settings route.", + "statement": "Desktop AppDelegate controls quiet login launch and reopening independently of Settings routes. Window and application workspace confirmations have distinct scope and copy, default Return to Cancel and accept only explicit destructive approval; Quit checks workspaces across all registered open windows rather than just the focused window.", "evidence": [ { - "resource": "repo://TFM/App/TermindApp.swift#L346-L352", - "version": "repo-lines-v1:sha256:1b9bb80e55d92365ce931a5a5b53e10545f3ccba78896fdd85097dae453306e9:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNDVkNjNhNWZhYWViOWU2ZGIyZDhjYmVmOWY5MGRkZWI1MGU4OWZhYjJkMTk1NDZhYjg1MDUxN2Y5NjYxYWU2YyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMzI5OTIwMzJhNDc3ZTlhYWQ0ZjhmZWRhMmFmMDdmMjUzMGM0NjRjZWJkNDI5OWVkMzMzZTA5OTZmZTg2Nzg4OSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOWJmNTFhZDhlMDMyMTVjMTIzMzRhNWFhNDZkYTI2Nzc2OGY2N2Y2ZjgyYTg3YzA0ZDA5MmEzOWM5NzVjYTAxOSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMDliNDE5MmQ0NDZhYjI4MDRmOGI3OGZlNzBiYjYzNzk1Zjk4MGQwOGM4YjQyYWZiZWI3M2Y0NzcyMTRhMTQ2YyJ9" + "resource": "repo://TFM/App/TermindApp.swift#L472-L476", + "version": "repo-lines-v1:sha256:51ebdab552672e7929664ac1e4f35025c46c433533902b691b40e19001e09464:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZmE4ZTUzNGE5NzczMmFjN2JkY2M5Y2Y5NDM2ZThjZDFjNjBmZDYwYjczYzRhMThmZTM3OTE5ODE4ZDgyNGMzYSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMGM2NDc5Y2MyMzA0NDBmZDQxZTgyMjQyMTg2Nzk2OTg1NTlkMjg5M2JkNjdiNDM4MGFmYWI4MTFiMzhhNTVhYSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNGE1NzlhNDJiZjgwNDY3YTc5ZDhiNGE3Y2FjYmFjYjI1ZWRmMTRhZDViN2ViMWU0MzY0ODRiMjg1ZWEwNjUxNSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiN2UxNDZlNjNjMzdiZTdiMGNjZDRjY2ZiNGVhNzhiZDIwOTE4M2VmODcwOWI3YmFjMDkwYmE2NmJmZmQ2ZDY0OSJ9" }, { - "resource": "repo://TFM/App/TermindApp.swift#L408-L414", - "version": "repo-lines-v1:sha256:2b71255bc55c9f9263d42a1ccc2dc8bd67b00b9d20d338cf889c75f6e05c51b7:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiN2UwMjgyZDg0MTAxNWNkNDY0ZGI1YWJmYzk0MTNlOTAxOGExYjI5MmNjMTQwMDIxYjEzMjE2ZGY0YzdiNTg5OSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZiMDQ0OTE0Njk3MjJlODM2M2E5NGU0MDQ0NTM4ZTAxM2EyYTIxN2UzNmIxZTU2OGJhNjBhY2I3YjRjOGYyZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMTY2YWVmM2M5OGRkY2EzNmRjYmE3ODY5YWFlMGY0MDgwZTQ0Y2E2MzQ1Y2RjNTAxMzg4ZDAxYjRjOWY4YTI1ZSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZGQxNTUwOGYyY2U2MGE5MjcyMWQ0NDNlZDJlZTUzOTk0NDViNTE3MWQ1YTdiY2QxYjFhY2ZkNGZlZjQ0ZmEyYSJ9" + "resource": "repo://TFM/Platform/WorkspaceCloseConfirmation.swift#L4-L30", + "version": "repo-lines-v1:sha256:65e25c0b7357bf4a2e34d781eab0fc356f1def761b07e68ac25949fa9478df12:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjYTg2ZTlmMmEwMzRmNDRkZjkxZDMyMmEwNDBjOWZjYTA0ZWI4ZmJiMTc1YWQyZmJmZDVhODNhMDA3NmEzODIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY3Yzc5MTU2YTY1ZWJjN2M0NGE1OTNkZTgzZDk5Nzg5N2I1YmY3NWQ3OWQ3OWY4MWM2YjJiNzYzYjZjMTFhNzYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJkODc5MTJkZjE3ODhhMTg1ODM0Njg0YmM0ZmU2YmM1NzdlZmQwMTBjNmUxODkwZDliNTFkNWRlOTQ0MjlmZjAifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift#L433-L465", - "version": "repo-lines-v1:sha256:14872f2fb0ad6cc4229d2ee10216f3ae106c413beb407c9d6e4f4a9715e1e67d:eyJzZWxlY3RlZExpbmVDb3VudCI6MzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZlMWRlMDE2MzEyOTVkYWI4OTJmZjVjZGUyNzFlZjc5ZDQwM2NjZTlkODNjNzczODk2NzM2ODVjMTIyYTlkMWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY5MGE3YjI3NGUyNDE4MTRiZWM2N2VjNGQzZmQ0N2I4MTcwNmIyZTgzN2ZmMjNhYjhlZWY1NzBlOTI2YWFhNTQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUxMzk3NzgyYzBmZjNmMDk1NjMyMWZlMTM2OTVlN2MyMDk5YTVhZmE1OTA5MjBlMWNmYjJmNDUxYzU0NjYzOTMifQ" + "resource": "repo://TFMTests/WorkspaceCloseConfirmationTests.swift#L11-L54", + "version": "repo-lines-v1:sha256:580c2bdd89c397cf33d1adae12dde6eff6d3c3f0791ef00280a0e6d193072f47:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhlNDgxN2ZjZGUyMzJmOTcxZmJkMTcxNjJhMDI0Y2YyYTU2Yzg2MDAxN2Y5NDMwZWI0YWZiOGJjZDc2NThjMDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIxNWUzMTE0NzI2ZGJkOTM3ZjI4YWY3MDU0ZGEwMzhkYzg0YzdjNjhlMmRlNDEyYTNhYjVlYWU3ODVhNjBhODcifQ" } ] }, @@ -1052,7 +1072,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/architecture/platform-runtime.json b/openwiki/.claims/architecture/platform-runtime.json index beceac6f..9a8e5a62 100644 --- a/openwiki/.claims/architecture/platform-runtime.json +++ b/openwiki/.claims/architecture/platform-runtime.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:c32f8234a8b6a9ced069d542dad15cecdb5d353f9e1c6034452f7ab82dbc5119", + "pageVersion": "sha256:267e535cd563a3cf3299e79802f1bace5c06b2678a69296ef1178fd21434416c", "claims": [ { "id": "claim_bd9f5ddda6b6466eb79c622b02fd73f2", @@ -12,7 +12,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" }, { "resource": "repo://TFMTests/ArchitectureBoundaryTests.swift", @@ -30,7 +30,7 @@ "evidence": [ { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" } ] }, @@ -40,11 +40,11 @@ "evidence": [ { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/App/RootView.swift", - "version": "repo-file-v1:sha256:d0c901e4e4a0bf405d36a1fe66ffd8777529c97165e0eb42013812cd3371ed64" + "version": "repo-file-v1:sha256:f8fd87e3753f006035489ddd4feb30ed89c01c72f0a47ea5dfccf1da3c9a00b4" }, { "resource": "repo://TFM/Features/Workspaces/WindowModel.swift", @@ -68,19 +68,19 @@ }, { "id": "claim_988820a5d0344307af4a55732527e55a", - "statement": "The desktop native window coordinator registers and unregisters each WindowModel, handles workspace transfer/new-window requests, preserves system-restored geometry, and closes the window model when its native NSWindow closes.", + "statement": "The desktop native window coordinator registers and unregisters window models, handles new-window/live transfer requests, preserves restored geometry and closes models on native window close. Its registration bridge establishes fullSizeContentView and a transparent titlebar before sizing, suppresses isDocumentEdited decoration and attaches a native close proxy that honors the original delegate's veto, confirms populated windows with a reentrancy guard, permits empty/closed models to close, preserves workspaces on veto and restores the original delegate on detach.", "evidence": [ { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L246-L275", - "version": "repo-lines-v1:sha256:bbe0d8067f02655ce3a10d1729814680adb066f99fb1a9e7284bd84ba23cb6ec:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlhZDUzYmFhYzE4Nzg1MmI0MzFjMjMyZTA1MTYwNjFkNDk2ZjdhMjE1NmVlNGJmN2RlYTY0N2ZjNjY0OTg2NmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI1ZDM2MGViNzcxMzI4YWMxYmM0MjZmMjc4NWMwMDEwNDdmNGFmMjExYjkzYzI5NGRlYWZhZjdjMjI0N2RlMDciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI4MDljYzUyZTU0MzQxMThmY2FiYzcyNDI5ZWU3ZjA0YmU1MGY3MWE1ZDJmNWU1YTI2ZjViOTUwYjIzMjFiMWUifQ" + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L589-L628", + "version": "repo-lines-v1:sha256:9264a0364b40c87ccdf0c9f6b6dd0e605dd731a8ce45d308eb7afef0a174674f:eyJzZWxlY3RlZExpbmVDb3VudCI6NDAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlZTkzZTAzMjRhYzVlYTE2MjczOTFlYTI1OGNkYTcwNDcyYTRmYjRhMTdjOGQ1OWUyODg2N2YzYzJmNzk3YmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L337-L380", - "version": "repo-lines-v1:sha256:9d0be27eb9bd671f1d070ef2eb3ba5809494e8faff898faf9afa4dad8330aef8:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkMWQzZDYzMmM5MThkZmFiYzJkZmViZTQ0MmMxM2I3MDA2YTgxNzlmZjg5MDYyNDk5ZjBmZTMzNzRjYjFkZjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2ZGJkNDNhODE0ZDU0YzZlOGY1YmYzYzc5NThiYTVmMGJiNDNmNzViNzc5ZWNmYWJjOWEyMzk1ZjRlYzZlMzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMxNTFjMDIzNTg2OTk0MDFhZDg1ZTkxZjZjNGU1YWQzYWU3ZmU5NGE3YWNjOTc0YTg5MTQ4MzZlNzIzZDg3NDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImZiODU1MjNhOWI3Yjg1ZWIwY2RhNmY1ODQ1OWE5YThiZTgxZmVjZWRmMGYwNGMxZTJjOGU0ODhlZDE1Njg5YzkifQ" + "resource": "repo://TFM/Platform/WorkspaceCloseConfirmation.swift#L33-L78", + "version": "repo-lines-v1:sha256:328950e35b60ce7789f0de515913e9a849709fdd2687b7373bb28f9f4c45de9e:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFjY2I5ZDIxNWFmNmY4YWMxYjZjZWFlNjE1N2ViMmQxZDA5ZDY0ZmY0YzNkNjA2MTAxYjU0ZjAyZmIxN2YwMTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L458-L471", - "version": "repo-lines-v1:sha256:356a9b13c6a3d80b4f35aa53ec029a4261c9a64d5c365740003fb4fd11d496ca:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg4MDA1ZTViMzQxMjg0NzIxNTA5NTE0ODRlN2VkYmNmNjhlYTUyYWE2NDkzYjAyMTdjYjA3OGZmOTdiNTllMDAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM5ZjMyZjFiMjI2NTNjYTE3MDYzYTU4ODk0NTVkODQyODUyZDQwYjE1MDllZWQzOGE1YjI1YjBhMWJjNjE2YTciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU1NGZlOWE1ZDY4ZmYzY2I3MWYwYjhmYzIxNmYwY2JlM2FjNjM2OThkYzkwOTZlOGI2YTA2NDgxMzJmOTQ0OTIifQ" + "resource": "repo://TFMTests/WorkspaceCloseConfirmationTests.swift#L56-L109", + "version": "repo-lines-v1:sha256:c0d1e4726a36089748f7a5d28c5e3781d8fe91cf54d7ad13b6cd833ad54b5c79:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjlhZjA5YzllY2YzNzIyMDkzZjQyYWJlYjE1ZDY2M2I4MjE4MDJkOTlmMGU1OWI3YWE2OWViOWZjNDIxODFlODMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA0NDcyOWIyNjAyNjJhYTYyZmQwNzgzYTU3ODg2ZmQ5MjE5MTFiNGIxOTJhNWRiMmNlZTg5YmM2ZTMyNzJmZWQifQ" } ] }, @@ -90,11 +90,11 @@ "evidence": [ { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" } ] }, @@ -230,7 +230,7 @@ "evidence": [ { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFMTests/ExternalConnectionURLTests.swift", @@ -266,7 +266,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" }, { "resource": "repo://TFM/Platform/MacDeviceName.swift", @@ -313,7 +313,7 @@ "version": "repo-lines-v1:sha256:dd03ee06c1e8b8b1576fb669f8d00c56262138152c2931b19c07a8dc20ccefbc:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZGQwM2VlMDZjMWU4YjhiMTU3NmZiNjY5ZjhkMDBjNTYyNjIxMzgxNTJjMjkzMWIxOWMwN2E4ZGMyMGNjZWZiYyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZGQwM2VlMDZjMWU4YjhiMTU3NmZiNjY5ZjhkMDBjNTYyNjIxMzgxNTJjMjkzMWIxOWMwN2E4ZGMyMGNjZWZiYyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYzM4M2RhNzBhYzM2NTZhMjUxY2MzNjU0Y2NjYzk0MjdlOTBmOGE2Y2JiZWM2ZTE4NjI4MDUwNWY5YzRkOThjYyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTgyNmYzNDAyZDM1YTYwZGQzYzAwZjgyNDM0ZmUzMmQ4ZjliNWJjYjUwMDRkYTQ2ZTBiZDE3NjgwODQ1NjQxOSJ9" }, { - "resource": "repo://TFM/App/AppModel.swift#L390-L494", + "resource": "repo://TFM/App/AppModel.swift#L398-L502", "version": "repo-lines-v1:sha256:95f85512022bc21ca36086dca57c50c4315e1a8323e8c5f2f0be204806129eff:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI5ZDAzZTMzMDk1NTcyZTQxZDE0Nzg5NGE3MjcwN2EyMjdhZGQyY2YxYTIxYmIwZDcyMmEwMTgyOWMwMjI2ZjM5IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlNmIwNDQ5MTQ2OTcyMmU4MzYzYTk0ZTQwNDQ1MzhlMDEzYTJhMjE3ZTM2YjFlNTY4YmE2MGFjYjdiNGM4ZjJlIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhN2Y3YWFkM2NlNGVkZWU4NWQyYjhmMGMwOGNjMDU0NDNlYzNmMDdiMjAwNmFkNzk0ZDBhZThkMDVlYjY0NDc2IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI2OTFhMDNkYTRjN2QzMWQ5OGIyNmIyZWE3Mjc4NGM5ZDhhMTYxMjkxN2NkY2ZhNDYwZDlhMmE2YzA0MTg4NTQ1In0" } ] @@ -346,7 +346,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFMTests/RepositoryOutboxTests.swift", @@ -356,15 +356,19 @@ }, { "id": "claim_0f846fc667814b1dba324b10557a622f", - "statement": "Desktop shutdown fences new assistant runs, cancels run preparations, stops stored conversations and waits for their terminal persistence before closing agent background commands and connection pools; repeated calls share one cleanup task, while the AppKit termination reply has a once-only three-second fallback that does not guarantee cleanup completed.", + "statement": "Desktop shutdown fences assistant admission, stops notifications and clears the Dock badge, cancels preparations including unstored captured failure persistence and stops all stored conversations before awaiting terminal persistence. MCP and SFTP drains start concurrently; after preparation, conversation and service drains, agent background commands, agent pools and Tailscale close in order. Repeated calls share one cleanup task; the AppKit reply races completion against a once-only three-second fallback that does not guarantee cleanup completed.", "evidence": [ { - "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "resource": "repo://Frameworks/TermindApplication/Agent/AssistantExecutionController.swift#L94-L108", + "version": "repo-lines-v1:sha256:67bedae5915d7f6a4ba3a785219f82320187368632bd179ce1d31a2c45ade07e:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIxOTY1OWM4ZDM4MWNhY2NmMDg0NjZiNzc4NTk4M2E4Zjc2OWE1MjU0MjA5ODYyODA4ZWRhNTkzNDQ2ZTIzYjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVmM2MyYmU5Yzg4ODZjMmM1YzI4MjYzOTk4MTBmZTYyMDY0YWZhZWEyN2RmMTNmNzQ5OGI4M2QyMmVlODBjODkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjhmNWY2ZGIwODM0YjM4NzEyNGRlZWVmZGZhMjFhOGIwN2UwN2FiYTM0OWFiMWRkZTNlMDg3Yzg0ZGE5NmEyYTIifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "resource": "repo://TFM/App/AppModel.swift#L964-L1022", + "version": "repo-lines-v1:sha256:0ff1141ee4f4feddf6f32a9468ec1eae205106fcab6fc8526562c6b035b6b3ae:eyJzZWxlY3RlZExpbmVDb3VudCI6NTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIyNjQ0ZDEyMmI4N2I5YzI1MDkzYzk3NjMxM2Y4M2IyNmI4NTFjNzZkOGVlYzI0OTQwMjVlZjg5YzdkMmQyNzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNkYmExMmRjNjgzMWEzMWU5NzdjMTMzMDU0NWZiMzZhMTFjYjE2MDQ5YjQ5ZGYwN2MzYjAyNjM4YjIwNzA2YzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFiYzYzMzljNWY0OWY2ZjFiYjliNTNmYjljNDQxNmY1OGNkZTAxNjlkYjRlMGQ1ZDQ0ZmZmYmM4NDdmOGVjYWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkYjkxZWUwZThhZmIwMWVmNzYyMmM2ZTg0NzA2MDM2YmVmMzAwNTc5YWU1ZjcyYmQzOTViMmY3MjllNmRjOWUifQ" + }, + { + "resource": "repo://TFM/App/TermindApp.swift#L290-L314", + "version": "repo-lines-v1:sha256:850e12a59f116f944049eaf0226a5d7979e0610d2e89b9adf9e7b220b02dc96c:eyJzZWxlY3RlZExpbmVDb3VudCI6MjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMzNzRjMTE1MjIyZjljYzUwMWEyMjliZmUyMDk1YTNkZWZmMTFiMDg2MjFmOGFiMmI5MzgwZWEzMjQ1ZWM5MmEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNhMTE1ZmUxY2Q2ZDFkNTg2ZTIzYWM3NTJkODUyNzQwZWQ2YmE5ODNlZmU2NDg0NTMwNDM0YmM4MTBiOGQ5ZGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMxYmJkYmE3MTE3NjI3OTU5NzBjZWQxMDc4MzhjY2EyMGY2OGJkYmM0OWFiODdiNTE5YzcwZmEwOTdiZGU0OWIifQ" }, { "resource": "repo://TFMTests/AppTerminationTests.swift", @@ -474,11 +478,19 @@ }, { "id": "claim_fccd0b9456cb495bb6c515cfcf80437e", - "statement": "The macOS entrypoint sets up its scrubber client and launch language before opening the container, then cleans old context attachments after the container opens; a debug screenshot launch builds an in-memory container and returns an isolated demo AppModel with demo hosts, AI, metrics and onboarding rather than falling back from a failed production store.", + "statement": "The macOS entrypoint sets up its scrubber client and captures launchLanguage before opening the container, then cleans old context attachments after open; screenshot launches return an isolated in-memory demo runtime. The captured locale is injected into main and authentication scenes; Settings stores changes for the next launch and displays a notice, without automatically relaunching or replacing the root locale.", "evidence": [ { - "resource": "repo://TFM/App/TermindApp.swift#L45-L80", - "version": "repo-lines-v1:sha256:ce619523b0b8d2b586172ca222314c01e61c8d1f34f47ab70be5d9cd076a684c:eyJzZWxlY3RlZExpbmVDb3VudCI6MzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiY2M2MTFhZjIzYTkxYzNiNzI3ZDBhY2U0ZGUyMjI0MmZjNDkwNmM3MTk0YTYzNDkxOWI2NWQ3MDNkZDAwYjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1NWUzNDQwZWZmODhjNGJiYTg5MGEyNzcwZTIxYzE4MmVhNzEzNGUzMWY5MDE3OGI5YTI1YTU2YTFlZWQ2YmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5MGQ3NmNjNzA0YzU1NzliYjc4YTU5ZmQ4OTgyMDkzOGYzZjU1YmRhODVlZjZhZThkMDhhODM5Zjc2NzcyZGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjMxNWU1MzZjNGQ3N2U0MTM0YmYyMDg2YThjZjRmN2Q2ZGQwMWFjNTg1ZGNhZDI1MDJiNGVmMDllMWJmMzE4YWUifQ" + "resource": "repo://TFM/App/TermindApp.swift#L237-L271", + "version": "repo-lines-v1:sha256:ca76099aeb8b3f62a3cab3b4ad6f5a13201779bfe9b125c9fba25dc66fd44578:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNmODI0YzlmYjRjY2M1MWYzMjAwNzJiNmNjNzQ2YWVkZWU2MmQ3YmNhZGY5NDI4MmJjMjdjZjNkMjc5ZWQ4YTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcwYWNiNzk5ZWEyMjY2MWRjODkyYmE4MGY5ZWIyN2RiOTAwYjVkYWI2Y2E3MzU5NzBkNWYyZTdkYTZmOTdlOTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI0YjM2YmEyMWUzM2RlNTRiZWIwNDc5NWEzZWQxNmJkMzJmYWRjZDBmMDBjYjUwYmFhNzcyMDVmNmZkOGY1OWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBkMDhkODM2ZTY4Mzc5OTU3YzIwM2Y1MzRjODNmYTkzZDZmNWFkMjRkYzVlZDBiOTY4ZTUyZWM2ZDM4YzU4ZTgifQ" + }, + { + "resource": "repo://TFM/App/TermindApp.swift#L40-L52", + "version": "repo-lines-v1:sha256:cd20324012889da733a80267838543a25b74c58df39f9e0f3214244e0f13c04b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFlMjc4ZDAxODg0M2RhZWJkZGY5YjdlNWI1NzJiNzJhN2EyMmRjMDY5ZWU4OTU3M2FkYjg0NTdjM2E5Mzc0MGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdjZDE3ODg3ZDhiOWFjYjJlZjc4ZGRkZmJiNTJkMWEzM2UwZWI2MDRlMmU1NDczMjI5OGZhNmUyMjgyMjc3N2YiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNlNTU5YzRmYzM3Y2QzNDk4NjFiYzgyNGYzNGIxMTE3M2MwNGFlZDQzMzZlMzU0OWE5MDJkY2VlZDQ2NTc1MTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA3OWQ5YTZjMjc5YzZiNjliODNlNThmZmE5YzcyM2ExZmM5Y2QzMTJkMGRiYjIwYjVmMmViOWE0NDkzNDVmMDAifQ" + }, + { + "resource": "repo://TFM/Features/Settings/SettingsPageView.swift#L403-L456", + "version": "repo-lines-v1:sha256:d3a6e7a4fb7c1aba55553455699157614377c287af24337766a477d64b412aaf:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUzNzA0OGVmY2I2OTFkODE0YmUxYzY0M2Q0ZjBlYmE5ODcwNThkMmQ0YTExMGM2NjQzZDczNDMzOTRiYzFjNmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmYmRiMWRjZGJkNTk4NGQxZjNjNTEwZDBjZjlhOWJjZDQxMjQ3ODg5M2JiNDAxODkwZTcxYWM0OWJkYTk3ZGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMyMzQ1MDExMjEyZjIyMmJmZTAyNjQwMGNiNjM4NjcwMjcwNjY3M2RhOTViMGNkZGJjMDdiY2QwNjI2MDgzOWEifQ" } ] }, @@ -488,7 +500,7 @@ "evidence": [ { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" } ] }, @@ -520,15 +532,19 @@ }, { "id": "claim_f4928952c3594603be6c9a616d38734d", - "statement": "On accepted macOS termination, the delegate saves normal window size and starts async cleanup; pending external SFTP edits prompt a quit confirmation, and cancellation replies false and resets the termination latch without starting shutdown.", + "statement": "macOS Quit returns terminateLater and schedules a main-actor task that confirms application-wide open workspaces before awaiting an SFTP edit recheck and any pending-edit confirmation; either veto replies false and resets the termination latch without shutdown, while acceptance saves normal window size and starts cleanup. Duplicate requests while latched return terminateLater without starting another confirmation task; the three-second fallback starts only after acceptance.", "evidence": [ { - "resource": "repo://TFM/App/TermindApp.swift#L467-L499", - "version": "repo-lines-v1:sha256:209641db46bb153b0476bd566ee4d717edc93bb27063d85095bf219df72b7583:eyJzZWxlY3RlZExpbmVDb3VudCI6MzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjliOGJlZDVmYjA1YjU1Mzk2MGFiMDVlYTU4ZjI2ZDA5MGQ2MDY1NTE5ODkzZGNmOTllZmQ5ZTc1NGQ3Y2VhOGMifQ" + "resource": "repo://TFM/App/AppModel.swift#L48-L53", + "version": "repo-lines-v1:sha256:21a9855a693e14929a76ad0194c8b1503fd2b2cad02a3e1ed6ff54629b38848d:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYTYyOWE4OTEyZGFmZjY3MGNkN2ViOTFjZmM0Yzk1ZmM1NTVkYTU5OTQ3YjQzZDM1OGIxNzUyMzEyMWE5MjQ4NyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg5M2Y0MGUzNzgxZWI4OTZjODJkMDQ2YTA4MDY3YTExZTM1Y2RmMmYxZGZhZjI2ZmVhOTBiOGRiNjY3NjQxMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOWJiOWRiNWFmZWE2Njc5MmIwMjlkNmE2MGI1ZjM0YTFkMzQ4ODhiNjU2MjkyNTA1ZjJhZmJhM2M0ZWE1ZDI1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZDhjNWY5ZDIzOGQwNmYyZDliODc5MmM4ZGFjNjg4OWY4YzVjOTRiN2YwNjQ0M2UxNDE5NTU2N2YwZjk4MTQ5ZSJ9" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L222-L232", - "version": "repo-lines-v1:sha256:166a2746ae245c41ba02b2aba0284733708d4d4eed090a91bb083f3bdb81da74:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMxOGVjMjYzNzA4NDFjMzdiOWQwOTY1ZjJlNDFlYzcwOWIzM2EzZTBiMGQ2OThlMWIwNGU1YmE4ZDljMmQ0YWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNjM2VjMzc0ZDQ0MjQyYjIzZWZlZTIzYmMwZmRiMmVjY2M0MDZkNmZmNWQ2MTQyYWQ2YmYwMTI5MDAzY2FhMjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlNDNhZDg5YzI1YWQ0MGY3ZGZiNjNhOGJmMjExODAwYjgwODg2NTE4MTFjMjBkOTYyYmVlNGNmMmM5MTg5YzEifQ" + "resource": "repo://TFM/App/TermindApp.swift#L467-L504", + "version": "repo-lines-v1:sha256:8ac2140fc31e6a705706a4d7f26f471bfa6949022eee7b4265dcdd9f759040f8:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjliOGJlZDVmYjA1YjU1Mzk2MGFiMDVlYTU4ZjI2ZDA5MGQ2MDY1NTE5ODkzZGNmOTllZmQ5ZTc1NGQ3Y2VhOGMifQ" + }, + { + "resource": "repo://TFM/Platform/WorkspaceCloseConfirmation.swift#L8-L30", + "version": "repo-lines-v1:sha256:e17f009a8fafe7487f8c50d397d891aa79be22a0c268fd8a5afd2d21ffbdbdb9:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY4ZTc5YmRiNzQwMWMzZWU3OGViMTcxZTJhNDU0OTFmNzQ4MmExMTc3ZWMxMjY5NjAzYzA5ZjM1NWJlMzk3MmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNlZjU2NjZhZDhmNmU5ZTRhZjc3YmJkYmEzOTc1NmVmNTNlNWI2NDVmYWVlNDI3YzYxNTJhMmU2NmMwNjYzM2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJkODc5MTJkZjE3ODhhMTg1ODM0Njg0YmM0ZmU2YmM1NzdlZmQwMTBjNmUxODkwZDliNTFkNWRlOTQ0MjlmZjAifQ" } ] }, @@ -551,7 +567,7 @@ "version": "repo-lines-v1:sha256:b555bddfeefb996e592d9ccd0ceb8bf1add6fab74debb13d95057628e5ccdd32:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI0YTU0NzRjYTQyZGYyYjU5YWQ4MzRlZTEzYjFiNTczYTlhNzkxOWM1ODg1NDZjOTg4MWI5Nzc5MWMwZTliODEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ4ZjE0OGYxNWFlNTBkMmE3MjZlMGRhYmNhMWI2NWQ0NzVmNTYyYmI5YTlmZWMxZmVjMWEzM2EyYzdjZDIwYjQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjZkMjk5MDMyMTk5YjM3NTAwZDdiYmRhMTQ4OGI1N2YyNmFjYzUyYjgwZTA1MmQ2MzAzNmZkNmRiZGIyNzMyN2IifQ" }, { - "resource": "repo://TFM/App/RootView.swift#L235-L254", + "resource": "repo://TFM/App/RootView.swift#L226-L245", "version": "repo-lines-v1:sha256:22f5a2fdad1de0ae2cc39153e1f11d73f65d1efbd6c349129bb5040308c6f9d8:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBhZDFjODhkMzAwYTM1MWU4M2RhYTc0YzdkYmQ4NmNlMDk1NDNjYWM4YWNlOWE3OWNkZWE2NTA4YjAxNTc5MTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkxOWJmOTM3N2ZiZmU5NWM3NWIxNThlY2NhMjMwMTRlZmMwN2ZkNzZjNmI5Zjk4NjAwNGZlNDcxNDk2OGRiNmIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjkzODYxNjc2YTBjZTEwMDk4YTM5MjEzZDU4ZjcyZDBjZDRlNzk2MjE5MzM1MGNiNmMwY2M3ZjgyOGU0MjhjODIifQ" }, { @@ -743,8 +759,8 @@ "version": "repo-lines-v1:sha256:20aa5af3d381d4fc40fda59bdb9c5a3cbac6b92a43ee52f283aa68ff8cbcfc61:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA3NjdhZDQxN2RlODMxZTdkYjVlY2RlMDJlMWMyOTlhM2I5OTJjNmIzNDdhOWNjNzMwMjIzYzYyNmZjMjljZWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFmNGVkYjE4NDBiNmE1NzQzNDljYjhiNGEyYTJjMGY3YTk1YjAwYzg1NDhiZDAxZDgwNjdhMjc0OGQwYjA5ZjQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU0Y2JhYmM1Njc0M2Y1OWI5NTY3MmI2YzM0ZDc1YThhNDkxMjAwNWEyZDE3ZWIwNzk2MDczN2VhM2JjZjI2ZDkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdmYTk3ODYyOWQzZDE3ZWMyNTk2NDQ0MjY5YTAwNTk4ZjEwODk5YjcxNGE0YzIwZDhhNmYwNWM1NmM3MzkwMDUifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L578-L625", - "version": "repo-lines-v1:sha256:e320fa1876252c49dc4da3ba860c55cb94af9d76cf80eae43d51cbc300a56f50:eyJzZWxlY3RlZExpbmVDb3VudCI6NDgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJiZjJkODBhMmVjNGVkNmM0ZWQyMThkYTFkY2RlNWJjNzM2ZGIzODEyZTRjY2UxMjQ5OTVmMWZlZDc5MWE4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZjZTY5MWVjMTQyM2NiYWNmYzE3YjJlMGEzNzRjZmFhNDZmYmE4ZTg1NzlkNDE2YTU4Nzc1YzMxYTMxYzdiNTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L578-L629", + "version": "repo-lines-v1:sha256:c7b2f397f6297ed6dc50f7bf062b8ac8749e9cf03f78dfa50d28ec4551d829fe:eyJzZWxlY3RlZExpbmVDb3VudCI6NTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJiZjJkODBhMmVjNGVkNmM0ZWQyMThkYTFkY2RlNWJjNzM2ZGIzODEyZTRjY2UxMjQ5OTVmMWZlZDc5MWE4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZjZTY5MWVjMTQyM2NiYWNmYzE3YjJlMGEzNzRjZmFhNDZmYmE4ZTg1NzlkNDE2YTU4Nzc1YzMxYTMxYzdiNTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" } ] }, @@ -838,7 +854,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/architecture/shared-module-directories.json b/openwiki/.claims/architecture/shared-module-directories.json index b7e18b24..84956ddc 100644 --- a/openwiki/.claims/architecture/shared-module-directories.json +++ b/openwiki/.claims/architecture/shared-module-directories.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:6223dc5ca0ceea965a4e429192024c8206e2779298a056d39bd49a64e9f663af", + "pageVersion": "sha256:78602505605cd93f6a858145bd424c0d2bbd85077f1ce2a1c581bc85f5190430", "claims": [ { "id": "claim_6e30bc8cde134c23815d274543d0786c", @@ -370,37 +370,45 @@ }, { "id": "claim_72a4effdbd6c4615b9408a74fa878c58", - "statement": "SwiftDataAgentRunRepository deduplicates history by message ID across successive run snapshots and stores frozen replay/context backing separately from ordinary messages; it restores transcripts in run order with compaction projection and recovers recorded tool results absent from a later transcript snapshot.", + "statement": "SwiftDataAgentRunRepository deduplicates history by message ID across snapshots, keeps frozen replay/context backing separate, restores in run order with compaction projection and recovers recorded tool results. AssistantConversationOperations commits edits through truncateConversation before projecting persisted user edits in ConversationModel. AgentReplayPersistenceTests cover deduplication/deletion, frozen result recovery, failed/interrupted staging adoption, pre-dispatch cancellation and configuration failure with fresh-context payload checks, and V15 migration without invented context.", "evidence": [ { - "resource": "repo://FrameworkTests/AgentReplayPersistenceTests.swift#L11-L74", - "version": "repo-lines-v1:sha256:57d87c546f9742f54aa6619888ebfd33221813cf86ec59ad4cfa20e37905a6bb:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjgwODVhYzhjNzMzZTQ1ZGUyZmFjMTcyZjlmMjgzNmVkNzVlMDE4NzgzZTM2ZTMyZjI2NGE2ZWEyMTI0NzRkMWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE3MzgwYmYzNGNkYmJjNGVmODdiMWZhMTc2NWNiYTY1OTFiZGJmODk4ZDAzOGNkNmY2MDg5YTAxYzUxNGU3MzEifQ" + "resource": "repo://FrameworkTests/AssistantConversationOperationsTests.swift#L20-L70", + "version": "repo-lines-v1:sha256:5d46378b4d514fc05d62df448026293dd08d19281d68d9ef17e526f806232a84:eyJzZWxlY3RlZExpbmVDb3VudCI6NTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJiNTUyNzliYWVhMTU3NDIwNzZjMzU2ODgwODgwMTIxYjQwZTU5MjlmYzI3YTgxNjRlMjI3N2VmYmVlZTcxYzQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVjYzU0MWFkZGUwM2Y3YzU3ZWYzN2I1MWYyOTgwYWM3Njk0YzIwOTk2OGRkMjdlYjQ0OGQyMTI4ZDJkMjBlNjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQyZWQ3NGZiOTE0Y2QzZmM1ZmQ2MjBlYjFlOTg2MTQ3NjgyMGU3OGE0NmIwMzljNjNlMjA3MWM5YTFlZjE5MzMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIzODFkZTQ5NTM1ODlmZWE0MzY0ZDcwYjAzNTI0Y2IxNjQ2YmQxOTQzMzIwY2M4ZmY3Njc2MjZlODg0MWM3OTEifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L222-L314", - "version": "repo-lines-v1:sha256:074332d6526371d325cd622f41e593dc864d1291169579f84f97097450566134:eyJzZWxlY3RlZExpbmVDb3VudCI6OTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIzYWRhOWMyODlmYTIzOTFmMWNjZWQ4MzlmMTQyOGIxZjNhZmZkMjYwMmYxY2E5MmE5NTk0ZTk5OTNjMzUzYWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA3OTA3YmVlNWRiMjNhMzUxNDcyODQxNzBkZjBmMjBhMDU3NzRhYjRjNDEwYWFlNDI1M2JjNmNhNzI0NzAzM2MiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE3OTRhODhlYWRmMDM1Zjc2MDFjNWRhODBiNzE2ODYwNzQ3NTMwNzg4ZjViYjc1OWU4NDE1ODIwNTRlZTE1M2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg2YmI4ZTFhZWZjZDg0ZTUwYjYzZmQwOTRkODA2MzRjZTVlM2RiODg5OWQ0Y2E5NmFjYjcxYTQzMTA3MzZiNGMifQ" - }, - { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L62-L112", - "version": "repo-lines-v1:sha256:73c06d763d2c9071c47c82f851a261c94f10b18c12e9fb1477b45d7718ac2205:eyJzZWxlY3RlZExpbmVDb3VudCI6NTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc4NjA2MDBjMWQxYTVmOGE3NjdmZTM0MTA1NDNhY2E0NjA0NDAwZmFmODhmNjQyNjczNTZmOGJkMjBiZGQ4ZWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY5NjBjMTViNmMwNDcwNTc1MmY5M2NjMDRhNjdjMzUyNDdlNDI5MDhiNGQxYzFkN2RmMjFmNTY2YzNmMzdlYTYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijk5MGRiOWU4MzA3ZGQzZTk3Y2VmYWY3ODk0NDJiMDc2MWM0YWQxOTAwNDNlNGMzYWRhM2ExMmRhNzk0YTk0ZGUifQ" - }, - { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L785-L839", - "version": "repo-lines-v1:sha256:d7c559067250de1bae8e0749bea447f411c8fe95dd7f7ee569b3c8010463a382:eyJzZWxlY3RlZExpbmVDb3VudCI6NTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAzNzE0ODNiZmQ5OGM1MGM5NDg5ODlmOWEwNTJjMzI1MGUzNzMyYTczYTc2MGY1ZDJhN2I2MjFhNTJhNmUzYjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjljMjdkYzQ2OTBhZmJjZmM5NjgxOGIyYWZkNDY4ZjkwOGUyNDBhYTgwZWEzZWYwZjcyYjM2ZjNhOThjYTUyNjgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNiYjUzMTU2YTNkYjA5MDdmY2I4NDEwNjJjNjc5NjdiMzMwY2I5NTk1ZjViNWEzM2I4ZWVmNTZjZTVlMzczZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNiZTE1OGU4OWZkYzM4OTc0NzZhMGZmYTc2NjE0MmUxNGNkYjIyMTNjYjIzMmQwYTEzMGU3NTlhNWM3ZDllNGUifQ" + "resource": "repo://Frameworks/TermindApplication/Agent/AssistantConversationOperations.swift#L131-L159", + "version": "repo-lines-v1:sha256:897b86b7d6be263de8cedbe5808c42f0486f0f71a797e437e91fd577d809dee7:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU3ODhmN2QzZWVhNGY0NTY0NzdmM2QwMjliNTAzZDI5YmU4YjExOTNiNjJjZTZlOWMzZTk5ZDQ1NDczMjViNDYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIyNjAzMWNiNjEzNmI1NGNjZWFlYTE1Y2YxNTBlOTI0M2NhMTEzZDc1ZmQ5MmU1MWFhNTAzMmQ4MjkwNDYzZjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoyLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM3OTRkMDJhODEyMTk0NGQxNzZkMjY1ZTliYjljYzU5ZjZhYjg5MGJjYWRmOGFlZmRlYmRiYmI0ZjJlYTg0NDQifQ" } ] }, { "id": "claim_0f6882b385b8491e91ef8e79ec902839", - "statement": "Agent persistence marks unfinished runs interrupted on repository initialization; editing truncates a conversation suffix and stages a replacement user run that a real run later adopts without duplicating the message, while conversation deletion removes its related tool and message backing and tombstones prevent late saves.", + "statement": "Agent persistence marks unfinished runs interrupted on initialization; editing truncates a suffix and stages a replacement whose message and replay backing a real run adopts in one save, including failed/interrupted preparation and pre-dispatch cancellation. ConversationModel replaces staging summary and message-to-run ownership in the live projection; deletion removes related backing and repository-local tombstones prevent late saves.", "evidence": [ { - "resource": "repo://FrameworkTests/AgentReplayPersistenceTests.swift", - "version": "repo-file-v1:sha256:14f58c527d4e7bd48d53770f4aa82830f48392d3de991a82f366d4f59abbc88c" + "resource": "repo://FrameworkTests/AgentReplayPersistenceTests.swift#L76-L273", + "version": "repo-lines-v1:sha256:77e705d7a25c4aa0e80af3de346f473d0365079168aed9e815825aa8fa308298:eyJzZWxlY3RlZExpbmVDb3VudCI6MTk4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI5MjJmYTk2NjM5NDVhYjAwMWVjYzFkODQ2NDcxODhmMGZmZGQ5YTYzNzBhMzFhMjkwYjA5YWFiNTc3MTZmOGFhIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhN2EyYmFmMTIwNWQxNjk1MzhlYzFhOTE5YzE4NjU3NjdjOGMyZDA4NTZkMzkyZTNkZmQ4YTYyMGI0NDk3YzRiIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmNzMxNmM2NWFiYzFjNjZmOTFmM2YzODVjNzkzZDIxMzIzZWI5N2U5MWYzYjRlZmFiNDkzYzBhOGJmNWM1M2RiIn0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift", - "version": "repo-file-v1:sha256:a2020171e8e508b6aea665dd867d1fd1bea1c8e8079afbb1543369ade17f2c37" + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2260-L2334", + "version": "repo-lines-v1:sha256:b10c7c378c645206eaf7b10b2fc66a10cb5b8dfb29b16960f8e95bb7ffbbfc19:eyJzZWxlY3RlZExpbmVDb3VudCI6NzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA1ZDljYjE2OTdkZmIwNTZhYzMzNjEzNDI5YTU5OTZhYWU4ZDJjNWRkZTYzODllZDQ0MjlhMzVjYjA3MjEzNTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU3YzRmZjVmY2EyMGUzODk0Zjc3ZmY2NDk0Y2FmNmVkZDU2NzFiZjE3N2YyMmVmMDE3NDBlNzNhMTYwMTc3OWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJiNTdmZTYwMTUxZDJlN2IwYWNlZTEzOTJhYzI5OGUxYzBjOGE2OWRiMzlkNWU4OTllYzNkMzdkMTM4Njc3ZjMifQ" + }, + { + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L12-L119", + "version": "repo-lines-v1:sha256:2b903dd75a66ff1e30dcfaafd8b5f7db3851ebcb1dfdc48007e4ba9c40845104:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJjYmU3MmYzNzMxNDJhMmEwMTgzZTliNTJjMTg5MzFkOTA1MjllYWM4ZDBmYmFjYWUyZTc2NzUxNmMxNWVmYjNiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlNmIwNDQ5MTQ2OTcyMmU4MzYzYTk0ZTQwNDQ1MzhlMDEzYTJhMjE3ZTM2YjFlNTY4YmE2MGFjYjdiNGM4ZjJlIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJiNmVjOGUyNWExOTgwZDRiZWY0NGZjOTVjYmVhMDM2ZGJmMzY4NDYxZTVhNTc2ZGE2MDYxM2FjMWFiYWM4OTUyIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5MDg2NTI3OTliNTFlNzllMWE4Nzk2ZWQzMzQzOWQ1ZjUxMDU2M2IwOTg2NWQ4MjhiY2ExYmQzMGE2NzE4ZmJhIn0" + }, + { + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L410-L518", + "version": "repo-lines-v1:sha256:f2b453dc00332cf2279a1ae9de72564a33559f6e4cc5228882d3a13f88db04bc:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJhMTFlN2IxNGQyMWMzOGM3ZmM1ZTJkZTg3YTIwNDVhMDAyOGM5MjQ2MTUyMjc1ODhjZjcwOTVhOTNlZGQ2OWNhIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlNmIwNDQ5MTQ2OTcyMmU4MzYzYTk0ZTQwNDQ1MzhlMDEzYTJhMjE3ZTM2YjFlNTY4YmE2MGFjYjdiNGM4ZjJlIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5MDg2NTI3OTliNTFlNzllMWE4Nzk2ZWQzMzQzOWQ1ZjUxMDU2M2IwOTg2NWQ4MjhiY2ExYmQzMGE2NzE4ZmJhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5MDg2NTI3OTliNTFlNzllMWE4Nzk2ZWQzMzQzOWQ1ZjUxMDU2M2IwOTg2NWQ4MjhiY2ExYmQzMGE2NzE4ZmJhIn0" + }, + { + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L608-L647", + "version": "repo-lines-v1:sha256:aff0a1ce86c0f9cddba22354100b1fc07b86bd2de8a679a9f0ce7a91c7baf02d:eyJzZWxlY3RlZExpbmVDb3VudCI6NDAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcyYmI2ZDU3ODMxMmY2MmZkZDc5Yzg4N2EwY2M2ZWQwNGNkOTA5NDBmMWFhYTdlOWE1MDIwZWY2OTI5MWE5NTciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjllMGFhODc1MDZkYmJiZDI2ODg4MDNhYTliYmRhZWIxOWFmMjY0M2MyMWMwZjBlNGI4YTg4ZTM4NzUzNTg1YmEifQ" + }, + { + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L885-L896", + "version": "repo-lines-v1:sha256:4ee92abd6430fd299255074bf6ccb5820157bd7dfc82868b315f0705b67887f7:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ5NGQ4ZDkyN2FkMTU0ZTM2ZmFhZjZiMmI2MjMxZDllN2Q2MzRiY2IzZTliMjI2MzA4ZmJhMGVkOWQ3MDY0NjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYwODRjMzRhMWYxMDlmYmRmNWIyZDYwMjdkMDJmNzRjZmI1ZjYyYmRkNTdlNTQwNTg3Mzk5MjU0NWM0Mjc5ODQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImZmMGQ3NTk1MGRmMGZmOWEzZWU5NjYzOWRjYzhmZjVlYWVkYjQwZDY4MmJlYTkyNjIyYjUyMGRkODg3ZDkzZTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoyLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM3OTRkMDJhODEyMTk0NGQxNzZkMjY1ZTliYjljYzU5ZjZhYjg5MGJjYWRmOGFlZmRlYmRiYmI0ZjJlYTg0NDQifQ" } ] }, @@ -632,7 +640,7 @@ }, { "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift", - "version": "repo-file-v1:sha256:a2020171e8e508b6aea665dd867d1fd1bea1c8e8079afbb1543369ade17f2c37" + "version": "repo-file-v1:sha256:370b9d4b8a8a3b9ddbb29e774d178ff9ba6347e2f9cd570326fc7e65b3d53e63" }, { "resource": "repo://Frameworks/TermindPersistence/SwiftDataAssistantModelSelectionStore.swift", @@ -699,7 +707,7 @@ "version": "repo-lines-v1:sha256:ed2da4f019715029f48f05a3dedf5a21c0de0f72bfea7915ccdedaaa23a8f5bd:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQxMWY5MjJmMzJjZWUzYmQwNGY4NWMxN2NlMGQyODRkNjg3ZGQwNTBmMGIzYWU5YWVjZThkNDBkMGU4NDU4NGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZmZjRhMTQ4OTg1ZDc4MDY2YzZmOTNmYmMwYWZlYmQ1MzFhNzc3YTEzYmM3ZTM1MWNmMzUwODQwZGQ2YzhkMjEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNjN2IwZDJmMTJlMzdjZTIyNDg5ODUxMWU5NWJkYWE5NzJjYWExYTU2MjMxZDNiYjkyMmNmYTU3YmFhZTg1ZjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEyMjM3YjQzNjk5NWRiM2ZjZTI4MmFmNDgwNzRkZDJkNWNlM2MwZTU3NGZhZmRiZDFkM2NlMGI0ZjFhZDgyNzcifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L771-L783", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L750-L762", "version": "repo-lines-v1:sha256:17e9d8aa1863459eef413b879dd2014f08d0e0f5649819a4953d6bc7365f5320:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg1YjBmZDZiMzhmNWY5N2VkN2YzZmI5YzAxM2ViMTIwMTE2ZDQxYTk3NDE0ZGFhNzVjODNmNDA5NjQxMDA4Y2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNiYjUzMTU2YTNkYjA5MDdmY2I4NDEwNjJjNjc5NjdiMzMwY2I5NTk1ZjViNWEzM2I4ZWVmNTZjZTVlMzczZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM2MTRmNmMyNWJkOWNkZWVkYWMzMWY1ZGViMTk4ZjExNTc2MWJkNjY4ZjkwYjg3NzlhNjAwMTczMGU4YWViM2YifQ" }, { @@ -889,7 +897,7 @@ "version": "repo-lines-v1:sha256:37419b5fca8d47f4a181b40d2b79deecbc5c3963e7363a47430cc5928733c4e6:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMxMTEwNTNlMDE4ZGZlYWJkZjA2ZWMxMzJhYTNmZDlhMGM0NzE1NGFmMzRmZjQ3YmNhZTJhYTMxNWU4M2U3NGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM2YWEzYzNiZTg5OGIzNmJkOGQ0ODAyMWFmYjM5ZjJmNzY3OWQwMDYxYjc5YmRmODFmYTIwYmRkZDVhMzU0NTciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc0ZTk5YzAwNDM3ZDI3MjcxMmM0MTc1MzFkOGIzZjU3MTk1NmJlOWFjMWRkMWUzMGU5ZTBiMmUwYmUyODBjNmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJmYjcyY2M5MzQ4NTVmMzBmYTM0ZDAzNzk1ZjZjNzRhMTVlMTkwMmQ4ZTM3MDU2MmUwODM3MzQ0ZDJlNTQxNmEifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L531-L553", + "resource": "repo://TFM/App/AppModel.swift#L539-L561", "version": "repo-lines-v1:sha256:dff49b036dd726c637f6a0a27ae0c399f7b1213f65ebb4dd541039d31230d761:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdiMGNlNjE4MThmYTdhM2Q2NzdmN2UzNGFlZjZmOTM0N2NjNzEyMzZjMDMwNTg2ZDZmMmJlNjIxMjYyMDkxNDIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFlNjczYWJkNDU3YTk3ZWVhMTdhNjAwNjJlYjBmYWVhNmJkZjA5NDg2OGNjNmI4ZjhjMGViZGEyZjYxZWQ0MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmOTUwNjk2ZTUzYzAxNzdhNzAyYjA5YzcwNGY5MTkzY2E0Njk3YTQ4ODZjYzRhYTQwNGJlNjM3MTQ0ZjdjOWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc4YmEzZjYxZjJhODgyY2I5ZTIxNThlZjRiNzUzZmQ3OWRkZTA0ZjczMjlmM2Q5MmNhZjJmNWIxODkyZjgzMGYifQ" } ] @@ -1076,7 +1084,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/architecture/system-boundaries.json b/openwiki/.claims/architecture/system-boundaries.json index 7e16cbf0..66c282a6 100644 --- a/openwiki/.claims/architecture/system-boundaries.json +++ b/openwiki/.claims/architecture/system-boundaries.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:9873df397941c0bfd1bd9ea5bb241a61d4e6b93f59df8afb75f807d871fe2341", + "pageVersion": "sha256:b2b3051a7c3fda79b5fd3db1b349441735a2c20c9ac9ea99751270a074df3d40", "claims": [ { "id": "claim_085ce722f39f48eea75366ec3b3f7554", @@ -147,40 +147,12 @@ "statement": "Connection-history ownership is layered and independent from device naming: Domain defines events that reference sourceDeviceID, Application defines the history model/repository port, and Persistence owns event storage/journal transactions; the reusable DeviceRecord-to-name directory has its own Domain value, Application model/port, and Persistence repository, while TFM supplies trimmed kern.hostname with a DeviceKit fallback and TFI supplies DeviceKit's Device.current.safeDescription directly as their app-owned naming adapters.", "evidence": [ { - "resource": "repo://Frameworks/TermindApplication/Repositories/ConnectionHistoryRepository.swift", - "version": "repo-file-v1:sha256:bc820384eb0b3010fde22d9c46b6847f41b982a0df9eb04fe5cbd52cb8ffd4d3" + "resource": "repo://TFM/App/AppModel.swift#L788-L813", + "version": "repo-lines-v1:sha256:3dbd25c27842dbbe5e68b3dec44c23b0726f73db9ce154f48db5df8991632417:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFmYWY1MjA0Njk5ZGYxZjYyZWVlYjY1ODA5NDM4NWJiYTU2MTI0YThjNmQ4ODJjNmEwN2IxZDZiZTA0MzAyMzIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJkZjIwYzczZTZlNWY0YzVhODg1OWQ5MzJjOGM2NDFhM2E2MjdiODI4NjYwYTE1YWJkOGVlNjFiY2M5MjEwYzEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImVmZTg4YmFiMDgwZmI0NTAyZDg4YTM2MDIxNjY3MjFmYmE3N2RhYzgyMDMxODYwNDg2ODQ0NTcyOTBlM2I5MWUifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Repositories/DeviceDirectoryRepository.swift", - "version": "repo-file-v1:sha256:68f529231c8aa605e9668027e609139eee4b28e51794f0a8efbb64b49e291716" - }, - { - "resource": "repo://Frameworks/TermindDomain/Connections/ConnectionHistoryEntry.swift", - "version": "repo-file-v1:sha256:edcf5020f1965dcc1bad5d1b9dc1523cced21a62d1a93f3a1fd079dde25fb37f" - }, - { - "resource": "repo://Frameworks/TermindDomain/DeviceRecord.swift", - "version": "repo-file-v1:sha256:7bd4537277fda9c7e6e70318290ee3f17ce0271433ebff5c3ac0a28394f1d1d0" - }, - { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataConnectionHistoryRepository.swift", - "version": "repo-file-v1:sha256:9b83815bd041eb47effa2441e8338f6c23c064e853756c2bc4f8fccc20bd5cdb" - }, - { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataDeviceDirectoryRepository.swift", - "version": "repo-file-v1:sha256:6e7d5f4abe1a0727bae40cc3363e3f1261af7cf38f849021a89a93a9a515a348" - }, - { - "resource": "repo://TFI/App/MobileAppModel.swift", - "version": "repo-file-v1:sha256:f83323dded23ec74be6130fa9c8ad66d188b5d0cce9cf7f82b226f4f9d58769c" - }, - { - "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" - }, - { - "resource": "repo://TFM/Platform/MacDeviceName.swift", - "version": "repo-file-v1:sha256:d884719f29fe1d5e7a8e980adcc5205737202a72bcb9e839ba39c34f0dcb414b" + "resource": "repo://TFM/Features/Assistant/AssistantView.swift#L370-L384", + "version": "repo-lines-v1:sha256:52f4b873e342548613bd8016a6741e8d58b9eb9d0a7b0abd84782d1996164f38:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdjMzVkODM0MzQzYjQ5MDY1NDM3ZDAwOTVhYTM4MWQxYWM1MmMxZDE3YWUzZGJhMDA2NDMwZDcyZTYzZTAzM2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdiYmU4N2FhYjMzOTZhYTNiNWU1MjAxZjFmYWMwNDU4MmRhOTExNTFiOTAzYzhlNWUxMGQxNzNhMjA3ODE4Y2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg0YzhlNDg3MDMxYjBmYmUzMzBmNmZjMzc5MzQzYmE0NGRiOWZmNWNlMzQxYjgxNjkwYjY4YTlmZWQwZjkwZjAifQ" } ] }, @@ -282,7 +254,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" } ] }, @@ -600,7 +572,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/concepts/agent-runtime-and-assistant.json b/openwiki/.claims/concepts/agent-runtime-and-assistant.json index e5bfe4a0..c3685dea 100644 --- a/openwiki/.claims/concepts/agent-runtime-and-assistant.json +++ b/openwiki/.claims/concepts/agent-runtime-and-assistant.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:7da7643ceb02ed8cb75346346cd3224bf2dc34d7374f7045e79f760cf86957fb", + "pageVersion": "sha256:7fe20e0875f4d5634a25749f89a26ddd5403e2c6a96515417a863d603cc459a0", "claims": [ { "id": "claim_981f16f030e9469588af705aa377161e", @@ -36,7 +36,7 @@ "evidence": [ { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" } ] }, @@ -100,7 +100,7 @@ "evidence": [ { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" }, { "resource": "repo://TFMTests/AgentRuntimeTests.swift", @@ -143,11 +143,11 @@ "version": "repo-lines-v1:sha256:114d3a8f7c59e4bc4d31ce15bc0b57c9f30d25de36a8c89842532f55d243386f:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc4NjA2MDBjMWQxYTVmOGE3NjdmZTM0MTA1NDNhY2E0NjA0NDAwZmFmODhmNjQyNjczNTZmOGJkMjBiZGQ4ZWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY1YTc2MDcyNmFhZWVjYmZkZTBhOTRlNzRhZmExYzQyZGM2Y2Y2MGRlNGZkNWQyZmY1MmMxNjU5ZjM1ZTlmOTUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY5NjBjMTViNmMwNDcwNTc1MmY5M2NjMDRhNjdjMzUyNDdlNDI5MDhiNGQxYzFkN2RmMjFmNTY2YzNmMzdlYTYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjkzNTkwOWM2ZTA0NDZlY2VlYjIzMTdlN2QwZDcxNGVkNTAzNzNiOGZmZDhlNzQ2OWI4ZGEzZGI4YzU5NTMyZTYifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L785-L839", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L764-L818", "version": "repo-lines-v1:sha256:d7c559067250de1bae8e0749bea447f411c8fe95dd7f7ee569b3c8010463a382:eyJzZWxlY3RlZExpbmVDb3VudCI6NTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAzNzE0ODNiZmQ5OGM1MGM5NDg5ODlmOWEwNTJjMzI1MGUzNzMyYTczYTc2MGY1ZDJhN2I2MjFhNTJhNmUzYjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjljMjdkYzQ2OTBhZmJjZmM5NjgxOGIyYWZkNDY4ZjkwOGUyNDBhYTgwZWEzZWYwZjcyYjM2ZjNhOThjYTUyNjgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNiZTE1OGU4OWZkYzM4OTc0NzZhMGZmYTc2NjE0MmUxNGNkYjIyMTNjYjIzMmQwYTEzMGU3NTlhNWM3ZDllNGUifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L906-L918", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L885-L897", "version": "repo-lines-v1:sha256:f142043bb1b01de9a0458b3995239712303b9ee5f7cd1bc4531d3ee6da2913f1:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ5NGQ4ZDkyN2FkMTU0ZTM2ZmFhZjZiMmI2MjMxZDllN2Q2MzRiY2IzZTliMjI2MzA4ZmJhMGVkOWQ3MDY0NjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImZmMGQ3NTk1MGRmMGZmOWEzZWU5NjYzOWRjYzhmZjVlYWVkYjQwZDY4MmJlYTkyNjIyYjUyMGRkODg3ZDkzZTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" } ] @@ -161,7 +161,7 @@ "version": "repo-lines-v1:sha256:7c9756ce11f5a802355fdf037aff24ef90f81e71df715249370eef766c0a4ab0:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ2MTZmMWZiZWQwMTYzZTEyMTU4OGUxOTZkMjA3Zjk1ZWYwNTFmMGZiMDEwN2YyNDI4OTA1MzAyOTEyYTVhNmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJlMGJhYWZiMDE3NmFmY2M1Yzg3NzI0MTkzYmY4ZmM2NWU4MzgwNGUyYWUwYzk5YjZkODViZjViMmY2ZWFmODEifQ" }, { - "resource": "repo://TFMTests/AgentPersistenceTests.swift#L1206-L1255", + "resource": "repo://TFMTests/AgentPersistenceTests.swift#L1212-L1261", "version": "repo-lines-v1:sha256:309eb4d5282b146727667728b061d8a1bf59db4633971690ac54823192c7dd34:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg3MzRkODNjMTFmMDlhYWJjODY3YTUxODMyZTI4MmE0ZGVlMjEzMmZhOGY5Mjc4MWMxNWU5ZGM4MzkxMTcyMTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJhZjRjZmQyZjMzYTRkZmMwMDJmZmE2MWVkMDg2ZWU0YWIzNWU3NjA1YTZhNGFlZmM5YTc5YTFkZTMyYzIwYzEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU4ZWFmM2UxZGIyODRmNmE2ZmQ0ZTA2NmFlMmZiNjU0YWU2OGZkZmNmM2U3ZTRkY2E0YzJkZDIxNGE0OWI5ZDAifQ" } ] @@ -288,7 +288,7 @@ }, { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" } ] }, @@ -376,7 +376,7 @@ }, { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" }, { "resource": "repo://TFI/Application/Agent/MobileAssistantModel.swift", @@ -390,31 +390,23 @@ }, { "id": "claim_ee0f3773cc6349719ef3197c3a24b62b", - "statement": "Desktop and mobile prepare edits synchronously before creating a UI task, preserving original attachment payload/suffix but capturing fresh submission context; performAgentEdit checks identity before tracked persistence and again before runtime preparation, and failed persistence does not apply the live edit.", + "statement": "Desktop and mobile prepare edits synchronously before creating a UI task, preserving original attachment payload/suffix but capturing fresh submission context; performAgentEdit checks identity before tracked persistence and again before runtime preparation, and failed persistence does not apply the live edit. Editing requires an origin-free non-compaction user target still in the model transcript and is blocked during streaming, compaction, archive, history mutation or captured-failure persistence, but ordinary preparation is supersedable; SwiftData independently rejects origin-backed targets.", "evidence": [ { - "resource": "repo://FrameworkTests/AssistantExecutionControllerTests.swift", - "version": "repo-file-v1:sha256:d981b01f01960be1871c2358f14a61a20d0797d0b6f4fd3d69c1618311242f27" + "resource": "repo://FrameworkTests/AssistantChatCollaborationReviewTests.swift#L49-L59", + "version": "repo-lines-v1:sha256:13fc987e640368f2833dfb474d35f392d33328e2c44f3b982406d0feff96c49a:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjllMmNjNGM5MDkwMzdlOTdkODc2YjkyMmIwNzkwMjIxZDNjNTMyYzk1ZTUxNDVjZDdlYmJiYjQwYWNiNmRhMjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFkZTAyZTc4MGQwOGUxMWNlOTg0ZDIwZGIxNzM3NzBjNjRkZjVmNGJmNTJjMzc4YmY3MmUyZTM4NjZkYmE4MDYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImYwMzg5MDQzZTBhMWRhZjI5YWJkNTI0MDNhNDA2ZWVmOTJkZjYyMWUzMGEwMjkzZjA2MDE4OGRiZmU1NzRmMmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNmYTVhZDA0MzQ0YjRlOGJlODk1MDVjM2M4ODU3ODRiZDU2MGQ0ZjNhZGQwZTcxOWQ4MWM5ZjU4ZGQ1ZDdlOTQifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/AssistantConversationOperations.swift", - "version": "repo-file-v1:sha256:d55c34f3ed990638f514b681cc1b47ca858db56da4794f1250986a0e2ef63cfe" + "resource": "repo://FrameworkTests/AssistantExecutionControllerTests.swift#L657-L691", + "version": "repo-lines-v1:sha256:204b187bc56e58ba5718dae42d8c67838ce91fe119dddd523ff2b5b479bf3157:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFhYTU4ZmU0MmEyZWE4ODVmNDg3OWQ3MzVlOGJkMzU2ZTczYmEwZDFkNjkxYzc5MWVmZmU5YmE5YzJjNDg5ZjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU4ZWFmM2UxZGIyODRmNmE2ZmQ0ZTA2NmFlMmZiNjU0YWU2OGZkZmNmM2U3ZTRkY2E0YzJkZDIxNGE0OWI5ZDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijg3ZGNlYTZjODRkNTBjODkyMjYzNTcxMDQwMmQ5NTdhZGViMzI0ZDg2YmY5NzBiOTQ4NWRjNmUzZGY0NGFjOGEifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/AssistantExecutionController.swift", - "version": "repo-file-v1:sha256:ba9dd21c27203520bd1130ad8404905312d726caf6e4626cd92195e1a9563c9a" + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L1132-L1173", + "version": "repo-lines-v1:sha256:a4bcfff92d242673a486f5cbbd5db0e8e601d38f847b643363023840920b2801:eyJzZWxlY3RlZExpbmVDb3VudCI6NDIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI1MmFjMDJlY2QwNWE5M2M1NGI3NDdjNDIxM2IxZDdmMjY5OGM4ZTRlMjBiYjNhMGI2ZGZiYjZjMTc4MDE0OGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU3YzRmZjVmY2EyMGUzODk0Zjc3ZmY2NDk0Y2FmNmVkZDU2NzFiZjE3N2YyMmVmMDE3NDBlNzNhMTYwMTc3OWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBmMDMzM2U2ZTFlZjc5ZTY1MDliN2ZkNTA5MmZiMDFmMzY5NDQxODk0YzNmZGVjMTliOTU3NTY4NmJjNzc5ZDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU1YjAwYjk5MjY2NDI4ZmU1NTI1NDYwZDE0YzNhMjZlNDI5Yzc3Yjk1ODUyZmEzNzEyYzEzYmNiMTMwM2I3MTAifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" - }, - { - "resource": "repo://TFI/Application/Agent/MobileAssistantModel.swift", - "version": "repo-file-v1:sha256:642174e59430960b53869f98b07b15f773e9e2c70fbdcf514aabbf6132911233" - }, - { - "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L429-L448", + "version": "repo-lines-v1:sha256:5011a53ac6e2d7dde6a23e30f131fba2dbbfc7383362bc0afe868323296d98e4:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNmNzA5MTI3NmEyNGM5MTU5YzRjZGUzZWZiY2RlYWM3YmQ4ZWM3ZjQxYzVkYjNhNmIyOWM3MTE1OGZkZDg5Y2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZkZjhhYTE5NDZhMDViYmFlMTcxZjUyMmVkYzAyODIzMmMxYjVjYmVkNjk0ZWI5NzI0OGY4NWVlZThiODJlMWEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImEzNmFiYjhjZTk2MDkxYmQ1YmU3YTQzZjMyYjRmN2Q0MWI1NjdlMjJhYmY3ZDYxZmZhMTY2NDRjZDRlNDg5ZDMifQ" } ] }, @@ -432,7 +424,7 @@ }, { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" }, { "resource": "repo://TFI/Application/Agent/MobileAssistantModel.swift", @@ -480,7 +472,7 @@ }, { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" } ] }, @@ -498,7 +490,7 @@ }, { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" }, { "resource": "repo://TFITests/MobileAssistantTests.swift", @@ -516,7 +508,7 @@ }, { "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift", - "version": "repo-file-v1:sha256:a2020171e8e508b6aea665dd867d1fd1bea1c8e8079afbb1543369ade17f2c37" + "version": "repo-file-v1:sha256:370b9d4b8a8a3b9ddbb29e774d178ff9ba6347e2f9cd570326fc7e65b3d53e63" } ] }, @@ -576,27 +568,27 @@ }, { "id": "claim_dce95033c20b4420b1a4e93eaa088f42", - "statement": "A validated history edit commits before live mutation and truncates from the selected ordinary user message in canonical run/message order, preserving a same-run prefix and its owned tool graph with a neutral completed summary while deleting wholly later runs, including empty runs; editing is rejected if any removed suffix has transcript tool activity or a durable tool invocation, even if only proposed, denied or read-only.", + "statement": "A validated history edit commits before live mutation and truncates from the selected ordinary user message in canonical run/message order, retaining a same-run prefix and its owned tool graph under a neutral completed summary and deleting wholly later runs including empty runs. Suffix tool activity does not block editing: calls, results, reviews and cards are removed across lifecycle states, without undoing external tool effects.", "evidence": [ { "resource": "repo://Frameworks/TermindApplication/Agent/AssistantConversationOperations.swift#L142-L160", "version": "repo-lines-v1:sha256:45383d212fb6991f01b24f3ea84538b0f619b50ed950673b4c79037e4de1652d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU3ODhmN2QzZWVhNGY0NTY0NzdmM2QwMjliNTAzZDI5YmU4YjExOTNiNjJjZTZlOWMzZTk5ZDQ1NDczMjViNDYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4MzBkMzQxYjc5NmIyZjQyNTk4YTk3M2UwMTJkOGIyZTZjNTE2MDkwYzliMDQ0YWMwZDhkYTIyZjU2MzI4MzciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L1132-L1195", - "version": "repo-lines-v1:sha256:3f458b16bf7519145141b9195e19516fa8904207eff18d53d8f4c36281ce2d21:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI1MmFjMDJlY2QwNWE5M2M1NGI3NDdjNDIxM2IxZDdmMjY5OGM4ZTRlMjBiYjNhMGI2ZGZiYjZjMTc4MDE0OGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBmMDMzM2U2ZTFlZjc5ZTY1MDliN2ZkNTA5MmZiMDFmMzY5NDQxODk0YzNmZGVjMTliOTU3NTY4NmJjNzc5ZDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEwNWQ3YTk3ODIzNWUyMTkyYjA3ZTFkYWM4MmE3OGJhZTQ4Y2UyMWI0MDdmNzAzMTBjNWQyYTI0MjNjMjU5N2YifQ" + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L1132-L1311", + "version": "repo-lines-v1:sha256:250fcf5e385430b19fdda8574e3742f3a2262112690b926243885a336a411119:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJiNTJhYzAyZWNkMDVhOTNjNTRiNzQ3YzQyMTNiMWQ3ZjI2OThjOGU0ZTIwYmIzYTBiNmRmYmI2YzE3ODAxNDhkIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1OTA5YTUzYTYxOGQwMjhlMWQzMDAwYzBhN2FiY2VlY2JlNWZiMzdmNGU1MTFlZmU0NjY0M2ZhZDZlNjY4NzJmIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIwZjAzMzNlNmUxZWY3OWU2NTA5YjdmZDUwOTJmYjAxZjM2OTQ0MTg5NGMzZmRlYzE5Yjk1NzU2ODZiYzc3OWQ1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmYjA0NTczZTY0ZjNmYjg2YTQ1OTZjYmRmYzZmOTNkYTIxYzE1OGJiNjNiYTgyMjg1OWNkMzNmZTNhNTM0OWRlIn0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L409-L539", - "version": "repo-lines-v1:sha256:15ca72b5ef0d6167a6e0275abfc3137f9c24159a2ef7d5eebbc53f5b564f23d0:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMxLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MmJiNmQ1NzgzMTJmNjJmZGQ3OWM4ODdhMGNjNmVkMDRjZDkwOTQwZjFhYWE3ZTlhNTAyMGVmNjkyOTFhOTU3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlNmIwNDQ5MTQ2OTcyMmU4MzYzYTk0ZTQwNDQ1MzhlMDEzYTJhMjE3ZTM2YjFlNTY4YmE2MGFjYjdiNGM4ZjJlIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI3MTAyMWNhNTY3NTNhOGVjNDA3ZGY2OWY4YTVjOGRjMGJiYjgxMjlhNjE0NTdjY2FjYWE3YzhmMDcxMjFkODUyIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5MDg2NTI3OTliNTFlNzllMWE4Nzk2ZWQzMzQzOWQ1ZjUxMDU2M2IwOTg2NWQ4MjhiY2ExYmQzMGE2NzE4ZmJhIn0" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L409-L519", + "version": "repo-lines-v1:sha256:22918a6b32737062dfe23bb6d41d6a729c364c1fcdfec93f023801f4643325e9:eyJzZWxlY3RlZExpbmVDb3VudCI6MTExLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MmJiNmQ1NzgzMTJmNjJmZGQ3OWM4ODdhMGNjNmVkMDRjZDkwOTQwZjFhYWE3ZTlhNTAyMGVmNjkyOTFhOTU3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI3MTAyMWNhNTY3NTNhOGVjNDA3ZGY2OWY4YTVjOGRjMGJiYjgxMjlhNjE0NTdjY2FjYWE3YzhmMDcxMjFkODUyIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI1ZmI4NGY5MTE3YTNlMmZjM2FjZDgwMjk5YmU1OGZjYTlhNjNjMDEzMTNmNzM3ZjRjNGQ0MWE1MzA2MjQyZDUwIn0" }, { - "resource": "repo://TFMTests/AgentPersistenceTests.swift#L835-L881", - "version": "repo-lines-v1:sha256:3f37337fb0989f664099b80e18385148413c93c93a9e1f60cd90820c382c2d5f:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJkOWZlYzhiYjc0ZjczNWI2ZWM0ZWIwYTcxYjc5YWU2ZDAwNjhkNjBiMGQ0NWMxNTZjMzc1Zjc3OTlkODYyNWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhlZjg4NTUyZmE5YjUzZDI2ZDY4NDE4MmZmNDEwODg4NTAxNzVhM2EzMTcyYmY5Mjg3ZDY0Y2UxMWZmNzkzYjgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjIwNGY5NTNlYjgwMzA5MzBiODgzMGFiYjczZTE3Zjk4NzVjMzY3MGRkMzJmYmUwYTMwNDllNzUzODg5YjlkNmIifQ" + "resource": "repo://TFMTests/AgentPersistenceTests.swift#L834-L1120", + "version": "repo-lines-v1:sha256:92a3f9411d5f9baec42c2c077088defcc1fd2747e5b5aa39747e14b7fdda06ad:eyJzZWxlY3RlZExpbmVDb3VudCI6Mjg3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJmOGZkYjFhNGZiYjQzZTgzMjFiNzJlNGNmNWZiNjA1YjMyYzBkYTA5ZjA1Y2Y0NjY0ZDk5NWVhYWFmZmY5N2IwIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJmZWEwNWUyY2RhYTBlYTBjYzM1MDVjNGJiOTViNzU1NjQ5OTk5ODdlMDU3ODI2ZDI3ODEwNjczMjBkMzdlNDY1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJhZmYwZDBlNTllNGYxNWExNzUyODY4NTUxNjI5MWQ1ZTUzNzdmY2Y1ZjEyMjZjNWNlNzY1ZmVjOWMzOGFlNWE2In0" }, { - "resource": "repo://TFMTests/AgentPersistenceTests.swift#L884-L1058", - "version": "repo-lines-v1:sha256:adc95abe146256ac2bfe180058b574ed06a081c7d89d9e0950116fbb8b2c86b3:eyJzZWxlY3RlZExpbmVDb3VudCI6MTc1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIxYzlhMjg4MzM2YWFlNTM2ZWUyNDBiMGZjMGU5MTI5YzMwYmYzZGZiNmJiNWQwYzI1YThlNTk3ODk2N2Y1Y2Y3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJhOWIyMmZhZjU2OTc3M2NiZjQwNmNkYzM5ZTJkMjk2N2M5NDMxYjU0YTAwZmQxZDc1MTdmNjhkNDEzYjYwODUzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlOGVhZjNlMWRiMjg0ZjZhNmZkNGUwNjZhZTJmYjY1NGFlNjhmZGZjZjNlN2U0ZGNhNGMyZGQyMTRhNDliOWQwIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlOGVhZjNlMWRiMjg0ZjZhNmZkNGUwNjZhZTJmYjY1NGFlNjhmZGZjZjNlN2U0ZGNhNGMyZGQyMTRhNDliOWQwIn0" + "resource": "repo://TFMTests/ChatComposerStateTests.swift#L2257-L2333", + "version": "repo-lines-v1:sha256:db6975e09c12d3116643168800e886b85efab683dc7bbab64d74108d55f709d7:eyJzZWxlY3RlZExpbmVDb3VudCI6NzcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVjYzM0MTkwMzRmYTdjYTkyMGQ4OTBlNTBlOGM3ZGVhNzA1MDc1ODk2Y2FmNDUyOTUxNWZkMTc3NWVmODZiYTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjgxNzg3YzM4ZTk4N2JhM2FlZmU5MmNlZTRkZjBhMWQ2OTExYjI1ZmEzZDhiNDk4ZGQ2OTU3MTI2ZmM2N2Y3ZWYifQ" } ] }, @@ -605,24 +597,24 @@ "statement": "Edit tool cleanup prioritizes known source/call ownership; restore infers legacy source IDs and assigns recovered result ownership, letting live edits use retained result and full display-prefix ownership through compaction. Only unrecoverable ownership falls back to the inclusive timestamp cutoff, including modern provider-native events persisted before their source-message snapshot; compactions prune explicit removed boundaries, nil bases survive, and retained openings preserve the title.", "evidence": [ { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L1144-L1195", - "version": "repo-lines-v1:sha256:92f032bab39cb1baa0579b266c5668ecfda0bf7109b19c3a341f5ab8ad93bfec:eyJzZWxlY3RlZExpbmVDb3VudCI6NTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdiOTQxYWMzMGQxZDFjN2M1M2M0NjBmOTRlYTA3MzNhNWM3ZWE1NDgzZmU0NTZmMmJhNTg0MDQ2MDgzYTdjZjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg2ZDg5M2RmMWZmNjEwODQyYzRjOTAwOGZlOWVkYTYwNDYwNWFkYmI5OGEzYjk1OTQ1YTMwNDBjMzE5ZDgzM2UiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEwNWQ3YTk3ODIzNWUyMTkyYjA3ZTFkYWM4MmE3OGJhZTQ4Y2UyMWI0MDdmNzAzMTBjNWQyYTI0MjNjMjU5N2YifQ" + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L1195-L1306", + "version": "repo-lines-v1:sha256:3fe0717a325638caae94822d230ef331f0dc7db42481f5505aaed9529a811ceb:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEyLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3YjYxNDY2NmIzNjYzNWZhMDJkMzhkNGJiYjAyNzE3YjhkM2MyM2M2ZGYxYTlhZWIzMmMzNDY3YzlkMTI3N2VkIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0OGQ0MWEzMDg0NzY3MDkwN2YwNTdmYTM2Yzk4M2JjMjgxMTY4OGIxZjY0MjBiNzY5NTdmMjZjNmZiNDM4ZDM2IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJhN2FhYjQxOTQ4MDMyZWJhMmNiODNjZWViYjA3MWJjZmMxMzc2OGJiMTAxZDY3MTMwNGNhZGQ3YWRmOTQ1NjRmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI0NGYyZWVkNzJiOTA1YmY3MGFlYzgwNTYwN2ZmMTE2NmI4NDg4NDVlM2M1OTE2YTIyMzU4YWMzZDIzMTY0Mzg1In0" }, { "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L264-L303", "version": "repo-lines-v1:sha256:cbfc8cf1f7006df154aa854e224be6a4c03499c62683821492d863dded964ddc:eyJzZWxlY3RlZExpbmVDb3VudCI6NDAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNmODkyOWE2NWI4YTYzYzIwOWQ2ZWFlMzlhZjYyNjJlMTc3OTM5MWRkMDI0N2UxYTcyZjU4NjM1MGQyMDZkZmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUxOGQwNGFjNWYzYmRkMDQ5NDFlZTZiZDUzODBlZWY0YmZiMzVkMTg3ZDI0MDI2ZTgxNmIxZGFlOTdjZWI1NTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImFkZTViMGU0OWEzZjBiZDAwYzFkMjUzMDRmZTk4YmJkOWU4NTJlYTFjZDQ4Zjg0YzYzNzQwOWQ4MjI4MDg1ZmMifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L449-L505", - "version": "repo-lines-v1:sha256:67641bef487476dc108ea2f10c4100e98669b3b91aa633527936564b711eeaf0:eyJzZWxlY3RlZExpbmVDb3VudCI6NTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMxNjVmOWY1ZTZjOWIxNmY2ZWI5NjA1Mjk3ZTExMjNkZTQ3OGJiZThmM2RkYWQyZDNiNzRkNjEyNjExNzdhMjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImEyYjA3MTkyMDFhZDMxMWI4ZTRlNTg2MDg2Njc3OTU4MjI0ZDlkYjE3MTYwYzJjOTc2MTFhMmRhZjc0NmI1N2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM0ZmU5ZTVmYmZmMzY5YzBkODIzMzZlMDA1NGFiMmM4NzZhODBkN2VhZTYzNDVkODgyMDA0YjgyYTc5ZDkxYTgifQ" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L660-L687", + "version": "repo-lines-v1:sha256:bd434e864596637e645d6eb82622055fd9b8b8d494f895a8278ecdca3581663c:eyJzZWxlY3RlZExpbmVDb3VudCI6MjgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg2MjZkYmE4MTBjOWI5NmZhYmI1MmJkNjhhMTFhY2ZhOTRmYWQyZjZmYWY1ODJhMzMyYjA3MmIzNzE2NWI0NzgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjgyMDZiNTMyMTkzMjkzZThiM2U3OGI2ZmY2MzczNDVlMzhmZTJlYTRmZTQyMjc0YTc2NDhiYzlhNGYzNjk5ZjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE3NDJmODZmOTJlMzg5MTljZjQzOGQ5MjdlYTUwZDMyZDkwMjljZjgxOGJmYTFkNWYzYWZiYTk1NGQxMmNlNzMifQ" }, { - "resource": "repo://TFMTests/AgentPersistenceTests.swift#L884-L1058", - "version": "repo-lines-v1:sha256:adc95abe146256ac2bfe180058b574ed06a081c7d89d9e0950116fbb8b2c86b3:eyJzZWxlY3RlZExpbmVDb3VudCI6MTc1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIxYzlhMjg4MzM2YWFlNTM2ZWUyNDBiMGZjMGU5MTI5YzMwYmYzZGZiNmJiNWQwYzI1YThlNTk3ODk2N2Y1Y2Y3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJhOWIyMmZhZjU2OTc3M2NiZjQwNmNkYzM5ZTJkMjk2N2M5NDMxYjU0YTAwZmQxZDc1MTdmNjhkNDEzYjYwODUzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlOGVhZjNlMWRiMjg0ZjZhNmZkNGUwNjZhZTJmYjY1NGFlNjhmZGZjZjNlN2U0ZGNhNGMyZGQyMTRhNDliOWQwIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlOGVhZjNlMWRiMjg0ZjZhNmZkNGUwNjZhZTJmYjY1NGFlNjhmZGZjZjNlN2U0ZGNhNGMyZGQyMTRhNDliOWQwIn0" + "resource": "repo://TFMTests/AgentPersistenceTests.swift#L834-L1065", + "version": "repo-lines-v1:sha256:8d4f3aaa9981af77c9d277310a7b824b764dfba470f0898aa53c3eb4df327438:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMyLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJmOGZkYjFhNGZiYjQzZTgzMjFiNzJlNGNmNWZiNjA1YjMyYzBkYTA5ZjA1Y2Y0NjY0ZDk5NWVhYWFmZmY5N2IwIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJmZWEwNWUyY2RhYTBlYTBjYzM1MDVjNGJiOTViNzU1NjQ5OTk5ODdlMDU3ODI2ZDI3ODEwNjczMjBkMzdlNDY1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5ZDAxODMwNWY5YTk2MzdiODgxYTY4ZWZlZjM3OGU0NTdhMDkxYWExMmIwYzQ2ZmYzZDVmYjgzN2RkOTE1ZGY2In0" }, { - "resource": "repo://TFMTests/ChatComposerStateTests.swift", - "version": "repo-file-v1:sha256:f4a3cc9c49409a681d6a8af4cd2df0ccac1487f7c336fe23084c3d702551344d" + "resource": "repo://TFMTests/ChatComposerStateTests.swift#L2335-L2419", + "version": "repo-lines-v1:sha256:59fe0175816eb6f89aaa1dbac680eef8ece5cdcbfe67c2e68675573587db8051:eyJzZWxlY3RlZExpbmVDb3VudCI6ODUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjVlYjI2NjhjNzZjODExNTI0Y2Y0NTRlZTkyNjUyYWMxNzU2ZTdjNGRiMDBkMDMwYjczMWE4OGNhZDMzM2Y1MWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ5NzZiYzRlZGFlZTk4NTVlZWFkMjc5MDI4ZmZlMDgwMjE5YjQwMzQzMWM1NDA3NDRkZWYzNjgyZmIyYTdhZDYifQ" } ] }, @@ -635,15 +627,15 @@ "version": "repo-lines-v1:sha256:e4fad18b6a94ed7b164d925406264f825e9f6fde1c027aaba953000636d429f2:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MmJiNmQ1NzgzMTJmNjJmZGQ3OWM4ODdhMGNjNmVkMDRjZDkwOTQwZjFhYWE3ZTlhNTAyMGVmNjkyOTFhOTU3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJkY2UzN2IzN2YwNGIxOGQwODk0NmFmYTY3M2UwYzIzNjcyMWEwMzkwNTQ2YjM4NTUyYzI0YWY3MDc4ZTdhYjMyIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlY2U5NjA2M2ViYmNhNjY1M2Q0YTYwYTViNjBjZWE4OTRkOTVmOWExNjQyZTQxNDEwMjFkNDZlNjQ4MjgyNGVlIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmNTcxZWY2ZjVlOTYzMTE4ZDM3MTdiNGQ4MmI0YjBiOTliMmI0OTI3NjYzNGFjNWZiNmZhMGE3ZTVjNDBlZjhjIn0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L507-L535", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L486-L514", "version": "repo-lines-v1:sha256:26c3ef0906ce4f204aec28cf677038c0753dc81b380b85204b76a2f8962b2817:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgwNzAyNmVmMjJiMzJkYmQ3OWY3NDc4ZDYzMTg3YTBjZDI4ODNjYjYyMTM4MmU3N2QzNGQyMjcyYTA3MzFlOTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE3NDM2MjdlZDk4ZTAyYjU3OGY1MjY5Yzc2MGVlNTg0ZWU1NDJjM2UzYzlhNDQwODkxY2QxNTE2MjVhNTlkZjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRlZjBlYzVlMTdjMGI5MTM2OTFhYzdjYWFiYmVkZjA1MDRiN2Q3YmUwMjBmOGZlZGMzOGQ4NTMzNzk2MjVjOGYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjlkZjlmMzU1MjY2ZmFjMjQ4ZDgxZDMwMDc2YWY4YWRiODFjNDBjOTY3NjFkMWQ3MzQ5OGUxY2RmMDNlYWZlMmUifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L710-L727", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L689-L706", "version": "repo-lines-v1:sha256:9430ebe704cfe45e02e2a23637debf77c4d62d0849a7065c405e4b78d26bff00:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY0MDUyMDRhZTk4ZDM4NjdlMTNmYTU5Y2E0MTU4YmU1Y2FkMTMxODZiZTBiMzlhOTZmNTNkMGQ2YmZkNTZiZjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQwYTA4ZjYzZjc3MGZlNGQ4MDFjZmU5YmFmZTg3NjdhMTEzZWM3ZmE2Njg4ZjA1OGU1NTc0ZGJjYTNjMGIyZTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFmNjMyY2Y1OTNjNzEzYzQwMjI0YzhkNGVhZjhmZWI2ZjUyMWMyNDIzYzRiMTVhYzVkYzFhODAzMGEwM2U3MWIifQ" }, { - "resource": "repo://TFMTests/AgentPersistenceTests.swift#L1061-L1120", + "resource": "repo://TFMTests/AgentPersistenceTests.swift#L1067-L1126", "version": "repo-lines-v1:sha256:0f33e4023a498a0b07b9840bc6164aab10ee8ecf9af5c6a7b7c562e62cd3dd5f:eyJzZWxlY3RlZExpbmVDb3VudCI6NjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE0ZTAzOTdjN2QwYzg5OTg4NTkxYWFhZTZkZmZlNTdiNzI4NmYwODczNTYxMmRkOTc5M2FlYzJhNGYyZjU4MDAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJhZjRjZmQyZjMzYTRkZmMwMDJmZmE2MWVkMDg2ZWU0YWIzNWU3NjA1YTZhNGFlZmM5YTc5YTFkZTMyYzIwYzEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEzNjFmOGZjOTJhNWE1NWNhNmJmYTI5ZDE5NDQ0MTQ5ZmIxMjZhNDRiOWUwZDMzMTExM2UxNzlkNjgwY2Y2NWIifQ" } ] @@ -662,7 +654,7 @@ }, { "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift", - "version": "repo-file-v1:sha256:21a22d7699e409b1d775c74e7edb68971567505a0fb7c729ff026b78f031a8cf" + "version": "repo-file-v1:sha256:3f95e02c923d69b8d360b399669f771a20898bcb7bb09bf147add70c1f336ef7" } ] }, @@ -679,11 +671,11 @@ "version": "repo-lines-v1:sha256:e4fad18b6a94ed7b164d925406264f825e9f6fde1c027aaba953000636d429f2:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MmJiNmQ1NzgzMTJmNjJmZGQ3OWM4ODdhMGNjNmVkMDRjZDkwOTQwZjFhYWE3ZTlhNTAyMGVmNjkyOTFhOTU3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJkY2UzN2IzN2YwNGIxOGQwODk0NmFmYTY3M2UwYzIzNjcyMWEwMzkwNTQ2YjM4NTUyYzI0YWY3MDc4ZTdhYjMyIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlY2U5NjA2M2ViYmNhNjY1M2Q0YTYwYTViNjBjZWE4OTRkOTVmOWExNjQyZTQxNDEwMjFkNDZlNjQ4MjgyNGVlIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmNTcxZWY2ZjVlOTYzMTE4ZDM3MTdiNGQ4MmI0YjBiOTliMmI0OTI3NjYzNGFjNWZiNmZhMGE3ZTVjNDBlZjhjIn0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L474-L482", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L453-L461", "version": "repo-lines-v1:sha256:30e4b2269fdafb0b95f119cd557ef5b625420f3ef1dab432701fb54521508e92:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMWJiOTAwN2E3ZTAwNDcwMjE5MWJjNGJiYzcyN2RkODJmMGM0NTE3N2ZhODY0OTU1YjMyYzA5NWFkOGMxYjVlMSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMThkODhkZDM2NTI2NGYyYjVlZGNmYjM5NDBmMzg2Mzk1MGI2ZDRkYWJhOWY3NDc4ZjM5MzA1ZGRmZTM5MGY0NiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMDdmMTAyMGM1YzI5MDdiZWJiM2Q0NTFlMjdjNzE3NjE1ZDNiMWNiNWU5ZjM2ZDM4Y2ZjMThmNWNhOTI1MDRhNSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNTQ0NDYwZGEwZGZkMWMxYzZhYzc4YTAwYTRjN2NmNzAwYTc2OGE5NjE2NzI2Y2M2NTdkYTMyMjhlZDQxOTc2YiJ9" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L536-L548", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L515-L527", "version": "repo-lines-v1:sha256:f5fa05397f6c5f103da1b22e51552d54d9f197d9f1cd73b34142a57e11b9d9cd:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYxZjE3YzM4MDE4ZjhkMDliMDEwMWYzMDI4ZTlmZDk1MGJmZWVhMjljYjAzMTkxYWVlNDNlNzBmODU2MTViMTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhZGUyYTZhM2JkNmI1M2Q1MWI3ZGZjMzk1MjQ2MDBiM2I0ZDQ2OGMzMWM1Y2ZjNWM5MThiNjgyMDExOGY0ZjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM0Y2FmM2FiYTk4ODdmMWYwNWQ0OTgyM2ViOGQwNWE3YjhhMDkwMzhlNDQxNWE2ZmYzMDg3ZDQxZWRhOTQ2YWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI2ZjkxNzkxMGMyOWI0MDdmMThlZjI1MDJiNmU1NWE5NDc1M2NlYjVhNDIyMzY0NDUwODgyODBiZjg2M2E1YzUifQ" } ] @@ -705,7 +697,7 @@ "version": "repo-lines-v1:sha256:b7521ac6bc4ed6eb283d4fde4a665aa807abcf27718295247910830e414062a8:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjExZmUxMWRjOWZjNzMzZmEwODYyNTYxNGYyYTUwMTBmYWFjODhiZmQxMDMzM2VkZDA5N2UyYmVhZTQ4ZWY4NTkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU3YjBlY2ViYTg1ZmNjNmMwMmM4NGFjMTY5ZWYxZGNmNWYyM2ZhZDYxM2ZhMzMyMDhiZWM2YTJhMWFiNGM1N2YiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImVlYzAyYjAwYTc2Mjk0MTQ4MDZkMTIyMjc2OWI4MGUwZjZiMWM1MWZmYTQwYzU5YzExMjliMmY0MThjYTNlYzYifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2154-L2158", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2098-L2102", "version": "repo-lines-v1:sha256:a1c429dfcebeade12bf0cdca5630d98c473f9671a04c7038a825142db4534adc:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDg3NmQ1NzM5NTczZDQzNzE2OTIwNjIwOGI4ZTNkY2M5ZmMyYjhiNjIwODljNDE2ZWUwODczMzBhYjY1ODY2MCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMWQ0YjBmN2RhNTAzMWRiN2U2MWJiN2I2NGU5NjZiMTMyMWM4NTU0NTI2ZmY1NmRkOWQzMTlmMzNkMTQzYzEyZiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNTFhNDA4MmRmOWRjN2RlNGEzYWNkMDFmNDFiYWYyMDBiZDc4ODFlMzg0YmVhNWIyMzYzOWRhNGYyMmFlNGNmZCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMWZiMDcxOGI0OTQ0YjExYTYyOWM3NDcwNzEwZTlkOTRkNjk0NjE1MzQwNWE5NzRjNzFlYWNmNjRkNWNjMzk2OSJ9" } ] @@ -719,11 +711,11 @@ "version": "repo-lines-v1:sha256:b053494013fc58511669baa9d5fb076972a83b9c5e82c312c89b75c14f305b99:eyJzZWxlY3RlZExpbmVDb3VudCI6ODYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZhNTQ3YmRhMzJjYTJhZjNlYzYxYTg3ZGVhOGNkMTU0MTkyNjYyY2JhZjFkNDkzNThkNjFlNDMyMzIxODRkMWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIyZWMwZDNhZTJkZGU2OGU4YjU5YmQ4ODg0NmFhMmVlYWUzYmU1ZWEwNTllM2RkZjc2NThhMzg3MjkzMzY1OTMifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2733-L2744", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2677-L2688", "version": "repo-lines-v1:sha256:e89752873cdffa42470a3e8e5f35a41eb1ad3de2e5091d9310ff12ce1c6c34f1:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMzZWFiNTQxZmUxMzFlYmZlZTMyYWQ1YjBjMDk3MDcyZTYyOWE0NDFlMjkyZDg2NzBkNTA1MGRhNDYxOWJjNWMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJhMzVlYmI4ZjliMDUwMTY2MjRjMjYzZTM0ZDRmY2M4MjFiNzMxMzBmYzExM2I4NGRhZDk0YmRmMGU2ZDQwZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ2ODE3NGJiN2E0NjhiZTAyZTNkOTExYTVhZDNlOGFhNTZiZDA5NDM5MDlhMzM4MGY1N2FmYWMwNTRmZjM1NDEifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2906-L2962", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2850-L2906", "version": "repo-lines-v1:sha256:bf16b951021a86c889916c6ab69a11734867d745628cbb970b189c32ad4d97ca:eyJzZWxlY3RlZExpbmVDb3VudCI6NTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlkNDYzNTIwN2RjZWU1MzYwODlmYjRlNzBlYjBiMjA3ZGQ1MTA2Nzc0MzdkMzYxYjEwNzYxOGU5OTAzMWVjOWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjIyMjkxYjE2NzIxMjcwYTMzN2M3ZmNiYTk4ZTQ1ZGExY2Q5NmZkNjE3ZTljNTM1NTdhODE3ZDE0OGIxNjE2MDIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImEwZjFkMmE2YjczMWYyMjBkYjJkYWU5YWY4MjQzZTk1NzE5NDNmZmRiNDEyNjNjMzI1MmJhZTdiZTZmZGU4OGYifQ" } ] @@ -741,11 +733,11 @@ "version": "repo-lines-v1:sha256:d0c08e45ef96010507a5f98c0abc0d37d09c3de3184b995e60de6beec3906cd0:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjIwZDhlODgxOTVmMjU5MjExOGM5MTJlYzdjZjY3MDJiMjkzMTRhN2U3ZmIxYzlmY2NhOTZkMTJkZTFlYjY5MjYifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2747-L2787", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2691-L2731", "version": "repo-lines-v1:sha256:73a9fadd27e6313e00dfb2e760c0482e78f6608cffbea2173275fe1261684573:eyJzZWxlY3RlZExpbmVDb3VudCI6NDEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJjMWRiOGU5ZmVjNDE4OWU5ZjIxN2MxODc2MjUwNWJjMWZlZTZhYTYyOTE0YWM0NTYyMjA2MjMzZDYzYzRhYzkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI1MzRjYjRlZmZjNzE2YWM5ZDUwODFhM2EzYjg0ZTBmMTZmNTRhZDVlNWYzNmIyYzM0YmFmMjk2NTg5NThiZTYifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2843-L2895", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2787-L2839", "version": "repo-lines-v1:sha256:22f3d589947e66de8e6b6bf87c5b8f2fdc58432f251d0d955b1f04323416fe97:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNkYzZmZTQ1NzM1MmM1OWNkNjc0YmQyNjRmNDY3NzU0ODJlZGZkOWZkOWE2NTc5MzY4MzUxM2FlNjQ4OGZmOGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI5MjFkNTY0NTkxMDhlOGRmYjliYWIzMDI0ZjM1MDYzZjA0NWI2NzQ1MGE3YTBkOTU0NTdlZjYxZmU4YTRkYjYifQ" } ] @@ -777,11 +769,11 @@ "version": "repo-lines-v1:sha256:38fe1e9a61e603b506dc1a1e4e88f70fa344aa0fac39b7fa9e62678158e05fad:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjNzU4ZTRlNjEyZDFiZmRjZDJlYWYyYzUyNGZhNzk3MmQyZjZmYTYyZTY2NjQ5ZDAzOGZhYjQwNmQzMjUzZjkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdlMDNmOTA3OWQ3NTcxMDI4OGEzYTNhZmEwNjc5ZWJmYTAzMTQxMjQ4Y2FhOThiZGRkYzQwYzZkMjc4NGZkYTIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY1YWRmODQ1NmU2ZGY4MTAxMWQ4YmIxZTg2ZWMwMmY0YTNlMThiNzQ4MjBiZDg4OTQ2M2RlNjQ5ZTE4ZTA0ODAifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2554-L2615", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2498-L2559", "version": "repo-lines-v1:sha256:77668d68a961f9ab30b548321f29c83d849c528027934cf8a78e687f48e34bb4:eyJzZWxlY3RlZExpbmVDb3VudCI6NjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc0NzRjZjQyMmE2ODUzOGNmNDI2MDdkYjViMDRiMzIwNmNlNGU4MWM2ZmRjMjM1NTc2YmZiM2U2MGNiYzBhZjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZkYmEyNTM5NGIyNjJlOTYxOThlNWI3Mjc0ZDVmZGJiZjQxMmIxZWQxOWI1NmVjMTAxYTE4Yjk0MDkyY2ZhMzciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjY3YmFiNWY4ODhhMmUyYTg0ZjgxNmYyNjYyZGVkNmFkNTdkMjkyZDc0NTA2MmM4NDMyYjZmOTljZDkyMzQ1ZTkifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L3247-L3254", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L3191-L3198", "version": "repo-lines-v1:sha256:123b759b0fbc80688ec4cfb0b8a1e4785aac7544f5e56e57a1986a2a2d8a999d:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNmYyNzNkNmQxZDVkOWZiODdiOWE4NTkzNzQ4NDI5Nzg1Y2Y4NmJlZGY2MjI4ZTY2YmZmMGU5ZGUzNmI0NjE2ZSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTUyMmM0MmNkMTljNmQwM2Q4MDhjNDUyNzIwNTUyODI1YjdlYTFjNWI0OTFiMjE4NTExY2FjNzQxNWNmOWQ5YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNTFkNTFhZjJhNmNkMjUxOWMzOGJlNGU4ZGU3ZmFlZGViMzA2ZDVjNzk3NDFkYzIzYjJlYzNiYjg1ZWZhNmU5YiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiM2YzNzAwYWRjZGMwMjkyZGVlYjA1ZmUyNzEyN2Y3MWYwNjg4ZWM2MGE3YjNlZDA2Y2M5YWIwNGFjODAzOTE3ZSJ9" } ] @@ -835,7 +827,7 @@ "version": "repo-lines-v1:sha256:b772ff566d725c614b17c462fbafd616cb85797985ab735eadeba461c0f3a6d3:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU2ZjMxZTAyNWIyZDQ2MGNjNGNjZGY5NTdkNjkzMGM4MTRiM2JlZGRlZGQ5MDVjMjNkOTg1MGIxMTNmNTFiMDciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA2NTdhYTY2MTFhOTk2ZTA3NjllNjNiN2VhYjJmMzIzYTEwZTA3YTAxMmViYjk1MTBjYTdhMTE4M2U1MzQ4ZDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAzNjFmYmU2YTJkNzg3MDAzYjc5NDliMDY4NTFjOGJlNDczNTI3MDNiZjAwYmZkYjM5YTVlZmEwZDAxYTdkNmQifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L981-L1003", + "resource": "repo://TFM/App/AppModel.swift#L989-L1011", "version": "repo-lines-v1:sha256:6038f914333a2ccdde12f3dfafb90749af1a05c69522a8f4c8d57f659c2f2725:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc4Y2YxMzBiYzQ5ZmEyZTk4MjVlMzhmNDdmYTJjN2UxNWIyZjFjMTYwMzNmMDM4MTc2YzkyNjM0YzMxMjRmNDEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI3ZDcwNjRjNDA1MGRmMjJiOWM0NWMzZDIyZDMwMGVjMTRkNDJlZjZlZjQ3ODMxZmJlMDJjODAzNDU3NzBkOTgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM0MjE0OWM0MGFmNDRiZjAxOWQ5MDBkYzkyOGIzNWEyMThmNTI0NWY0NjllYWY3M2IwZTAzMDgxYjc1MjgzYzMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE5OGY2OWM0ZGU2YmEyMjkzMGEwNGUzOWNmZmM5NTFkOTkzMDE1MTc5ZWM3MjM5MWMyMDI0MjNjNTE3MzY0MDcifQ" } ] @@ -997,7 +989,7 @@ "version": "repo-lines-v1:sha256:f6b4774b8e3b057a1fbf4a5a9d42c0ec0d0e46b26978865dc73f8da6f3eb45e6:eyJzZWxlY3RlZExpbmVDb3VudCI6MzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMxMTEwNTNlMDE4ZGZlYWJkZjA2ZWMxMzJhYTNmZDlhMGM0NzE1NGFmMzRmZjQ3YmNhZTJhYTMxNWU4M2U3NGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVlM2RkNmY4MTdlMDBlYTVlZTVhYWI3YmVmYTI2ZDdhYjE4M2E5YzMxZDkxYjA0ZjUxMThjMDI0Nzg2ZjQ0YWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc0ZTk5YzAwNDM3ZDI3MjcxMmM0MTc1MzFkOGIzZjU3MTk1NmJlOWFjMWRkMWUzMGU5ZTBiMmUwYmUyODBjNmYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQwNmI0YjQ4MTU4Zjk0YTE0ZTJjOGMxZDA0M2VmY2M4NmE0Mzk4ZGFmMGUwYTM3YjdlNTFiOTY3NDA3N2MwYzcifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L531-L583", + "resource": "repo://TFM/App/AppModel.swift#L539-L591", "version": "repo-lines-v1:sha256:4c5f42b72d013a0a586dbbbf4947dd11f67b54b6106162e3543b0be3e7419386:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdiMGNlNjE4MThmYTdhM2Q2NzdmN2UzNGFlZjZmOTM0N2NjNzEyMzZjMDMwNTg2ZDZmMmJlNjIxMjYyMDkxNDIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJmMGEzN2I0MTdhZGVhYzhjNDU1YWQzMTQ5MTVkYjcyZjMyYWVjOGViYzUzYTU3NmUxMzAyMGY1YzE1MmQxMmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmOTUwNjk2ZTUzYzAxNzdhNzAyYjA5YzcwNGY5MTkzY2E0Njk3YTQ4ODZjYzRhYTQwNGJlNjM3MTQ0ZjdjOWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFmNTg4N2RkMThlOGUyYjBkMTk0MDM5N2ZkNjg0ZWIxMjAxMTg2NDQ4MGViMmQ3ZTZkYTI3MmE4ODIyMjU2ZTAifQ" } ] @@ -1055,7 +1047,7 @@ "version": "repo-lines-v1:sha256:207071022e19ce631298e7cdd46cc7c1593b5a6311d04a9cb041e2208cc5a6a3:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM5NzhiMjlhZjVmZGJlZDkyNTE1MDc0NjcyOTE1NjA5ZTRmZDhmMDFhOWIxOTI0YzA4ZThkYzIwYThjMTNkZWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRlMTliMWQwZmRmYTQ0ZWE3MTQwOTA4ODhhOTQ3OGExYWRmMDczY2IwODNiNmE2NzU5ZjNhZWNiYjY1ODUwYTEifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2510-L2552", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2454-L2496", "version": "repo-lines-v1:sha256:25e2df3150eae845b0f4c99e6841fdeab995aedb938be247f340abf344927851:eyJzZWxlY3RlZExpbmVDb3VudCI6NDMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ2MjE2MWQxMWFjOWY5YmEwZmJhMzFkZWUzOTU2YTg3NGFhZTY0NmExMGNhMmQzMzRmMTFkMDc2MWViNzg0MWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ4NDZhZjY2Yjc2MzVjMDg1OThlZWY0NmJkZjRmNDMzNTkzN2RiZjIzOGY3ZTZjMWMyYTFhNmY5YzFkYmYxODIifQ" } ] @@ -1089,7 +1081,7 @@ "version": "repo-lines-v1:sha256:e59fc418228f44f3cdb122629313bf0c7c494d89238ffb7e6d0c1c9fba14244e:eyJzZWxlY3RlZExpbmVDb3VudCI6NTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcxMjIxN2Y4MzliNTcyYmE2ZWI3ODA3YTNkMDZkODBjMmFhNTA0NzU1MDUxZTlhMWQ3MTk5YjJlZmNiN2FhMmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ4Yzg2MDEyOTQ2ZGFlYmEyNDEzODViMDJiMDM5MWRkZDlhYmZkMzY4YzUxZmNkNjJlZGIxNzViODNlZGUzOGIifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L771-L783", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L750-L762", "version": "repo-lines-v1:sha256:17e9d8aa1863459eef413b879dd2014f08d0e0f5649819a4953d6bc7365f5320:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg1YjBmZDZiMzhmNWY5N2VkN2YzZmI5YzAxM2ViMTIwMTE2ZDQxYTk3NDE0ZGFhNzVjODNmNDA5NjQxMDA4Y2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNiYjUzMTU2YTNkYjA5MDdmY2I4NDEwNjJjNjc5NjdiMzMwY2I5NTk1ZjViNWEzM2I4ZWVmNTZjZTVlMzczZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM2MTRmNmMyNWJkOWNkZWVkYWMzMWY1ZGViMTk4ZjExNTc2MWJkNjY4ZjkwYjg3NzlhNjAwMTczMGU4YWViM2YifQ" }, { @@ -1195,7 +1187,7 @@ "version": "repo-lines-v1:sha256:71191fb557110c2064512fbddbb756348a97ae8f649a4b9e864ab89327904718:eyJzZWxlY3RlZExpbmVDb3VudCI6NDIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZlZGRkNjNmNjVhMTA5NTcyMWZmZjQ0MzdiOWU4M2NmYTgzMWY2MTAwN2ViNmUwYWRjMjBiNWM2NWIyNzkwMmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjllOTdkYTJmNzllMDQyMWExMjdlODhjOWZlNWRiMzNlMmYwOTE5YThjOWM0ODhkMDhlNzJkOGFjMzM2OTU4OWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA5Y2I2MzY3MTkxNGQ3OWUyODQxZGQ5MmEwNjdkMTUyNDFjYTFmOWIyN2M4YmQxNjAyZjdiMzA0N2E4ZDY1NDcifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L651-L696", + "resource": "repo://TFM/App/AppModel.swift#L659-L704", "version": "repo-lines-v1:sha256:7b085aabc036948059fba20f14dadc6a17be8111a681b774016b30b6e439bf49:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcxODAyNjQ5MjA5NTY5ZTNmNjMxNDc0MWY2YjFjNjFhNjJjZmM5ODY2MWE3N2VlYTEwM2UwZDIyODg5ZDA4ZjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQ4MDVmNWZiYjVmY2MzNWE5YmQwNzgwOGU2MWJjMmY3MjcxNDVlMzI2ODdkZDNhZGRhYmQxY2MxNzM1MjgyMzIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJjOWY5ZjFhZTI4ZDI2YTYzOWViZGU1ZTA1ZGQ2OGY5NzFmMjBjYmZmZDQ2ZWMxNzY5ZmJhOGQ4ZWU4NzVkNTkifQ" } ] @@ -1257,7 +1249,7 @@ "version": "repo-lines-v1:sha256:13a2da49fe0724999733015c7d408121aa0aa3a293e0dc7cf9e9b54f58f8c22c:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE3YTIxOTZiMzA1N2NkYWI3NDllZjYwYTgwMzE2NzJmYzljM2ZkMzRiOGY3MmFiZmVjYzMwOWUzY2FmYzg2YjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVlM2RkNmY4MTdlMDBlYTVlZTVhYWI3YmVmYTI2ZDdhYjE4M2E5YzMxZDkxYjA0ZjUxMThjMDI0Nzg2ZjQ0YWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImI0NTBiYTM0NzJiZjE1M2YzMmMwM2RhNmY0MTFmZTJjODU1NzEyNDAxZTRlNTJmNTQxOTM4MjEwNWM2Y2Y5N2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQwNmI0YjQ4MTU4Zjk0YTE0ZTJjOGMxZDA0M2VmY2M4NmE0Mzk4ZGFmMGUwYTM3YjdlNTFiOTY3NDA3N2MwYzcifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L528-L584", + "resource": "repo://TFM/App/AppModel.swift#L536-L592", "version": "repo-lines-v1:sha256:be215f4b526172ded0ef63eb45a6fed6f677089ce259e02dc556f4801d56ac3a:eyJzZWxlY3RlZExpbmVDb3VudCI6NTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFmZmVkYjY5ZWNkZjY5YTQ5ZjhkZWIwODBhODE2MjYxNGE1N2YzNmNmZDI2MWZlNmI4ZDgyNmQ2ZDJlMmQwNWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEzOWM0MDc4MjVjNmFhOGFkM2IxMTM0YTg1NDI4NWQzM2Y1ZDQ2NmYzOGRkM2Q1Y2NiZjA0NjVmMDg1OTNmZjEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImYwZDM3M2UwNjZmOTBkNTliYmUwYzkxMGRiMTBiMjgxMmM0MGI4NmYyMTRiZWExMmFiYjkyNzQxYjNiNTNhODYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQxNTFmZmRiYWUyM2RmYjViODEwMDliZmIzYTFkMjQ0ZjY5MmQ1M2JlMjcwNTBhZDMyYmU1MWZiNGZiOTcxMjQifQ" } ] @@ -1274,7 +1266,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/concepts/commerce-localization-and-product-surfaces.json b/openwiki/.claims/concepts/commerce-localization-and-product-surfaces.json index ba37d317..31307611 100644 --- a/openwiki/.claims/concepts/commerce-localization-and-product-surfaces.json +++ b/openwiki/.claims/concepts/commerce-localization-and-product-surfaces.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:bfcc767924aef1756c700892275c7dd731393511422da859052a8d6bd27080e0", + "pageVersion": "sha256:18191558f929a45782672980dc9d51c3ef65638d80380ec8454be69e54ebf44d", "claims": [ { "id": "claim_70c5c3da992944188e719c65adc9b06c", @@ -101,7 +101,7 @@ "version": "repo-lines-v1:sha256:ab75083d626a99ced9a6cce1632a7e1fb6bbdca7e3e7538ba68a496802a3f11b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEzN2NjMWI4M2RjYjRhN2Y4M2MxYmEwN2EzNDIzODdjMGY2YTM0ZmM4NjI4ZTJiOGY4ZDkyMTNmNDA1NWZiNDUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk1YTllYTQ1NjQyYjQyODBkMDZkODI0ZmNmNWVmM2RhNjI0NzBhNTNiOTMzZTc3ZDhhZWE5YmM4Mzc3YTk4NWYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjU2ZDAyYTIyYjVhZDhlN2QyY2RjMzg3NTExY2M5YzM0Y2NjNzk3MjFkYjg5YmE5NmY2YmM2YTE0Y2U3NzVhYjEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAyOTQ3OTNiNTczNzY3MDgyM2Y1NGM4YTAxZDliYzc1NTg0MWIxMDM1OGI1Y2Q5MzBlMTY1YmZlZmI2MGRkOWMifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L225-L288", + "resource": "repo://TFM/App/AppModel.swift#L233-L296", "version": "repo-lines-v1:sha256:5d4ff1b17d7716da5e5caff569d9434b84c417f6c4bda8f23eb97a26b4e12efd:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc4ZTY5ZGUwMjZiYTc2OWJjYTg5M2ZjNDQ0YmE5YmFjODU0ZTRmZDhiNDU0Mzk1MDhmYzkyNzdlMDE5Y2UwZjMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZiNTc5ZWI3Y2Q4NWU0ZDUzYmMzODlhNzE0M2FjYThlYTRlNjE5NWYwZDA1NWMxNTUzMzkwYTk4NTlkNzJmMTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJmNjFkMzNiNGZmMTEzN2Y0YjIyM2VlZjQ3NjdlM2FlNDU2M2EwOTUzN2FhY2Q4MWNlZGYyYmFlNDRhYWI4Y2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEwYTU5ZDYwOTFmY2YyNjZhMjhmZjdiYmY4ZTcwODJlNWRlYmJiODMwZGY2ZWYwZWY3OWQ2OTIxNmZmNTIzZjUifQ" } ] @@ -148,19 +148,19 @@ }, { "id": "claim_8cb59d2af4c74383a37b91861cd0ec90", - "statement": "App-facing text is defined as semantic `LocalizedStringResource` entries under `Frameworks/TermindPresentation/Strings` and localized in the shared `Frameworks/Resources/Localizable.xcstrings` app catalog; `InfoPlist.xcstrings` owns bundle metadata. New or changed English app text needs finished `zh-Hans`, `zh-Hant` and `ja` translations; the default CJK check validates both app catalogs' normalization, JSON, Chinese/Japanese completion and spacing, not AgentKit or website copy.", + "statement": "App-facing resources belong to shared semantic AppStrings definitions and Localizable.xcstrings, with InfoPlist.xcstrings for metadata. The available Python check-cjk-localization.py validates both catalogs by default for normalization, JSON, finished zh-Hans/zh-Hant/ja units and spacing; guidelines referring to check-zh-localization.sh and jq are stale, and CI runs the Python checker and its unit tests instead.", "evidence": [ { - "resource": "repo://AGENTS.md#L80-L101", - "version": "repo-lines-v1:sha256:6c9efbc83da4073dc5b3a4125fcc4c35e2e3b8fac8d8f5ab0fdad06c6877a8ea:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlYjZlZDYzN2JlNjgzYzc2MTJmNjc3ZWE5ZTFhNTZiOTY1OTllNzcyZjZkNzY5MGI0MjM3NDBjZDE0YWZmZjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg0NTE4ZWQ0MjVmYWVjYTU4MTRhMWMxMDE1NGYxMzI4MThlYjNlYzBiNmU4Mzk4ZGY0OTRiOGFkZjdjM2EyNDYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk4NmYwZmMyNmM4NWViY2IwODgwNDgxN2JiMGQzZGJkN2EwOWE2MWE1N2RhOWEwNTk3YWFjOWU3NWQwZDc0OTIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBlZGM0MzMyOThhZDU4MTI5ZWUwYTg4NTJjMTNmNTI5MjBkYWFmOTRhOGM2NGE0ODcxZWVlZjQ2ZTEzMTVlZDIifQ" + "resource": "repo://.github/workflows/localization.yml#L31-L36", + "version": "repo-lines-v1:sha256:3fe4524442887eee373d7233a44f71dd40c9e4e1e7fd1c03e2066dd266cd5ebe:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMjQ0ZTU5NjA2MjI1OTY3ZjY4YjM3ZGRiNjE4MjBiYmZhNjA0YWFiMzFiZDIzZTEyNjJlMGQ4ZjBmZDlhNDNkMiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYTMxYjE5MTc0OTM4ZTE3MzlkMzg3ZjY4MTE4ZGUyN2NlZWZlMzU1OTY4NDU3YmNkODRkYjhhZDI3OWYzZmJjOSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTAyNjA0NWMxNzkzOTAzMDU1YjNhZDVjZGFiNGE0ZjVmMDQ3OWExNjBlNzc1Y2JhZmY2ODlhNDc1YjVlZGY1NCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjAsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSJ9" + }, + { + "resource": "repo://AGENTS.md#L94-L101", + "version": "repo-lines-v1:sha256:a471105b6c549db962e46e02d0594514f0eb557cd92caba51b838ba18268dcb4:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMWRmMzY1YzcyMzNlMGQ1YmY3NThiYWM1ZTkwZGE3OGI3MTZiMjMyOTU4ZTQzZTg5MWIxMTQwOGQ3Y2FmMTZmMyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiODQ1MThlZDQyNWZhZWNhNTgxNGExYzEwMTU0ZjEzMjgxOGViM2VjMGI2ZTgzOThkZjQ5NGI4YWRmN2MzYTI0NiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTNhMDA1MzIwMjZmOTMwMjMxYjdhNDVmMWI5MWZhNGJiZWU0MDhjYWJkZDZjZjg1YWJhZmRlZTM2YTc2ODQwMCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMGVkYzQzMzI5OGFkNTgxMjllZTBhODg1MmMxM2Y1MjkyMGRhYWY5NGE4YzY0YTQ4NzFlZWVmNDZlMTMxNWVkMiJ9" }, { "resource": "repo://Scripts/check-cjk-localization.py", "version": "repo-file-v1:sha256:179c42fdaca9bf8b59b7bf3f1bf935848d54fa04d91f8e42912d876c4ed99925" - }, - { - "resource": "repo://Scripts/normalize-string-catalogs.py#L20-L24", - "version": "repo-lines-v1:sha256:0ecd8eb2e41daaf37fd998d466afae44f0b4e327f8cab389cb8e91136d3cd3ba:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMzNjZjQ1NGY0ZmM0NmE2NmQwN2UwNjcyNmVmOWZmMjMyYWFiMWM3YmIyZWQzMTYzODZhZTFjMzE4MmMwNGM2MCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZmFmYmEwNTI3YzM5NTNjNGI2ZTBlNWI1NzM5YmE4MzYxMmY4MDUzYmNkYzk0NjY3MzAxOTNjZDFkNTkwYjQwOCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYzUxZmY0MWNkZDFkMjBhODRiMmUzM2JlMmUzNGM3OWFlZDE5OTgwNTUyZjYyNTdjZGJmZDgzNmNjYjY1ZTk0MiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYmIzMDYxOWQ0OTM1N2JhYzkwNDNjZGFhZWExM2NmZDI5NjZkMjg5M2Y4OTc5ZjY5YmFiZDhjYmMxZWYxMjkxMyJ9" } ] }, @@ -253,7 +253,7 @@ "version": "repo-lines-v1:sha256:f57959ca5e073da37ef4373d22333019ffe35e98b4c3e70052d18e7fa74823af:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ0Mjc1Y2MzNDlhNDVkMmRjMWZkOGRkNjc5MWE3MzUxNWZlODM1YjdiNDU4OWJjZmY0ZGJlODIyN2MzMWRmYzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk5NzdmMWRlZTdiYjUwMTIyNDA1MjJjZDc3ZDg1MjFkZDZjOGJiMDJiNDdjMGJiNmIwYmY0NDY5MmQxM2RlYzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBjYmFlZDgzNzA5OTVjOWViYjI4NzRjYTNiM2Y1OGQxNzQ3Nzc1M2E0ZDI0M2Y5MmNmYTcwYjlhYzg2N2ExYWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ0YTM1MTM3YzUwNzYyMjA5MmY1OTAzYWUxMTE4YTY2NTc1YzIyMmU3ZWQxMzc2YmM2Mzc1YzhiMTFjZGNlMDQifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L477-L495", + "resource": "repo://TFM/App/AppModel.swift#L485-L503", "version": "repo-lines-v1:sha256:91c18124ad9802d1c41a2fe94ca53c38e913dcf1544e67dc55f82405660c4486:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiYTVlYmQ0MmUzYTYwMzMzYzc0YjRlNjMxYWIyMTYzYmNlMGEwNTQ2Mzk5NzNiY2UwOTdjMGQ1M2UyODgxNDQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU4NTUwNThkODhkZWZkMWYxYzM1MDQ2OTE1ZjQzYzc3MmUyYTVjMWI2NTRmYThhYmIwNmE1NjA5YjM3OGViMWMifQ" }, { @@ -271,8 +271,8 @@ "version": "repo-file-v1:sha256:179c42fdaca9bf8b59b7bf3f1bf935848d54fa04d91f8e42912d876c4ed99925" }, { - "resource": "repo://TFMTests/AppLanguageTests.swift#L8-L172", - "version": "repo-lines-v1:sha256:d44ca229fd4201504f2ca4a52ed1714655ec670dfdd2c61ec169fae3d38c7a49:eyJzZWxlY3RlZExpbmVDb3VudCI6MTY1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI2MzQ0ZjM1YzZiZTlmZDliODNiYjg5ZTE0ZWRlZmJhZTkyNDQ3MDRhYmQ1ZGJhNjM1MWIzZTEwYWEwOGE5OGRjIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIyODE5YjdmOWM0YzQwZGI0MmRhNWE2YjBhMjdkYjM1MTI4NzE1NTgyNzgyYTVjOTdhODBmNTY5YjRmMGQzZjdkIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJiOGI2YjNlZGMzOTU4NTkyMGIwNjI3MDc2YTM3YWVkYzU1OTYwYzVkYTczNmNjMGFiYjk4M2FmY2M4OTBkZjcwIn0" + "resource": "repo://TFMTests/AppLanguageTests.swift#L8-L185", + "version": "repo-lines-v1:sha256:3da21fc3f0ee893ec46a86dc888e0cbcacd7edeb99e3f6847a54e75f2137eae7:eyJzZWxlY3RlZExpbmVDb3VudCI6MTc4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI2MzQ0ZjM1YzZiZTlmZDliODNiYjg5ZTE0ZWRlZmJhZTkyNDQ3MDRhYmQ1ZGJhNjM1MWIzZTEwYWEwOGE5OGRjIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIyODE5YjdmOWM0YzQwZGI0MmRhNWE2YjBhMjdkYjM1MTI4NzE1NTgyNzgyYTVjOTdhODBmNTY5YjRmMGQzZjdkIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJiOGI2YjNlZGMzOTU4NTkyMGIwNjI3MDc2YTM3YWVkYzU1OTYwYzVkYTczNmNjMGFiYjk4M2FmY2M4OTBkZjcwIn0" } ] }, @@ -296,7 +296,7 @@ "evidence": [ { "resource": "repo://Frameworks/Resources/Localizable.xcstrings", - "version": "repo-file-v1:sha256:7a75d3a7750b4cb0cbe469c5b9339562aad54f8aeaecf676c6862e9ed51388b5" + "version": "repo-file-v1:sha256:568a304613f20338e661657b0616fcd3d76defeadc112844615eed4e2accfa96" }, { "resource": "repo://TFM/Features/KnownHosts/KnownHostsPageView.swift", @@ -362,25 +362,21 @@ }, { "resource": "repo://TFMTests/AppLanguageTests.swift", - "version": "repo-file-v1:sha256:644bfcc202f4846774efc6e7747ebf2b66d116e00574ba71ad529716392ed224" + "version": "repo-file-v1:sha256:adad98919a8d3c113abaa52b9cfd824370296bf2669c60bee5af4dc74869be1d" } ] }, { "id": "claim_6956578bf21c46cead580bfc216f992d", - "statement": "AppStrings groups semantic LocalizedStringResource definitions in TermindPresentation/Strings for TFM and TFI, including single-platform text; platform-specific wording uses explicit variants and protocol values, identifiers and diagnostic codes stay with their existing owners.", + "statement": "AppStrings groups shared semantic resources created on demand for catalogs compiled into each app's main bundle, including platform-specific variants; identifiers and diagnostic codes stay with their owners. Repository guidance keeps STRING_CATALOG_GENERATE_SYMBOLS = NO to avoid competing generated symbols.", "evidence": [ { - "resource": "repo://AGENTS.md#L80-L88", - "version": "repo-lines-v1:sha256:f9d9dcb4c54f6c1967a03f3556e21c1f8f981df863a869f0877914c6d88103b6:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiN2ViNmVkNjM3YmU2ODNjNzYxMmY2NzdlYTllMWE1NmI5NjU5OWU3NzJmNmQ3NjkwYjQyMzc0MGNkMTRhZmZmNyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiODM0NTZkMjYyMWEzM2E1YmZhNmJjNTAzYWY5YTI3YzdkMmNmMTU1YWUxMWUyOWFiZDliYzIyMTUzZWJiZDY0MiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOTg2ZjBmYzI2Yzg1ZWJjYjA4ODA0ODE3YmIwZDNkYmQ3YTA5YTYxYTU3ZGE5YTA1OTdhYWM5ZTc1ZDBkNzQ5MiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZThlOWYzMDg0MzUyNWNjN2Y0N2FjOGRjZTQ5MmQ1ZDE1OWM3ZTMzYmVjMzgwYWM0MTAxMDJjMmUxODcwMWY5NCJ9" + "resource": "repo://AGENTS.md#L88-L99", + "version": "repo-lines-v1:sha256:718f8c9ace4bcee47c0681b1ed6ac4fd39557683a21027bc7a336e45a8646552:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgzNDU2ZDI2MjFhMzNhNWJmYTZiYzUwM2FmOWEyN2M3ZDJjZjE1NWFlMTFlMjlhYmQ5YmMyMjE1M2ViYmQ2NDIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZmMmRiNzZiNzg4NGU4MTYyN2I0NTI4NmY5MTllMzE5NDM5Yzg0NTAxYWI4YjNlMTEwMjU2MGVjYjBlNjk5NDQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA3NzRjZjhiMmJlYWNkMGZmNjhhN2VjOThmNzhjOWVlMzgwMzUzNWI0OWUwYTJlNGJlNDI5M2JkMTViNzhmZWEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjIxNzFlZjc3NTVhMjEzZDc5MGUwNTk1NmRjYTk0YmYxOTc5OGJmMDRmYTkzYThhNzIxNDk4MjU3MzA3YjNlMGEifQ" }, { - "resource": "repo://Frameworks/TermindPresentation/Strings/AppStrings.swift#L1-L6", - "version": "repo-lines-v1:sha256:fc75544a0cec27d904ec80a42dc9c6dde7922c9e73b2e1d3d66401e16002c5e1:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZGRhOWI3NWY2NGIxMDZlYjU0NGRlMjAxZjU5OTEyMmQ3NWFiYzQ4M2M1YjE1NmJiYTZjMDVjMGY5NTZkYmMzYyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOTQzOTNjNmIxNDE5MzcxNzhiY2QzOGZmOGMzYzllMDIwYzJhN2MwN2ExOTk1M2QyZmQ5NTQyZDBkNjFhNDczZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjAsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjAsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSJ9" - }, - { - "resource": "repo://Frameworks/TermindPresentation/Strings/Navigation.swift#L1-L63", - "version": "repo-lines-v1:sha256:c6eb012801ab67fda0850b258a3f9a30d3906c7c4f88a31ed4f70613c234a0ce:eyJzZWxlY3RlZExpbmVDb3VudCI6NjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRkYTliNzVmNjRiMTA2ZWI1NDRkZTIwMWY1OTkxMjJkNzVhYmM0ODNjNWIxNTZiYmE2YzA1YzBmOTU2ZGJjM2MiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5Yjk0ZTg3YTJjYjg2YTViOTU5OGFiZmIxZDk1MDA5YjQyOWI3Y2ZlNzIzYTU4MTQ2ZDU0NDlkNzcxNTA2NDIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI0YTM2NWE5MGVjMTFlOTA0MzI3NTJjZjkzNzZlYzVlZDM2YjgwYmE1ZThlNjg1NGZjZjk4OTcwZDMwNDQ0NjkifQ" + "resource": "repo://Frameworks/TermindPresentation/Strings/AppStrings.swift#L3-L6", + "version": "repo-lines-v1:sha256:abea37d3b0a5546014b7dc354d0d3fc99b0bd7755f5df6a1e22b5b721ea56bb0:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYWJjYTdhZDA2Nzc0MzIxZDE5Njg5ODY2ZDUyNzYyMzc1ZDUwNzM4OTQ1MDc1MDgwYWU5OTdjNWIyZmJlODZmZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOTQzOTNjNmIxNDE5MzcxNzhiY2QzOGZmOGMzYzllMDIwYzJhN2MwN2ExOTk1M2QyZmQ5NTQyZDBkNjFhNDczZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjIsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMTE4YmZjYzA1ZGE1ZWQ5YjU1ZGM1NzI1YTRmY2RhZWM1YzI1NzVmZGY0Y2UyMmExOTkwOWIxYjNmNGRlODFmZCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjAsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSJ9" } ] }, @@ -430,49 +426,29 @@ }, { "id": "claim_9e5dcbfb0e4c4a6ebb3d30af40da4805", - "statement": "Both shared app string catalogs are manually managed: normalization sets every extraction state to manual and canonical Xcode formatting without deleting stale keys or altering translations, while the CJK check verifies normalized catalogs and completed Simplified, Traditional and Japanese units; stale extraction from one target is not grounds to delete a shared key.", + "statement": "Shared catalogs are manual, and normalization retains stale keys and translations. The CJK checker exempts shouldTranslate:false entries, otherwise requires finished units, preserves format argument positions/types/multiplicity including literal percent, uses English localized source values when present, and requires other-only CJK plural variations for English plurals. Japanese mixed-script spacing and Chinese spacing/punctuation checks have a narrow Type %@ subtitle exception.", "evidence": [ { - "resource": "repo://AGENTS.md#L80-L101", - "version": "repo-lines-v1:sha256:6c9efbc83da4073dc5b3a4125fcc4c35e2e3b8fac8d8f5ab0fdad06c6877a8ea:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlYjZlZDYzN2JlNjgzYzc2MTJmNjc3ZWE5ZTFhNTZiOTY1OTllNzcyZjZkNzY5MGI0MjM3NDBjZDE0YWZmZjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg0NTE4ZWQ0MjVmYWVjYTU4MTRhMWMxMDE1NGYxMzI4MThlYjNlYzBiNmU4Mzk4ZGY0OTRiOGFkZjdjM2EyNDYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk4NmYwZmMyNmM4NWViY2IwODgwNDgxN2JiMGQzZGJkN2EwOWE2MWE1N2RhOWEwNTk3YWFjOWU3NWQwZDc0OTIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBlZGM0MzMyOThhZDU4MTI5ZWUwYTg4NTJjMTNmNTI5MjBkYWFmOTRhOGM2NGE0ODcxZWVlZjQ2ZTEzMTVlZDIifQ" + "resource": "repo://Scripts/Tests/test_cjk_localization.py", + "version": "repo-file-v1:sha256:fa5c3b4e2884f9a6f166010dbbedd3b8d7013a893107a061d8d652724ac25963" }, { - "resource": "repo://Scripts/check-cjk-localization.py", - "version": "repo-file-v1:sha256:179c42fdaca9bf8b59b7bf3f1bf935848d54fa04d91f8e42912d876c4ed99925" - }, - { - "resource": "repo://Scripts/normalize-string-catalogs.py#L1-L6", - "version": "repo-lines-v1:sha256:a4f7e4e8cd45a6a2e257248bc9cef3775d26f29a5f556dc4f80c9f52b74cddea:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDY4MmNiZGI0YzhiMDc1MThiZjQ4NmM1ODk5MGZjMzkxNzgzYWEyMGE5MTZiZmIyN2FkMjExZWIxZDVjMzY0MiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOWVlYjY0YTVlNDE3MWJjNjIxOGQxYzVhMjRjOTYwYWY3YzUyOGYyYjBmMzU3NWFjNmFjN2VlYzE5M2ViNzM2MyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjAsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMWFhZjc2MzdjYjZlN2U1ZGI4NWQ2MzQyNzQ2MDE0ZGYwNDFjY2Q1NThhOTIwYjI5ZWI2YWM3MTlkN2JhZjVjMiJ9" - }, - { - "resource": "repo://Scripts/normalize-string-catalogs.py#L75-L80", - "version": "repo-lines-v1:sha256:6c71ea7347ff9717c89060d589415e0ed157060322ff0439a96fa684359794ff:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDk3MzhkYzIzYzYwZjQ0NmMxNGQ3MTNmOWRiODZkNWNiNWMyOTcyOTI3YjQ1MWYyZjI3OWZjYWM4ZWMzZDM5OSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTc0MTRjYzM0NWNjNmQ5MTY1YTdhOTk3ZWUzZGRkMjU1ZTExNjMxZjRkZWI0OWE2ZTI2MzNlNGRiNjljNTFhYiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTZiNDZlMTg5ZWQyZWEzODEzZWI3OTY0ZjcwYjNkMTBjZDUxZTFhMGIyZGNkYWNhODk4N2QxNGNmOTA3NzYxMyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMzYxYjQ1OTI5ZjJiYjIzYjY4NWM1NDdmNjdkOGI2ZTY1NGU2NDQ5YTYzOWQyZDc2ZjVmN2VmY2ZhMWVlMGNjOSJ9" - }, - { - "resource": "repo://Scripts/normalize-string-catalogs.py#L97-L127", - "version": "repo-lines-v1:sha256:26d3ca462ef0501fa83b81598f8af234c64c8303b8c6e420735fdd32caaaceb8:eyJzZWxlY3RlZExpbmVDb3VudCI6MzEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFiMzk3OTMzYTI0YTgwMWVlOTIzMzI3YmQ2OWEwMTA0OWMwNzc5MTUwMjFjZjJlYzM5ZGRhNGY4MWFhODM3ZTQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJhY2QwNjFjNGE0OWIwMjdkZTY0MjUyYTZhNTZlM2ZiNThjNTIxYjNlMzUwOGRhZGFlN2FkYzFkNDA2ZDI4ZTkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjcxMTc5YzVmOTVhMDJlMTg2NTczYjkzNDhhYTYzOTZkNjZiNDUzNjgwZWEzODNjM2M1ZjUxMGFiYThhNGZlZWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjkyZTI1MTEwNDUwNTM2Njc5NDMzOWI2ZTRhNDZkZGY1MDBlNzNlNWJmZjBhMjQzYjU5ZmQzNjI0OGE0YWI0YjEifQ" + "resource": "repo://Scripts/check-cjk-localization.py#L24-L96", + "version": "repo-lines-v1:sha256:0ea2fa1e2f8da7c1d909a25d096277da289a9db1bd7778f61c213c73a7e6cbc8:eyJzZWxlY3RlZExpbmVDb3VudCI6NzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ5MWU4MjQzZGFmMDFhYjA0NDZhOWNjMTkxZjY1Y2I5ZmM0NTU4ZGFlZWU1NDAxMzA2ZTBlYWQxNWM0MjI3ZDEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJhOWMwN2JiN2NlOGE4ODNmYjIyNTUxMzBkMThiZWIxMWY3NjVhNDA4Mzg5Zjc5ZjM5ZWJlOTBiOThhMjUxNGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkzZTA0ZjAwODcyNjAxMDYwNDg5MTk4MmFkZjdhMjc3ZDA0NDQwNTk5ZTg0MDUxNjgxY2M2NjUyMThmYzdjMTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijk0YjEwNjc5N2MzMTM3NDc4OTAyZWUyNDQyM2Y2Y2FlMTNkN2Q2YjUxYjBiZDQ0ODg3YmRkZDlkZTgyOThiY2EifQ" } ] }, { "id": "claim_6656160cabd74b2c9387b5713a3afeba", - "statement": "`AppLanguage` writes canonical explicit-language tags to `AppleLanguages`, removes the override for Follow System, migrates older Simplified Chinese preferences and treats unsupported stored tags as system language; catalog lookup selects the effective app-bundle localization including regional Traditional Chinese.", + "statement": "AppLanguage persists canonical explicit tags in AppleLanguages and removes that override for Follow System. Only legacy Chinese preferences resolved as Hans are migrated; unsupported values including regional Traditional Chinese preferences follow system without rewriting the stored value. AppLocalization selects main-bundle catalogs by Chinese script or English/Japanese language code with main-bundle fallback; the Traditional Chinese picker label becomes 正體中文 only for Taiwan region with flag display available.", "evidence": [ { - "resource": "repo://Frameworks/TermindApplication/AppLanguage.swift#L3-L38", - "version": "repo-lines-v1:sha256:81e3b31c2c44cd90163a0839f912c5e077d97a6f77200f8256be6b1ef55a1c88:eyJzZWxlY3RlZExpbmVDb3VudCI6MzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMwZTE4NTRiZjA4NjFkOWY2MDlmMzQ3YTE0NzE1ZmM1ZjE4NTRjYTI2NjBhYzhhYzY1ZDk1YTFjOWE1ZGFhY2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjoyLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjExOGJmY2MwNWRhNWVkOWI1NWRjNTcyNWE0ZmNkYWVjNWMyNTc1ZmRmNGNlMjJhMTk5MDliMWIzZjRkZTgxZmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUyM2ZjMDUxYmQzNDMwODljOTYyMzJkMjBlMzlhZjJjZmU4MDQ2ODQ1NmQxN2M5NzEyMzhiOTNmMTRkZTU4MjEifQ" + "resource": "repo://Frameworks/TermindApplication/AppLanguage.swift", + "version": "repo-file-v1:sha256:640c5145eee8e4d817a738eb3e5a1788bceac19d49e3bb081bccb66e1914e2e5" }, { - "resource": "repo://Frameworks/TermindApplication/AppLanguage.swift#L41-L104", - "version": "repo-lines-v1:sha256:fc716696c184910b180d1e1170c1830d8342fb389768a3d890257263bc4742b5:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVmZmE4ODM4NGY1ZGJhZjgwNWMxYmQ1OGZmZjBiOTVhOGY3ODBmOWE1MjAwYjUyYjk5YjlkYjIxNjcyYTAzZTQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" - }, - { - "resource": "repo://TFMTests/AppLanguageTests.swift#L9-L105", - "version": "repo-lines-v1:sha256:a61731279e0a83895233af6115ef1e3ea29fd1bd573e60cf34363b00b3755da3:eyJzZWxlY3RlZExpbmVDb3VudCI6OTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQzMTIzODBkOGU0ZTNiYjlhNzg4OWEwZGMxNmVhMDNkODJkYjU1Zjk1NDg5ZmFhMWI3NTNlMzMwODllZjRlZDUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc1NTJjOTcxMTBhMDM1ZjNkYjQ2MWIzM2VjZmQ1MzAwNzk5ODMxZWJlYjdlMmEzOThkMzJiY2ZmNTJlYjFlNWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijk0ZTdkMmVlYzM0ZjNiMzZmMmI1NDhjNTJiNzhiYzBhZjJlNmNhMThlNjc5NmY5NTMyODc1MjgzYTcwM2Q5NjcifQ" - }, - { - "resource": "repo://TFMTests/AppLanguageTests.swift#L118-L129", - "version": "repo-lines-v1:sha256:d899741cbafb6d9990e1c82b6684e6f98373d57606845bee1fe3edb3c888e415:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAzNTFjNDM0MmE4MjllMmYzYThkYjZjZjhlNzA5MjcyY2I3ZGNlNjdiNTVjNTA3NGE3MzJlNzNjMDUxYTc4MjMifQ" + "resource": "repo://TFMTests/AppLanguageTests.swift#L46-L142", + "version": "repo-lines-v1:sha256:ac355cee0c3bff2d1cc3a88fad871a829857f628a599f3eb76115dec48e450ee:eyJzZWxlY3RlZExpbmVDb3VudCI6OTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkYWFkMjJhMDQ1NGJkYWExNTg0NjE2OGU2MzNkNDRhODYxZGQ4ODE2NDdiOWVjOGQ0ZmI1MTc1NGQ2MzZiYmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAwOGRiMjk3NzVjZjc0YjU3NDQyMjdlM2Q2NzE2ZTM1NDcwY2Q5Mjg1ZWYzNzE0YjFkZjg4N2Y2M2EwOWE4MDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAzNTFjNDM0MmE4MjllMmYzYThkYjZjZjhlNzA5MjcyY2I3ZGNlNjdiNTVjNTA3NGE3MzJlNzNjMDUxYTc4MjMifQ" } ] }, @@ -485,7 +461,7 @@ "version": "repo-lines-v1:sha256:142bb4d3cbadc0a1286fd3e80311a0a3dd5f851221caa76b3b6b02279e921edb:eyJzZWxlY3RlZExpbmVDb3VudCI6ODUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhYmE1M2E3ZjZiZmE0NDY3MWYyN2ZjNjE1MDE2MDBjNGY1YTc3ODk4ZWJjZGEyMGU0NDFhOWJiN2ZhODg5NmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjExMGVhYzUyYWE2NjM0MDczNjQ0MjU1MTg2YTc5NGIxMjdmYzQ5MTA2NTRmYTNhMjBiOTgzMjMxOTIxZWU2ZWEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ1MGZiNmUwZThhYmY3YmNiNjdkODIzMzJjMmVlOGZlNzY0NzYzZDFlZjA5ODc4NWM3ZmM4Y2Q1Zjk2ZmRkOGUifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L477-L495", + "resource": "repo://TFM/App/AppModel.swift#L485-L503", "version": "repo-lines-v1:sha256:91c18124ad9802d1c41a2fe94ca53c38e913dcf1544e67dc55f82405660c4486:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiYTVlYmQ0MmUzYTYwMzMzYzc0YjRlNjMxYWIyMTYzYmNlMGEwNTQ2Mzk5NzNiY2UwOTdjMGQ1M2UyODgxNDQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU4NTUwNThkODhkZWZkMWYxYzM1MDQ2OTE1ZjQzYzc3MmUyYTVjMWI2NTRmYThhYmIwNmE1NjA5YjM3OGViMWMifQ" } ] @@ -536,7 +512,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, @@ -640,21 +616,29 @@ }, { "id": "claim_11df68d8a0f3451d916866174b64eb64", - "statement": "AppLanguage supports Japanese with a canonical ja preference; AppLocalization maps ja, ja-JP and ja_JP to the app-bundle Japanese catalog, with tests for resource resolution, reordered interpolation and other-only plurals.", + "statement": "AppLanguage supports canonical ja and AppLocalization resolves ja/ja-JP/ja_JP Japanese resources, tested with reordered interpolation and plurals. Desktop captures its language at app initialization and injects that locale into scenes; Settings persists a new selection without automatically relaunching and renders the relaunch notice explicitly in the selected language when different from the Settings view's captured launchLanguage.", "evidence": [ { - "resource": "repo://Frameworks/TermindApplication/AppLanguage.swift", - "version": "repo-file-v1:sha256:640c5145eee8e4d817a738eb3e5a1788bceac19d49e3bb081bccb66e1914e2e5" + "resource": "repo://TFM/App/TermindApp.swift#L248-L270", + "version": "repo-lines-v1:sha256:d38c3c56422f3d1b2aef93e708eb9c910139f22b4961af22223831b61d678d3d:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ3ZDU1NWFjYjdkNGNjMTNlZmI0YWI0ZmFmNmM0NDg2M2JjOTRlN2RmZDQ3NTU0YzNiYzFkMzVjZWYzNzUwNzMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM2YjAxYWYxZDk4YWNmMTg2ZjZkNjc5YjNmODk1OWM2Nzk4ZmU3Njg1OGJhNGJlMmFlNzhkOGI1Mzg1ZGI4ZjEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImRkOThiZjk0NDY4NWU0NTA1MzRiOGUyOTJiM2YwMDkzMjMyOTg4OWVlNzE0Y2MyODU2YWNiOTQ4NTc4N2RlM2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc3MWI4MDVkYjZhNzkzMmNiMTQ2YjBkMTk4ZGQ3MWUyZDIxNmIyZTkyMmNkMTFhYTEzYTVjMzJhZGZjN2Y3NGIifQ" }, { - "resource": "repo://TFMTests/AppLanguageTests.swift", - "version": "repo-file-v1:sha256:644bfcc202f4846774efc6e7747ebf2b66d116e00574ba71ad529716392ed224" + "resource": "repo://TFM/App/TermindApp.swift#L40-L52", + "version": "repo-lines-v1:sha256:cd20324012889da733a80267838543a25b74c58df39f9e0f3214244e0f13c04b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFlMjc4ZDAxODg0M2RhZWJkZGY5YjdlNWI1NzJiNzJhN2EyMmRjMDY5ZWU4OTU3M2FkYjg0NTdjM2E5Mzc0MGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdjZDE3ODg3ZDhiOWFjYjJlZjc4ZGRkZmJiNTJkMWEzM2UwZWI2MDRlMmU1NDczMjI5OGZhNmUyMjgyMjc3N2YiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNlNTU5YzRmYzM3Y2QzNDk4NjFiYzgyNGYzNGIxMTE3M2MwNGFlZDQzMzZlMzU0OWE5MDJkY2VlZDQ2NTc1MTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA3OWQ5YTZjMjc5YzZiNjliODNlNThmZmE5YzcyM2ExZmM5Y2QzMTJkMGRiYjIwYjVmMmViOWE0NDkzNDVmMDAifQ" + }, + { + "resource": "repo://TFM/Features/Settings/SettingsPageView.swift#L403-L456", + "version": "repo-lines-v1:sha256:d3a6e7a4fb7c1aba55553455699157614377c287af24337766a477d64b412aaf:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUzNzA0OGVmY2I2OTFkODE0YmUxYzY0M2Q0ZjBlYmE5ODcwNThkMmQ0YTExMGM2NjQzZDczNDMzOTRiYzFjNmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmYmRiMWRjZGJkNTk4NGQxZjNjNTEwZDBjZjlhOWJjZDQxMjQ3ODg5M2JiNDAxODkwZTcxYWM0OWJkYTk3ZGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMyMzQ1MDExMjEyZjIyMmJmZTAyNjQwMGNiNjM4NjcwMjcwNjY3M2RhOTViMGNkZGJjMDdiY2QwNjI2MDgzOWEifQ" + }, + { + "resource": "repo://TFMTests/AppLanguageTests.swift#L94-L118", + "version": "repo-lines-v1:sha256:2ed2a9b7c282f50ecb431e7d87317fe08d4b7d7c28d59ce220c4837fd905506c:eyJzZWxlY3RlZExpbmVDb3VudCI6MjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI1NjlmMTMyNDk2MmJkOWU1ZGYxNjRiZjdiZDA4Zjg4YmRhNzBkYTE1ZDFhODg2OGM2ODJjNDUyOTQyYjA0M2YiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg2ODI1MzgwYTc5YzlhMTUxOTJiNmQzMGVjNTkyZTQ5ZWQyMTZhOWQ3ODI3YTQ0NjZjYTAyNWNhMTI0MTkyNDEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijk0ZTdkMmVlYzM0ZjNiMzZmMmI1NDhjNTJiNzhiYzBhZjJlNmNhMThlNjc5NmY5NTMyODc1MjgzYTcwM2Q5NjcifQ" } ] } ], "verification": { "by": "openwiki/0.7.0", - "at": "2026-10-09T00:05:26.272Z" + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/concepts/persistence-and-secrets.json b/openwiki/.claims/concepts/persistence-and-secrets.json index ed30b283..2e9a57e1 100644 --- a/openwiki/.claims/concepts/persistence-and-secrets.json +++ b/openwiki/.claims/concepts/persistence-and-secrets.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:be00dbc71a86d8b877773417c737dcd4b88e15560f038624c8dbece9b799a372", + "pageVersion": "sha256:09cb18ddf2ce306d18ee2f24e6faa2655b8914e5afe47d045d58c817231510ec", "claims": [ { "id": "claim_e50b505da2214c6781fa8eb1a146dacf", @@ -22,7 +22,7 @@ }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" } ] }, @@ -184,23 +184,23 @@ }, { "id": "claim_96c797e1d62b4565991af39bf957dd37", - "statement": "Agent run persistence writes durable snapshots and sanitized message/tool data, marks unfinished runs interrupted at repository initialization, and reconstructs recorded tool results that were committed before their transcript row after a process interruption.", + "statement": "Agent persistence sanitizes durable snapshots and tool data and interrupts unfinished runs at initialization. Message-ID replay backing stores captured redacted context and frozen model text separately from attachments; saves deduplicate historical messages and add missing replay without overwriting existing backing. Restore rejoins backing and recovers results committed before transcript snapshots, with frozen previews and ownership before compaction projection.", "evidence": [ { - "resource": "repo://Frameworks/TermindPersistence/AgentPersistence.swift#L59-L109", - "version": "repo-lines-v1:sha256:da292992815fff9efe5274ba635a38f9c4189246f1737ba3ca30ef3208ab7ad7:eyJzZWxlY3RlZExpbmVDb3VudCI6NTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcxMjIxN2Y4MzliNTcyYmE2ZWI3ODA3YTNkMDZkODBjMmFhNTA0NzU1MDUxZTlhMWQ3MTk5YjJlZmNiN2FhMmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVmNjA3ODI4ZDQxZDU5YjhjM2Y0YmZmOWQ0ZWQyZDRkODYyMzRlOGZmOGE4M2U5OTQ4OGYyMjhjNWQzMzMzZWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjliZWQ2YWM2NjQyZTQ4OWUxY2Y0NmNiY2EzNjhmYTg0MzM2OWYzYzgxNGYzMDZlM2RjMGRiMjQ0M2VjYjY0MWYifQ" + "resource": "repo://FrameworkTests/AgentReplayPersistenceTests.swift#L11-L74", + "version": "repo-lines-v1:sha256:886087c7334bbea2412216d39b6397a792600b19626f4b25e4bf4a96482cd396:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjgwODVhYzhjNzMzZTQ1ZGUyZmFjMTcyZjlmMjgzNmVkNzVlMDE4NzgzZTM2ZTMyZjI2NGE2ZWEyMTI0NzRkMWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE3MzgwYmYzNGNkYmJjNGVmODdiMWZhMTc2NWNiYTY1OTFiZGJmODk4ZDAzOGNkNmY2MDg5YTAxYzUxNGU3MzEifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L17-L123", - "version": "repo-lines-v1:sha256:e4fad18b6a94ed7b164d925406264f825e9f6fde1c027aaba953000636d429f2:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MmJiNmQ1NzgzMTJmNjJmZGQ3OWM4ODdhMGNjNmVkMDRjZDkwOTQwZjFhYWE3ZTlhNTAyMGVmNjkyOTFhOTU3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJkY2UzN2IzN2YwNGIxOGQwODk0NmFmYTY3M2UwYzIzNjcyMWEwMzkwNTQ2YjM4NTUyYzI0YWY3MDc4ZTdhYjMyIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlY2U5NjA2M2ViYmNhNjY1M2Q0YTYwYTViNjBjZWE4OTRkOTVmOWExNjQyZTQxNDEwMjFkNDZlNjQ4MjgyNGVlIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmNTcxZWY2ZjVlOTYzMTE4ZDM3MTdiNGQ4MmI0YjBiOTliMmI0OTI3NjYzNGFjNWZiNmZhMGE3ZTVjNDBlZjhjIn0" + "resource": "repo://Frameworks/TermindPersistence/PersistentAgentMessageReplay.swift#L5-L20", + "version": "repo-lines-v1:sha256:8116abb9ec0fbb55f0dcbdb056f0fad0fc2454b902bc85677d0fd983ba7c272e:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImEwYTE2M2IyODFkNWI4NDNkODc3M2M0ZTFjYzI3NzYwYjZmOGU2NzlhOTc4ZDQzNTM3ZTZhMjI0Y2M3NzI3YmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY4OTUxYTVlMDA0MTE2ZjBmM2Y1MzVjODgxOTBiNmUwNmU3NGFmZGZlYjU2MTc0MTA0YTIzYzQyZjJiYTNlZTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L799-L839", - "version": "repo-lines-v1:sha256:0bbf3410aaea40a96735687ffa5a11a929afa27945aae74e81a0b4f54512d7bf:eyJzZWxlY3RlZExpbmVDb3VudCI6NDEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYzMDUyZGM5NmZhZDk0ODE0NjNkNjcyNzYyOTZkNzk1Y2Q1ZTAyODJmZmNkMDAzNzAxZTEyMzcwMjM3Mzk3NDEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjljMjdkYzQ2OTBhZmJjZmM5NjgxOGIyYWZkNDY4ZjkwOGUyNDBhYTgwZWEzZWYwZjcyYjM2ZjNhOThjYTUyNjgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ5ODY4MDg2NjZjMjE5ZjM3Y2M3MzlhZGRlOWQ4NjBmZDIxNWRkNDc4MDU1OWI4ZWFkMzlkN2Y4ODFhYjI2ZDMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNiZTE1OGU4OWZkYzM4OTc0NzZhMGZmYTc2NjE0MmUxNGNkYjIyMTNjYjIzMmQwYTEzMGU3NTlhNWM3ZDllNGUifQ" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L12-L118", + "version": "repo-lines-v1:sha256:77d268c3d101f83592ed6e64ac7946a3fe571ffb214778f167e6c5e9d687f26b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJjYmU3MmYzNzMxNDJhMmEwMTgzZTliNTJjMTg5MzFkOTA1MjllYWM4ZDBmYmFjYWUyZTc2NzUxNmMxNWVmYjNiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiYmVjZDA0NDViZjRhM2E4OTU3MDU3MzRhNGJhMmIxY2M0MTI0YjczNTc2MmFmNGVmMWM0Mjg3MjU5MjE4ZTc0IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJiNmVjOGUyNWExOTgwZDRiZWY0NGZjOTVjYmVhMDM2ZGJmMzY4NDYxZTVhNTc2ZGE2MDYxM2FjMWFiYWM4OTUyIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5OTliZGVmZWVhY2E2MzQwZTAyOTBkYzg2ZTg4OWM4OWQ1ZTE4OWFhNWJhNDRiNTg1YjNiNzA3MzRmNGE2MmQ4In0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L906-L918", - "version": "repo-lines-v1:sha256:f142043bb1b01de9a0458b3995239712303b9ee5f7cd1bc4531d3ee6da2913f1:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ5NGQ4ZDkyN2FkMTU0ZTM2ZmFhZjZiMmI2MjMxZDllN2Q2MzRiY2IzZTliMjI2MzA4ZmJhMGVkOWQ3MDY0NjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImZmMGQ3NTk1MGRmMGZmOWEzZWU5NjYzOWRjYzhmZjVlYWVkYjQwZDY4MmJlYTkyNjIyYjUyMGRkODg3ZDkzZTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L209-L307", + "version": "repo-lines-v1:sha256:cdb5fc8aa4d9f1b13fe917cb14f628f43bd7a92589e84c879de40755b1f5c7b7:eyJzZWxlY3RlZExpbmVDb3VudCI6OTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjNDE0MzFkYTk3OGVmMTk5NTVmMzc3MTE3NDFjMTBjZjc0MDNlMTUwMmMxNjgzMjQxMzYyMGFkYTU4ZDE4M2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ3ODRmYmM2ZjEwMjJhYjI1Y2E4ZDIyNDcxNzM1YjEzOTU1YmNjODA1YmI4Nzk4OTllZmJmNWY3ZmQxNDZjMzYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFiZWY3ZjM3ZDMxYTRkZTdhYjAyMTk2NDNkY2E1NWMzM2U2YTNhMGMwNGRiYjhiOGM0MzE5ZjNhMjQwYjE1ZTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ2ZTY0NDQ0MDY5ZDYzZmZjYjNmZTBhMzIzMjhhMTYwZGZkMmQ0NmNiNWE0NTNhYjRhZGRhYTYwYzlhZTUyNTgifQ" } ] }, @@ -331,11 +331,11 @@ "version": "repo-lines-v1:sha256:bf0a95952f58864d9e6156cdff6e8a1b8ba739ec591ea780cd43f2922ba6f493:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjViMWM2ZTBhOTU1MmUxOGZjODUyZjU3MjhkM2ZiOTkyY2Q0MDY4NmU1ZmI5MzdhZTk2NGMzZTcyMmMxNTZjM2QiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjNmY3Mjg3ZjAwYjYzYTNhZTEzMDA1YTU2MmI1OTg2MjNhNzMyY2QyNDM1YTUzOWZkYzk3NzU1NDVlOWI0NjYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImFjZDFmZWMzYjNhNjkyNmFmMWE2MWU0OGYwNmQ5MDViMzk5Nzg4MGU0YTk4ZWQ1OGI0NDdiYmIxN2NlMmE1NzAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ4NTkxZTdiMmE2ZTc2MzhiZThhNzA0YTI5MmNlYzZjM2E3ZWViNDE2NzMzZjRkYTM2ZDEzOTcxNjA4NzIyMjMifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L404-L499", - "version": "repo-lines-v1:sha256:d56eb46f6aed19c20f0c7fb0d68fe2af08da7b92b424c249716e12111f61764e:eyJzZWxlY3RlZExpbmVDb3VudCI6OTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg0OTJmYjhlNzg0OTUwYzYxMWY1ZjY0NmY4NTcwNjIxMzQwMWViMDM3NmYyNDExNzdhN2E1NDgwMjlhODYxMWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImEwOTdmNmQ1MGQ5NjQ1ZTYyY2E4OWQ5ZDUxMmEzZTg3ZmJhOGQ3OTNlNGQyOTY3OWE1ZmYyZGI0MzNhZTZmOWEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVlNmFlN2MzOThiOGNmNDc4NjJmODRjZWJiZjQ1NjIzMjA5NWQ4OWEyMGU0NWJlNDE0MDg4YzVlOWQ2NjMwYjEifQ" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L404-L478", + "version": "repo-lines-v1:sha256:eafca5179cba724f32de34a4d9cf7704e6a77715464fad1ae435c09e898a8a40:eyJzZWxlY3RlZExpbmVDb3VudCI6NzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg0OTJmYjhlNzg0OTUwYzYxMWY1ZjY0NmY4NTcwNjIxMzQwMWViMDM3NmYyNDExNzdhN2E1NDgwMjlhODYxMWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImEwOTdmNmQ1MGQ5NjQ1ZTYyY2E4OWQ5ZDUxMmEzZTg3ZmJhOGQ3OTNlNGQyOTY3OWE1ZmYyZGI0MzNhZTZmOWEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVlNmFlN2MzOThiOGNmNDc4NjJmODRjZWJiZjQ1NjIzMjA5NWQ4OWEyMGU0NWJlNDE0MDg4YzVlOWQ2NjMwYjEifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L729-L752", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L708-L731", "version": "repo-lines-v1:sha256:487650c28b9099463e7bfa7834c959b0cf74fc3d13d1b1961ff07461c20fe72d:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNmMDAxZTQ3OTg4NTAxYjJjMTQ3YmI0ODRlN2FiMWFkMTJhODcwM2JlZTA2MGJkOTdkNWM4Yzg3YjMwNGYxZjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjIwZmIwMDQ2MGVkM2FlMTI3MTgwYzExMDE3YzE3ZDYwMTAwNWM2MmFiMDVjOTFjYzZhYzk0OGRiOTAxMTE2YzQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdkM2E0OWNiYWFhZmQzZTYxYzdlNGZhZWU4NzViNGQ0ZjBlMzA2NzAxMzM2ZmNiNTI0MTMyMjJhOTk2MTU0YjYifQ" } ] @@ -349,15 +349,15 @@ "version": "repo-lines-v1:sha256:10fcea6f816ea3054ea522ebc36586e7b8782bd608763948eacef93a8150db49:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNmODkyOWE2NWI4YTYzYzIwOWQ2ZWFlMzlhZjYyNjJlMTc3OTM5MWRkMDI0N2UxYTcyZjU4NjM1MGQyMDZkZmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ3ODRmYmM2ZjEwMjJhYjI1Y2E4ZDIyNDcxNzM1YjEzOTU1YmNjODA1YmI4Nzk4OTllZmJmNWY3ZmQxNDZjMzYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUxOGQwNGFjNWYzYmRkMDQ5NDFlZTZiZDUzODBlZWY0YmZiMzVkMTg3ZDI0MDI2ZTgxNmIxZGFlOTdjZWI1NTkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQ2ZTY0NDQ0MDY5ZDYzZmZjYjNmZTBhMzIzMjhhMTYwZGZkMmQ0NmNiNWE0NTNhYjRhZGRhYTYwYzlhZTUyNTgifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L449-L503", - "version": "repo-lines-v1:sha256:fafe8c592e30d89e8ef0492fa35d86a8f9d9589c330a6cb24674a16215aa8fd5:eyJzZWxlY3RlZExpbmVDb3VudCI6NTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMxNjVmOWY1ZTZjOWIxNmY2ZWI5NjA1Mjk3ZTExMjNkZTQ3OGJiZThmM2RkYWQyZDNiNzRkNjEyNjExNzdhMjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5NTRiMmFkY2VmZTQzYTcxMzMzZDgzZTQ2YmJlMjMyMTAwYTVmYTUzZTgyNjZjYTQzY2VhNzkyODRmYzc5M2MiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImEyYjA3MTkyMDFhZDMxMWI4ZTRlNTg2MDg2Njc3OTU4MjI0ZDlkYjE3MTYwYzJjOTc2MTFhMmRhZjc0NmI1N2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRlZjBlYzVlMTdjMGI5MTM2OTFhYzdjYWFiYmVkZjA1MDRiN2Q3YmUwMjBmOGZlZGMzOGQ4NTMzNzk2MjVjOGYifQ" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L449-L482", + "version": "repo-lines-v1:sha256:f4df4c1cbb24fdb3b2071d8fa61426ed21ee71b91e7ecd5b3c55222d70674dc3:eyJzZWxlY3RlZExpbmVDb3VudCI6MzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI1ZmIyZDU1NzZhODAzNjhlODRlOTAwNzZjNWIwNzRhYjI4ZjEzNjEyYTM5YTYyYzljYjg3ZDY3ZDBlNWJiNDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5NTRiMmFkY2VmZTQzYTcxMzMzZDgzZTQ2YmJlMjMyMTAwYTVmYTUzZTgyNjZjYTQzY2VhNzkyODRmYzc5M2MiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImEyYjA3MTkyMDFhZDMxMWI4ZTRlNTg2MDg2Njc3OTU4MjI0ZDlkYjE3MTYwYzJjOTc2MTFhMmRhZjc0NmI1N2MiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRlZjBlYzVlMTdjMGI5MTM2OTFhYzdjYWFiYmVkZjA1MDRiN2Q3YmUwMjBmOGZlZGMzOGQ4NTMzNzk2MjVjOGYifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L650-L708", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L629-L687", "version": "repo-lines-v1:sha256:5014db9ccb6a0cbf40e8f138d14954028a7d6d199831d2ae6a4c0b9faa328b4d:eyJzZWxlY3RlZExpbmVDb3VudCI6NTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNlNzYyM2U4YWEyYzQ1ZTRjN2YzMWM4NjFlY2UwZTljOGI2NTIzM2FjMzUyYWI2MDY0OWUyM2U1MTI2MDQwYWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBlMTk0NzllZWYyMjk1MzI3ZjQ1ODc4OGY4OTUwYjg1MGE0NTM2ZjE1NWE1MmUxYmZmYjk3MTIxZWU0MTUzNWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE3NDJmODZmOTJlMzg5MTljZjQzOGQ5MjdlYTUwZDMyZDkwMjljZjgxOGJmYTFkNWYzYWZiYTk1NGQxMmNlNzMifQ" }, { - "resource": "repo://TFMTests/AgentPersistenceTests.swift#L924-L993", + "resource": "repo://TFMTests/AgentPersistenceTests.swift#L930-L999", "version": "repo-lines-v1:sha256:045fa4ef13bc52a90ffab25fb94e19299987d08fef05641b63c785c813222973:eyJzZWxlY3RlZExpbmVDb3VudCI6NzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEzYTU4YzAwNmQxODg5NGM0YzI2YWQzMTNjMzJiM2ViY2U3MmQ1MjMxZDkzYWI1ODUwMTEyOTUzZDVkZGExMDEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc0ZTBlOWJmNjRjMDkwMDBmM2E2ZDY5Njk3Y2EzOGI2YjUwZDFlMzExMzU2ZGFkNjA5ZjlhNTI5MThhYzcwNzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE1ZGIzZWMwZTEwMGVmMWMyODNhMTgwZjc1N2Y2ZWM2NTUyY2RmZTkxNDYzNjcyNzJkNTBhMzQ0ZDk2Nzg0MzgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjEzMTA0ODMyODA3MDk0MDk0NTE4ZjVkOGY2ZTg1NTlkZGI1NTIyMjZiY2FiMDVmOGRiN2E5Y2Q0OWJlMzZlMTcifQ" } ] @@ -375,38 +375,38 @@ "version": "repo-lines-v1:sha256:e4fad18b6a94ed7b164d925406264f825e9f6fde1c027aaba953000636d429f2:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MmJiNmQ1NzgzMTJmNjJmZGQ3OWM4ODdhMGNjNmVkMDRjZDkwOTQwZjFhYWE3ZTlhNTAyMGVmNjkyOTFhOTU3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJkY2UzN2IzN2YwNGIxOGQwODk0NmFmYTY3M2UwYzIzNjcyMWEwMzkwNTQ2YjM4NTUyYzI0YWY3MDc4ZTdhYjMyIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlY2U5NjA2M2ViYmNhNjY1M2Q0YTYwYTViNjBjZWE4OTRkOTVmOWExNjQyZTQxNDEwMjFkNDZlNjQ4MjgyNGVlIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmNTcxZWY2ZjVlOTYzMTE4ZDM3MTdiNGQ4MmI0YjBiOTliMmI0OTI3NjYzNGFjNWZiNmZhMGE3ZTVjNDBlZjhjIn0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L507-L535", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L486-L514", "version": "repo-lines-v1:sha256:26c3ef0906ce4f204aec28cf677038c0753dc81b380b85204b76a2f8962b2817:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgwNzAyNmVmMjJiMzJkYmQ3OWY3NDc4ZDYzMTg3YTBjZDI4ODNjYjYyMTM4MmU3N2QzNGQyMjcyYTA3MzFlOTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE3NDM2MjdlZDk4ZTAyYjU3OGY1MjY5Yzc2MGVlNTg0ZWU1NDJjM2UzYzlhNDQwODkxY2QxNTE2MjVhNTlkZjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRlZjBlYzVlMTdjMGI5MTM2OTFhYzdjYWFiYmVkZjA1MDRiN2Q3YmUwMjBmOGZlZGMzOGQ4NTMzNzk2MjVjOGYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjlkZjlmMzU1MjY2ZmFjMjQ4ZDgxZDMwMDc2YWY4YWRiODFjNDBjOTY3NjFkMWQ3MzQ5OGUxY2RmMDNlYWZlMmUifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L710-L727", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L689-L706", "version": "repo-lines-v1:sha256:9430ebe704cfe45e02e2a23637debf77c4d62d0849a7065c405e4b78d26bff00:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY0MDUyMDRhZTk4ZDM4NjdlMTNmYTU5Y2E0MTU4YmU1Y2FkMTMxODZiZTBiMzlhOTZmNTNkMGQ2YmZkNTZiZjQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQwYTA4ZjYzZjc3MGZlNGQ4MDFjZmU5YmFmZTg3NjdhMTEzZWM3ZmE2Njg4ZjA1OGU1NTc0ZGJjYTNjMGIyZTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFmNjMyY2Y1OTNjNzEzYzQwMjI0YzhkNGVhZjhmZWI2ZjUyMWMyNDIzYzRiMTVhYzVkYzFhODAzMGEwM2U3MWIifQ" } ] }, { "id": "claim_19d50bbe64434f3bbc06c0abae0e1ef0", - "statement": "Edited target, removed later and adopted staging runs are retired within the repository instance; late snapshots/events are ignored. Truncation and snapshot save roll back SwiftData and restore the prior in-memory retired-ID set on errors, without adding persisted tombstones.", + "statement": "Edited target, removed later and adopted staging runs are retired within the repository instance and late snapshots/events ignored; truncation and save roll back context and retired IDs on error. Whole-conversation deletion retires conversation/existing runs and removes tool graph, message backing, messages, runs, conversation/order/model-selection and compactions, suppressing late writes. Unlike truncation/save it has no local rollback-and-ID-restoration catch and does not purge collaboration receipts or creation relations.", "evidence": [ { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L158-L167", - "version": "repo-lines-v1:sha256:c887b3a1878d05b6825a0061c2a4ac62fa358fae324872204ffb83d6b940c2ed:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcyYmI2ZDU3ODMxMmY2MmZkZDc5Yzg4N2EwY2M2ZWQwNGNkOTA5NDBmMWFhYTdlOWE1MDIwZWY2OTI5MWE5NTciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ2MDIwMDcwNjJmOGU3MDc5ODNiZjBkYmI4ODMyMDYwMjZiMzM1ZGY5ODBmODNlZjQ0YWYzYjRjMWI3NDA0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjcxMDIxY2E1Njc1M2E4ZWM0MDdkZjY5ZjhhNWM4ZGMwYmJiODEyOWE2MTQ1N2NjYWNhYTdjOGYwNzEyMWQ4NTIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMwNzU5M2RmMjVjYjQxMmZiNWI3NWUwZWI3NWQ3NGM1ZWY0YjBhMzE1YmJlMWRmYjcwMTg4N2MwMjEyMGM1NjcifQ" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L158-L160", + "version": "repo-lines-v1:sha256:d9bfdf9d449b49bfb91863c1e59b05c3ef085582197dfe1c42e35201506d2ce0:eyJzZWxlY3RlZExpbmVDb3VudCI6MywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNzJiYjZkNTc4MzEyZjYyZmRkNzljODg3YTBjYzZlZDA0Y2Q5MDk0MGYxYWFhN2U5YTUwMjBlZjY5MjkxYTk1NyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOTY3NTgzY2M1OGEwMjZkYzlkMDY5YjFkODM1ZjczZjllMGU3MWRkMmM3OTMxZjQxNmJkYTExNjE4NTdkZDNhMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNzEwMjFjYTU2NzUzYThlYzQwN2RmNjlmOGE1YzhkYzBiYmI4MTI5YTYxNDU3Y2NhY2FhN2M4ZjA3MTIxZDg1MiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMjE5YTFkZTc0MTYyZDQzNGVkOTUzY2MzOGMwMGY5ZWE0ZDZhOTkxMTk2YmNmYmI3ZmVjMTI1NjM2NjJlMWZiMSJ9" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L474-L481", - "version": "repo-lines-v1:sha256:ac80ea14eb69afa592a1e4e5c065857c6f79fb565723f8272ad51d7de5f79865:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMWJiOTAwN2E3ZTAwNDcwMjE5MWJjNGJiYzcyN2RkODJmMGM0NTE3N2ZhODY0OTU1YjMyYzA5NWFkOGMxYjVlMSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOGMzMDJiOTM5ZDRkZjk3Yzg4YzkxMDE0ZmRmNjRkMTI2M2I2ODEzYTYxYjhhNThlNjBjYzM0MzRkZDM4NDg0NCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMDdmMTAyMGM1YzI5MDdiZWJiM2Q0NTFlMjdjNzE3NjE1ZDNiMWNiNWU5ZjM2ZDM4Y2ZjMThmNWNhOTI1MDRhNSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNDhhZTFlYzI0ZTJkZDEwY2Y0Yjc5ZDRjNzUyODNhNzkxZTMxNjI0ZTRkYzQ2NmRlNzAwYzAwYmI3ODEwMzA0NyJ9" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L18-L118", + "version": "repo-lines-v1:sha256:1095588dc7bbd0001fb59e4ae67a5e6fe5d10c32fb4f0de7e87b9af3d804ce8b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAxLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3N2Y1NDVlZWM2OTNmZmI0NTZhYzY0ZGZmNzhhMDYxNzFiNjFkMjcyNWNkZTI1N2FiODY4ODM1NzQ2ZDljMzc1IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiYmVjZDA0NDViZjRhM2E4OTU3MDU3MzRhNGJhMmIxY2M0MTI0YjczNTc2MmFmNGVmMWM0Mjg3MjU5MjE4ZTc0IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5MDg2NTI3OTliNTFlNzllMWE4Nzk2ZWQzMzQzOWQ1ZjUxMDU2M2IwOTg2NWQ4MjhiY2ExYmQzMGE2NzE4ZmJhIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5OTliZGVmZWVhY2E2MzQwZTAyOTBkYzg2ZTg4OWM4OWQ1ZTE4OWFhNWJhNDRiNTg1YjNiNzA3MzRmNGE2MmQ4In0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L536-L548", - "version": "repo-lines-v1:sha256:f5fa05397f6c5f103da1b22e51552d54d9f197d9f1cd73b34142a57e11b9d9cd:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYxZjE3YzM4MDE4ZjhkMDliMDEwMWYzMDI4ZTlmZDk1MGJmZWVhMjljYjAzMTkxYWVlNDNlNzBmODU2MTViMTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhZGUyYTZhM2JkNmI1M2Q1MWI3ZGZjMzk1MjQ2MDBiM2I0ZDQ2OGMzMWM1Y2ZjNWM5MThiNjgyMDExOGY0ZjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM0Y2FmM2FiYTk4ODdmMWYwNWQ0OTgyM2ViOGQwNWE3YjhhMDkwMzhlNDQxNWE2ZmYzMDg3ZDQxZWRhOTQ2YWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjI2ZjkxNzkxMGMyOWI0MDdmMThlZjI1MDJiNmU1NWE5NDc1M2NlYjVhNDIyMzY0NDUwODgyODBiZjg2M2E1YzUifQ" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L409-L518", + "version": "repo-lines-v1:sha256:dbc4112c7e6c83a2cd3904324433c13c164b4107a734e13aa3661da627dde33d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI3MmJiNmQ1NzgzMTJmNjJmZGQ3OWM4ODdhMGNjNmVkMDRjZDkwOTQwZjFhYWE3ZTlhNTAyMGVmNjkyOTFhOTU3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlNmIwNDQ5MTQ2OTcyMmU4MzYzYTk0ZTQwNDQ1MzhlMDEzYTJhMjE3ZTM2YjFlNTY4YmE2MGFjYjdiNGM4ZjJlIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI3MTAyMWNhNTY3NTNhOGVjNDA3ZGY2OWY4YTVjOGRjMGJiYjgxMjlhNjE0NTdjY2FjYWE3YzhmMDcxMjFkODUyIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI5MDg2NTI3OTliNTFlNzllMWE4Nzk2ZWQzMzQzOWQ1ZjUxMDU2M2IwOTg2NWQ4MjhiY2ExYmQzMGE2NzE4ZmJhIn0" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L7-L25", - "version": "repo-lines-v1:sha256:892b2744f0e5c8d0f42d21afaff71c62cb2d48bf0ccdb614593ec779a826ae65:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkzNzE0OTM1NDEzMWZlZDcyNmU1MTA3MDQ0NjQzM2M5YzczMGY2MWQ5MWJlZmY1MDY4OTYzZTI2NjhkZThjYTQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFjMjU4YjdiNGY1YjRhZjg4NjE4ZDI4NGJkODg1Y2VmYzNiMmQxOWRhMTk5OTlmN2ExNDI0YmQxMDliMjNjZjMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE4NjBhNzU1MzgyZWFiYWMzMmM1MTBkNmYwNjU1ZjIxMWJmYjM4MzA4YzZjMzU5YTdkNzk0YzM1NmRiNmNkYzQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIyNTlhZmUxOWRiYmEzYWM5ZDQ2ZjRmNGNlMmU0MzM5N2YzZTQ0ZjYyYTQ4NmQ2ZGNlODcxZGQ0YjQ1N2JiODMifQ" + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L609-L647", + "version": "repo-lines-v1:sha256:1f43cd6854c1ea44813c6f7e780b68a56e2249c30f79d841041cf73171224828:eyJzZWxlY3RlZExpbmVDb3VudCI6MzksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImExOTQxNGMyMTg5YWI3MDlhYTYzYzg0N2MxMDE2MTc5OGIzMzZhZmE2OWIxMmJiMDg5OTc1Yjc2MDkyMTM0ZDUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkwODY1Mjc5OWI1MWU3OWUxYTg3OTZlZDMzNDM5ZDVmNTEwNTYzYjA5ODY1ZDgyOGJjYTFiZDMwYTY3MThmYmEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjllMGFhODc1MDZkYmJiZDI2ODg4MDNhYTliYmRhZWIxOWFmMjY0M2MyMWMwZjBlNGI4YTg4ZTM4NzUzNTg1YmEifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L91-L123", - "version": "repo-lines-v1:sha256:e67298b3f9afe1c7822ac62ba6a96304da0d269350970f9af57f0de1449539b4:eyJzZWxlY3RlZExpbmVDb3VudCI6MzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjExZWJkNzZiM2QyNjMxNTM3MzYzZmEzODEwMjI3MjEzOTJjZmYwZDI1YWNkMzJiNDQyYzA4MWQ4OGI3NDBjMTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRjZTM3YjM3ZjA0YjE4ZDA4OTQ2YWZhNjczZTBjMjM2NzIxYTAzOTA1NDZiMzg1NTJjMjRhZjcwNzhlN2FiMzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImRjM2U2ZTI3ZjQ1YTQwZjU2ZmU1YjRiMjNlZjQ0ODQ3NjMyZTJhZTIzZTk5ZGQ1YjZkNWIxNTA0OTMxMmZhZWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY1NzFlZjZmNWU5NjMxMThkMzcxN2I0ZDgyYjRiMGI5OWIyYjQ5Mjc2NjM0YWM1ZmI2ZmEwYTdlNWM0MGVmOGMifQ" + "resource": "repo://TFMTests/AgentPersistenceTests.swift#L589-L639", + "version": "repo-lines-v1:sha256:88fa286d2a5f976208d6690f1727ec8d69a649f5acd4c16530fd73ca8c380383:eyJzZWxlY3RlZExpbmVDb3VudCI6NTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFhZmIwODY0ODVhZDgyODRmOGI3Yzc2YmU5MmQzZTYzOTU5ZWExMmUwYzc0ZjJjZmVkZWM2ODBlZmI2YTFjMmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ3ZDk2NDg2NGE4NzBlMTAyNWYwODJiNTM2ODY4YzRlYmUwMmY1ZjkzOTM3NmMyYmJhNmY1NGYzYTcwZmM3ZWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU4ZWFmM2UxZGIyODRmNmE2ZmQ0ZTA2NmFlMmZiNjU0YWU2OGZkZmNmM2U3ZTRkY2E0YzJkZDIxNGE0OWI5ZDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBiM2EyYjczOTM1N2ZjZTQwYWQzNTViYmE0OTFmMmZlY2ZmM2JlZWQ2MWNkOWY0ZDZjNzhlZmY1NThiMzFiMGMifQ" } ] }, @@ -423,11 +423,11 @@ "version": "repo-lines-v1:sha256:572c14aae2ce8defd734596e7ea69dd84e1525de9a6d92c91853c2f091d9f82c:eyJzZWxlY3RlZExpbmVDb3VudCI6ODQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2YjhlMjRkZDNlZmY5YzY3YTZiNzg3ZWMwMjA3ZmMxZTI0ZGMxZTcwZmRlZWViNmFjOTljOGFkNjA1Yzk3YmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImRhNzdhMDY4Y2ZiMmUwMWRiMDJlMTAyZDRiODhhOWMzYWRlOTU3YTNhMWMzYjczOGE3ZTBjMWNmMTc5MTUwMjgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjkxNDVmN2U5NmE0M2YyYjc3N2RlNTYwODkwNTUwNjJhY2JlNzk1MWQ1M2FiYjFkNWQzYWI0NzIxNjdmODBkMzYifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2299-L2309", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2243-L2253", "version": "repo-lines-v1:sha256:0d402f7bc9263922ff162bdaf324f7e872edec5da836fac2270db712da2a5624:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVjMjc4YzRhNGM4Mzc4MTY2NDI1ZTg2MjZmOTMyODgwZTBmMjkwNmFmODU2OWM0NWExYjNmYWY2MWVjNmNmZTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk3MTRjN2YwYmY1ZWJiYTg0MmI2Yjg3MjlkYTI2MTliNzQyODMxZmMzY2I1NWQxNjUxOGE0NTY4NDJkNTkxNjgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJhNjkzYWFiOTVkZDE1YTNmMTQxY2ZhOWE2YjczZjgyNjlmOGM1MjNlOWFhMWZiYmQ4NDJlMmViMTQzYTU5YzAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ5OTRjYWQ2MGQ4ZGM0ZTFlYWE2OTkwYzBjOGM1OWRiNTE5M2EyZWU3YTM0ZDQxNWMwZjEwOTdiMWNmNTJjOWYifQ" }, { - "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2393-L2408", + "resource": "repo://Frameworks/TermindApplication/Agent/ConversationModel.swift#L2337-L2352", "version": "repo-lines-v1:sha256:687448483f885eab66762ecd1a5336e7e44e5df4db6a99fb3f9065b9e40b1cc3:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNmZmM0MzNjZDM1OTM3MTAxZjgyZTM2MzI0MGZiMDkzMWJlMjZkYTcwYjg1MjRlMDEwYjVjMzlkNzdkNWYyNjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0NjVmODNlNjM1MzFmM2JjZGNmZTU5ZmE0MmE5Y2M1ZGIyMzI4MWFmNzAxM2NkZGNiMjY3YzUzZjM5MWRlY2MiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM4MDU1NTIyN2IxNGM1ZmEwYTQ0NTA1NjhmM2U0N2RmNzAxMDg1NjFlZDU4NjM3ZTZiODlkZDc5ZmVhOGE3NzgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ5MmYyYmQzZTc3MWViYmIyMDI4ZjY2NzQzMWI5ZGMyZjEwMWJjNTUyYWU3OWU4ZTJlOTcyN2Y5YTcwMTViMWMifQ" } ] @@ -442,21 +442,37 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, { "id": "claim_aed4501475d34b428b6ad2388faf953f", - "statement": "Desktop SwiftData composition shares the container main context among many persistence services and terminates on database-open failure; mobile composition creates separate contexts for several services, shares a vault context among host and history/device repositories, and throws on container-open failure rather than replacing the store.", + "statement": "Desktop app-lifetime composition shares the main context among many persistence services and the KeychainSecretStore among Host, AI and built-in MCP token services, with separate MCP OAuth storage; it prepares persistence before feature construction and injects repositories through AppDependencies. Mobile uses several dedicated contexts and a shared vault context and throws on open failure; desktop terminates rather than replacing its store. Desktop language and appearance/window/sidebar/listener defaults are local settings, not Settings replica records.", "evidence": [ { - "resource": "repo://TFI/App/MobileAppModel.swift", - "version": "repo-file-v1:sha256:f83323dded23ec74be6130fa9c8ad66d188b5d0cce9cf7f82b226f4f9d58769c" + "resource": "repo://Frameworks/TermindApplication/AppLanguage.swift#L67-L95", + "version": "repo-lines-v1:sha256:0ebe2392437edb6cac6007aff094c0e95de792742365dfe472139aa3b6d6d9e7:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZiN2VjMjUwZGZkZjllZDY5ZGU0ZjIwNDU0ZjdkMTZiNjQ3MThmMGZkYzY1ZGZhMTNmOWFhN2FlYWM5YzUzNGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdmM2JiNGEwNDYyZWIyZmFhMmQ2ZTI1NmMzNWU3Mjc0YmY3MmNkNjFkNjIyNTc1OTdlNmEzY2ZlOTZhM2U0NjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjliOTFiNTQwNjFmOTEyZDViODQwZmQ1MTc3MDliNTNjZGJhZTUwYmVkNTgwN2VlMzJlZWM3YzUzOGQ2OTA2MjYifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "resource": "repo://TFI/App/MobileAppModel.swift#L193-L324", + "version": "repo-lines-v1:sha256:37ed5d5ecdf807df5b58df8ad8200fdac61c8fee003e371215c6b93580bb0daa:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMyLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJjMDA3ODU5MDhmZWNmMjg1MTVjYTAzMThmZGIyODg4YTY1OGU0MjI1ZGVmYzEwMTVhMWFhZjc1MWRkNzk1MDU4IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJiOGY5M2NlMWU5NzIzNzllYTBiNDg3MjZhNzEyNTY3Yjk1YTA0YzU1MzdjMGIxY2MzYmM5M2I4N2E1NGRhMTRiIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJkZjNiZDRhMDU1MzQ4Mzc0Yjk0NDdiNDUzZGU5OTM1Y2Y5YjI1ZTA3OTllZDdhOTFkNTJlYzE2OTQxOGI5Y2I3IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI0YjQ1MWM0NjhhODAyYWIzZTYyNmQzMjcxM2QxNTAyYmE2ZTc0ZWEyMmYwOTIzMTBjMzA5ZjMwYTEyOTAxODVlIn0" + }, + { + "resource": "repo://TFM/App/AppDependencies.swift#L18-L75", + "version": "repo-lines-v1:sha256:79481719dab4d6180b78f7091ff00f72f95521eb7292e2e52c3b300592207a72:eyJzZWxlY3RlZExpbmVDb3VudCI6NTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ0NzUxNDBlMjZlOWM1YTE3MTFkNjQ0NWExOGI3ZjRhZTIwMjJmOTkzZWY2Mjg0MDc1NTQzMDFiOTZlMGRjMjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVmZjg3MTQwMzZjZjI3ZmMxYzZmZGQ0NWVjYzU1ZDg1M2I5YzM2YzU5ZjNiZjkzZjE4YjY0NjUzNWVjOTU0NzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImYxY2QyMGQwMDRjNjEzYWMwN2M0Zjk0ZGE0ZDM4NzlmMjc5MWVkZmZiNTU0MjQzNGJlMmI1ODIzYzgxMzNhODgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA5Y2NhZGY4MjM1NDEwNTc3NDczOGU5YTNlOWRiNTkyMGYxM2Q3NDBhMWM1MGY2YzMyOWNhZGZlNTE3NWFjZDgifQ" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L280-L324", + "version": "repo-lines-v1:sha256:61e1d3bfcad279307246e15a9913ce0abe2d7e9ddbd81a8fb69721346b8578aa:eyJzZWxlY3RlZExpbmVDb3VudCI6NDUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY0Y2MxMmRmY2E3Y2ZiNjcxNjFhOTU4ZGU0MzlmNDc2MWRmMmIxMDMxZGU5NzdiOGNkNWNiMGRmNTVjMTg0YzIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQzOWVmODM0ZDM5ZTIxMTcwNWE4OTk4NTllODlkNTViMGM3MGI0Yjc0YTYyZWZlZDdmYWE4MDlhYTg5YzA3ZDYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcwZGEyY2NmZmZiOWM0ZWM4NTIzMGU5NDMzZGVmMDFlNDIxYWI4MjJkN2I5ZWQwYTA4Y2QzMGIwODg4YWJkNTAifQ" + }, + { + "resource": "repo://TFM/App/DesktopSettingsKey.swift#L5-L36", + "version": "repo-lines-v1:sha256:45d4a63543e5b55b678f4714973150c01eec2e42ca1fe833267976c3ccb6210b:eyJzZWxlY3RlZExpbmVDb3VudCI6MzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjgwZWE5YzczOTIzNDdiM2U2ZDFhYWM1YWVkNzU2ZWYyY2NiZGQ4NjBiMDc1NTI5NjdhNjIwNGI1ZTA1NWYyNDAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZmMjI4ZDdmMThiZmNkOTJjYjA0YWMyNDdkNGViZGFiYjI0NDk0Y2FkOTEyNTJmNjQxZmVkNjQ3ZWY2MzA4MzciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM0NmY4Y2RkMDA0NDk4MWZkNzI0N2EwYzg5MzE2ODM0ZDNlZGFjOWU1MTAwNThkNGMwNGNhM2RkODczYzFhYTYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNmZjE0MTczNzYwZWUyNWIwMWEwYjRmNTg2MmY2MzY2OGQzZjE3ODNmN2ZlZjEzODdjY2ZlOTM4NWNmNTFiNjYifQ" + }, + { + "resource": "repo://TFM/App/TermindApp.swift#L81-L207", + "version": "repo-lines-v1:sha256:d6d198c9af6adbc46f8c249bf451baae75c79ef9b00133f49d6e52123f7a2612:eyJzZWxlY3RlZExpbmVDb3VudCI6MTI3LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI0NDdmOGE0ZmQ4ZTY5YTNhYmNmZWM2NzVmNmIyNDk4YjcyZjVhYTUxMzk5ZWY3MjhkYTEzZmU3YjY5MTEzOTljIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI1MTAzNDgxYzU3ZDBmMGRjMGY3NmNkMmNjOGIzNjA0YzRlODI5NmRhNDEyMmFkZDE5ZTgxNzQ1ZGM3ZTMzMWE2IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIxNDc2YmI1NTFlNzBlNDI4Y2JhYWRlZTYwMmExNzYxNjg5NTcwYTI0ZTYzMjFkYmU4NWJiZGYwZGM3NTNhNDczIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJhMWU2Y2NhZGM2M2I5NWJhYmY2MzgzMTVkYjVhNmM3OGFiNGIwNTIxNWJhMzIyYWVjMzVhMjQ1OGVmYmI5MmEzIn0" } ] }, @@ -498,7 +514,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, @@ -831,7 +847,7 @@ "version": "repo-lines-v1:sha256:9bd10fc8b406a8ad6038421bb2928a254fd408790458b8fb028d436c1f408250:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZjY1MjgwYWQwN2MwMDI0MzM0MmU1YTY1NWI5NjgxMzYzNGU3NjY2MzkxNGZkNzAwZGM2NWZiNjE5NzcxN2ZmNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZiMDQ0OTE0Njk3MjJlODM2M2E5NGU0MDQ0NTM4ZTAxM2EyYTIxN2UzNmIxZTU2OGJhNjBhY2I3YjRjOGYyZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMzQ3NzM5ZTQ1MWY1ZTZjM2RlMjNkMmRhODdlMjMzZTA2NGViMTVlZWEyZjRiZTQ4MjJlMmIxMTlhYTVhMjYyMyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMDQ5M2QxNDg2YTIyN2FkMzNlNjMyZjA0MTgyN2JmZGFhODI1NWVmYWJmNzI3ZjI1MWEwNzBkMzYwMDk3OTg1OCJ9" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L650-L678", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L629-L657", "version": "repo-lines-v1:sha256:6a78c6acd99dbbaaa18b59ecb829793eccc73fc22a753217b2f556d57e330795:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNlNzYyM2U4YWEyYzQ1ZTRjN2YzMWM4NjFlY2UwZTljOGI2NTIzM2FjMzUyYWI2MDY0OWUyM2U1MTI2MDQwYWYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE0YjczOGIzOTM5NGE5ZmZjYjAxYzk5ZmZiNjExNWVhNWFhOWJlNWE1MTg2ZjlhNGMwYzA2ZWJmODM1OTgyNGMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBlMTk0NzllZWYyMjk1MzI3ZjQ1ODc4OGY4OTUwYjg1MGE0NTM2ZjE1NWE1MmUxYmZmYjk3MTIxZWU0MTUzNWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjhkNzFmMmRmNjA1ZTFiNDk2OGQwMGNkMzRjMjkxMTJjNGRjNzY1ZDVkYzJjOTM4ZTI2ODUzNGYxNTRlMmVjN2YifQ" } ] @@ -871,7 +887,7 @@ "version": "repo-lines-v1:sha256:a4b798041b2899a7406c77ad868b9b72b8ca571df09d5b60b836096d5de1006e:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQzMWM3NDU2MjAwMDhiY2IwYzAxM2Q0ZjcwYmI2MGQyOGFiN2QyOGQ2YmZmNjg5ZTMwOGRhMTU4ZGVjOWY3ZjgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU3ZWE0YTgwNmY3OGM5YWJhNjdhNGFiYzkwYzBiMzc0ZWI5ZjEwNWJjODU0ZGNmMjMwZjc5YTM3NjNhOWNhZjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjY5NGUyYTRkMWQwMmM0ODJkMjFhZTUzMzY1Mzk2Y2M1OGQ3Y2FkYzcyMGM0NmZiMGJmZTVkZjg1NGMwOWY1NDgifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L771-L783", + "resource": "repo://Frameworks/TermindPersistence/SwiftDataAgentRunRepository.swift#L750-L762", "version": "repo-lines-v1:sha256:17e9d8aa1863459eef413b879dd2014f08d0e0f5649819a4953d6bc7365f5320:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg1YjBmZDZiMzhmNWY5N2VkN2YzZmI5YzAxM2ViMTIwMTE2ZDQxYTk3NDE0ZGFhNzVjODNmNDA5NjQxMDA4Y2UiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNiYjUzMTU2YTNkYjA5MDdmY2I4NDEwNjJjNjc5NjdiMzMwY2I5NTk1ZjViNWEzM2I4ZWVmNTZjZTVlMzczZjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM2MTRmNmMyNWJkOWNkZWVkYWMzMWY1ZGViMTk4ZjExNTc2MWJkNjY4ZjkwYjg3NzlhNjAwMTczMGU4YWViM2YifQ" }, { @@ -1221,6 +1237,6 @@ ], "verification": { "by": "openwiki/0.7.0", - "at": "2026-10-06T18:02:45.153Z" + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/concepts/ui-design-principles.json b/openwiki/.claims/concepts/ui-design-principles.json index a67e9780..45e2d768 100644 --- a/openwiki/.claims/concepts/ui-design-principles.json +++ b/openwiki/.claims/concepts/ui-design-principles.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:5d2d18d31f2c7a1c6dab2916e7b91d3ae815773f66fe4b24bc577a3e54a928dc", + "pageVersion": "sha256:4cf6a62fc6c355a004b9d390a8367264ee27fcdf63e68df111db19ccdfc0a303", "claims": [ { "id": "claim_3a403468456840acbfe0a524b90d5717", @@ -155,7 +155,7 @@ "version": "repo-lines-v1:sha256:74d825112c4c6bf034c5574b2ff117fea7e833ade0a321f2a8caec27c19d9deb:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA2LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIwMWJhNDcxOWM4MGI2ZmU5MTFiMDkxYTdjMDUxMjRiNjRlZWVjZTk2NGUwOWMwNThlZjhmOTgwNWRhY2E1NDZiIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJhMGE4ODZkMjJlMjhmYThjZmFkMzg2ZTUzMzlmN2JhNGFmNDEzM2U4NGRmMjU5YTYwNTNjNjJiMzI4ZjQ2Y2U3IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5M2IxZTE2YzVlMWYyNGI4Njc3YzQzYjI2ZmYyMGI3ZDM3MGE2OGIyMWIwY2ZlZTA3NmQ4OWNlNmNjYmEwMmFmIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJjMTNhOTQ2OThjNjNhZjM5MmJkMWI0YTZkZWIwZWI5ZjdmYzFlNWVjZTQ0MGM1OTU3MTdkYmEyYjM2ZGJjYTU4In0" }, { - "resource": "repo://TFM/App/RootView.swift#L395-L404", + "resource": "repo://TFM/App/RootView.swift#L386-L395", "version": "repo-lines-v1:sha256:f3e76fe3409f2b446bae7dbcf097ec6627a488879b512df98b753d5f8a05e16f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZiZmVmODg3ZmEzNjEwYjA4MDVkN2ZiNTA5Yjk0MjU5OGQ1ZDgyOGU5YmNhMTlhNjM0OWJiMDU4NzcwMzhhOTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI2ZmI3ZDYyZTU5NjIxZGEwOGZkN2U4NzViYWRjNjg3MTNiMzk4NWE2OTcwYjgyMTRmNzg5NGY2NmU1MzY0Y2UiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjIxMTI0NDU4ZWE1NjRkODI3NTg3MGQyYzFhMDcwZDU4Yjc1ZGJiZTZhZjNlNWE1MWU4ZDgxNWZjYTgwZDhjZjUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc2ZGJlYTRiYmU0MzNiZGU5ZjY1YzBjMjQ2Njg0NmRkZWNlMjMxMzJlN2YzZmExN2FlNzU4NjExZmVkMWZiN2UifQ" }, { @@ -216,7 +216,7 @@ }, { "resource": "repo://TFM/Features/Settings/SettingsPageView.swift", - "version": "repo-file-v1:sha256:1f9c2221300c6e82d4a539f738405066b0d50137bd597304b70a16e0fe2b0b71" + "version": "repo-file-v1:sha256:14ee756a3acf0aa5377e772147f5953d3d7bd79d251ef2523c6b1ddc8f56b784" }, { "resource": "repo://TFM/Platform/TerminalFontCatalog.swift", @@ -244,23 +244,23 @@ }, { "id": "claim_4556ccaeb3eb45819c67c7edb0d5f37b", - "statement": "TerminalThemeOptions retains missing/empty/wrong-appearance names and supplies a localized fallback notice. Rendering resolves a copy, applies base/optional overlay/local typography in order and does not repair storage; desktop autosaves/imports custom text while mobile explicitly saves a draft editor.", + "statement": "TerminalThemeOptions retains missing/empty/wrong-appearance names and supplies a localized fallback notice. Rendering resolves a copy, applies base/optional overlay/local typography in order and does not repair storage; desktop autosaves/imports custom text in TerminalCustomConfigurationEditorPage while mobile explicitly saves a draft editor.", "evidence": [ { - "resource": "repo://Frameworks/TermindPresentation/Terminal/TerminalAppearance.swift#L14-L77", - "version": "repo-lines-v1:sha256:8d020317a85e51dd6cc1a45be81912da9e08c2713ca1d308dd50e07e1d88ab5a:eyJzZWxlY3RlZExpbmVDb3VudCI6NjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ5YWY2NTk1ZjU3NGMzNDhjM2YzMTcwZTA1Yzc0OWQ1NjBmM2MwNjFiN2RhMTRmMDE1NDE5ZDZiOWFmNThkY2EiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRhN2I2MWI4Mjc5OTgzMjY3Y2ExNjFhN2M0ZjJlNGIxMjgwMmQ4N2Q3ZDQyZGE2ZTY1OWUwZTEwOGQwMTRjZmEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNkYjdiZjY0ODg2YjgxM2VlMjA0ZWQyMjk2OWZkOGYzODhiOTZlOWMzNGUzNjFiNWMxM2U0YjE5MTA3NWI3NDUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM0NjNhNDZlMDkzZjY0MmFlYmIxZWFjYTM0OWY4MDkwNDhhYTZjNWRjNjMyNmVlOTI4ZTQ5NGU5Y2I2Zjg4MWEifQ" + "resource": "repo://Frameworks/TermindPresentation/Terminal/TerminalAppearance.swift#L42-L88", + "version": "repo-lines-v1:sha256:e97eab5a85d34d9f7467c119470bf36169bc710e9cb30cd033f92ff3275eab60:eyJzZWxlY3RlZExpbmVDb3VudCI6NDcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRmZDYwMThmMGJjNzZmMDQyNGJmODc5Mjc3NGM2ODI2Njg5MzRjNjJlYTgxYTcwZjA3MjUxYTRmNGNhOTU2MmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBmYzVkOGVjN2Q1M2E5MTkzMzI1NjYzZGMwZTdjYmZiZDgzZTg3ZGY3N2UxYzk2NmI2NjU3ZmU2NTc4NDBlMGQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjUxY2ViNzRiYzA1Nzg0ZmI3ZDI4ZjU1NzU2NWQyNGU3N2Q4ZDBkMDBiZWZhNDZlNWQ0YTk5YzA1ZTEwMzhhYTkifQ" }, { "resource": "repo://Frameworks/TermindPresentation/Terminal/TerminalThemeOptions.swift", "version": "repo-file-v1:sha256:69d40e9ddfbb5658171ec98e5193832648eb19237ba45be0d53c00740b222c8b" }, { - "resource": "repo://TFI/Features/Settings/MobileTerminalThemeView.swift", - "version": "repo-file-v1:sha256:6efdee4d31790b38f39bc81896fc9ff5d821e0f2dad36f3f821f3d859f6c6df2" + "resource": "repo://TFI/Features/Settings/MobileTerminalThemeView.swift#L19-L60", + "version": "repo-lines-v1:sha256:1705e815fe766823deb9aa98b4eff172949bee845c0a870d748e6b4b50270dfc:eyJzZWxlY3RlZExpbmVDb3VudCI6NDIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNiYTQxMjgyYjkyN2Y1MzU3ODhjMzBmOWVhNzYyYWNkZjQ0YzQwYmY5OGM5YzU5OWViOWI2NzJmODgzNjY1MWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjEyZjVjYzUzNjJlM2YwODJkZThlZTQ5NmVlOTgxYzU4YjhkNWU2MjAzNThlMTY4MThhYzcxODVhZmY0ODM4YWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgifQ" }, { - "resource": "repo://TFM/Features/Settings/SettingsPageView.swift", - "version": "repo-file-v1:sha256:1f9c2221300c6e82d4a539f738405066b0d50137bd597304b70a16e0fe2b0b71" + "resource": "repo://TFM/Features/Settings/TerminalCustomConfigurationEditorSheet.swift#L110-L170", + "version": "repo-lines-v1:sha256:1c95758f537f1a660aff3c223a3d68f6a6015466ffda13022e3a06b9a802780d:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY5NjY2OGNjZjFjZDBlYzYwYjE1YjY2MDEwZTBjZDJiZjJiNjgzNjA4NmFiYzM3MDgyNzY2YTU4NzdkMWM5NDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijg5OTIzYTUxY2Y0OTdmYWVhNWY5NmU1MGI4ODlkNTZkMTY0MWU5MWVkYmJhOTZkOWE4OGI2ZTMwOTVkNTI5ZWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoyLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM3OTRkMDJhODEyMTk0NGQxNzZkMjY1ZTliYjljYzU5ZjZhYjg5MGJjYWRmOGFlZmRlYmRiYmI0ZjJlYTg0NDQifQ" } ] }, @@ -798,7 +798,7 @@ }, { "resource": "repo://TFM/Features/Settings/SettingsPageView.swift", - "version": "repo-file-v1:sha256:1f9c2221300c6e82d4a539f738405066b0d50137bd597304b70a16e0fe2b0b71" + "version": "repo-file-v1:sha256:14ee756a3acf0aa5377e772147f5953d3d7bd79d251ef2523c6b1ddc8f56b784" } ] }, @@ -982,7 +982,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:46:25.102Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/integrations/mcp-server-and-bridge.json b/openwiki/.claims/integrations/mcp-server-and-bridge.json index 0149e933..24ecd9e6 100644 --- a/openwiki/.claims/integrations/mcp-server-and-bridge.json +++ b/openwiki/.claims/integrations/mcp-server-and-bridge.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:c8eeb91fb7537a16680adc1ffdbc8f81ed369c6badc52b5cb20602c8642bc9f3", + "pageVersion": "sha256:21e8cfac6118ff91704ee983ab1f286de7df0089fc2eebb0f1c857ee9b49f542", "claims": [ { "id": "claim_b47979774aa94e7dae517a6894c508d4", @@ -298,7 +298,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/App/MCPServerController.swift", @@ -422,15 +422,47 @@ }, { "id": "claim_8aaf53d004754102aaa9fca31fa81936", - "statement": "The macOS AppModel keeps ordinary SFTP transfers separate from MCP ticket coordinators, revokes MCP host tickets on local or remote host removal, and stops MCP plus ordinary SFTP before closing shared agent background commands and connections on termination.", + "statement": "macOS AppModel separates ordinary SFTP transfers from MCP tickets and revokes MCP host tickets on local/outbox or remote host removal. Termination fences Assistant admission, cancels pending preparations (joining captured failure saves even for unstored conversations), and stops all stored conversations before awaiting their drains. MCP stop and ordinary SFTP cancellation waiting run concurrently; preparation persistence, conversation drains and both stops finish before shared background commands, then connections, then Tailscale close. Repeated termination reuses the cleanup task; AppTerminationTests guard these admission, persistence and ordering boundaries. MCPServerController.stop drains transfers, stops protocol and HTTP, and cancels external MCP prompts without closing the app-owned connection pool. The app delegate hands cleanup to a bounded termination reply coordinator.", "evidence": [ { - "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "resource": "repo://Frameworks/TermindApplication/Agent/AssistantExecutionController.swift#L811-L849", + "version": "repo-lines-v1:sha256:89bb691c6db21c8701b079243ffe0b84e409027a6e6b858009e87416431b7620:eyJzZWxlY3RlZExpbmVDb3VudCI6MzksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk2MDJkNjgzYmY5M2Q2MzYxODAyZTY3ODljZTM4NzViNzgwMmFjOTcxZTZiZGM3OGQ0YmM2YmJkNGI3YWY3ZWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRlOGYyMDc4MzdmY2FhYWQyNTUzZmRkYWJjNGM5MWI5MTdhZTE4MzEwMjA1ZmUzMWE4ZjAyYWM3N2NiMDMxMjAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZiMGQzZjE0ZTM2MzJiYTlmMGUzYjlhMmFhMzgzZTA2NWFhMGFjZTE5YzUxNTg1MDZhOWIxYmM3NDUyMTFhZTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJkNDE2NjE0M2NiNTMyNGZkMmNhMmM4NTE3MjQxZTYwZTJlZTEyNzI1N2RmM2JmYzI1ZTE0NGNhMmQ3NDRmMjQifQ" }, { - "resource": "repo://TFM/Application/MCPServer/MCPServerTools.swift", - "version": "repo-file-v1:sha256:6e1ccc217b7c3ea17192067e55e62343eacffa72bf0f3ed202aaf3fe98c8edba" + "resource": "repo://Frameworks/TermindApplication/Agent/AssistantExecutionController.swift#L94-L108", + "version": "repo-lines-v1:sha256:67bedae5915d7f6a4ba3a785219f82320187368632bd179ce1d31a2c45ade07e:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIxOTY1OWM4ZDM4MWNhY2NmMDg0NjZiNzc4NTk4M2E4Zjc2OWE1MjU0MjA5ODYyODA4ZWRhNTkzNDQ2ZTIzYjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVmM2MyYmU5Yzg4ODZjMmM1YzI4MjYzOTk4MTBmZTYyMDY0YWZhZWEyN2RmMTNmNzQ5OGI4M2QyMmVlODBjODkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjhmNWY2ZGIwODM0YjM4NzEyNGRlZWVmZGZhMjFhOGIwN2UwN2FiYTM0OWFiMWRkZTNlMDg3Yzg0ZGE5NmEyYTIifQ" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L272-L324", + "version": "repo-lines-v1:sha256:0dc805dd35e0a4a25fa5f9c6cb75b7d30afce99c8375ec4d0b4be2b465880c99:eyJzZWxlY3RlZExpbmVDb3VudCI6NTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEwMWNlMzA3NzIyZTVhMzE0MjRmZGIzOGUzOTkxYjI3M2EzNGNjMDQxMjFjMzgzNzdmNGQzOTU1NmMzMGUwZWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjBhYjMyNmFmMzE3MTllYWI0N2UyNDYyNmNhYjAwYjNiNzk4ZTEzN2E3NjQyNzcyZjc3MDRlYjQ3OWQyMTI0ODEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcwZGEyY2NmZmZiOWM0ZWM4NTIzMGU5NDMzZGVmMDFlNDIxYWI4MjJkN2I5ZWQwYTA4Y2QzMGIwODg4YWJkNTAifQ" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L331-L340", + "version": "repo-lines-v1:sha256:06da4067f794c25f9a85af51c7012e9d36e5aaf94e1cad0c064da4a1a4ce6e2e:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI4M2E2MjQ1ZDczOTAzYzVmOWVlNzdjNjFlMjllMGIzYmZiMzNmM2Y3MmVlZTIwNDVlMWI4MWE5MjZhMGYxYmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImJlNzYzYmQ2YTJkM2UwMjQ0YzBmMDMwZTI0YzFlMjQzOTA1MTM5MTkzMWE1ODBlNWNiY2YwN2Q2YjBiOWYxZDUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY5YTVjNDU4ZWJhZGU4ODY2N2VlMTE3OTczZTEyMWViMmM1YmExOGMxNTFlYWIyZGUzOTRmMzhhOGMzOWZkMGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjMwY2MwZGE1YmRiNmYxYmUyYTFiYmIwMzNlOWE4YWY1ZTJjMWE2NGM3Y2UwZTQ0MzU4MjM0Mjc4YTlmOTdlN2MifQ" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L378-L394", + "version": "repo-lines-v1:sha256:2ff10708aaad6d89300562e49efe6ccf1616388f82fc934e1cff9a7956404e89:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUyOTZiMzVhOTYwOTBjNDBhMjQxNjljODI1NTBkN2M4NWY1ZDI4N2I0ODJmYWExOTlkZDA1NDRjYjk3N2FiNjciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBjNjQ3OWNjMjMwNDQwZmQ0MWU4MjI0MjE4Njc5Njk4NTU5ZDI4OTNiZDY3YjQzODBhZmFiODExYjM4YTU1YWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQxOWI3MGIyZmViMTE5YTZkZmIwYTAwYzdjNWM1Y2QzNmNkZWU4NGVmZjdhNjAyYWIzMjBlOGVjM2RiYmYzN2IiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE3ZjdhYWQzY2U0ZWRlZTg1ZDJiOGYwYzA4Y2MwNTQ0M2VjM2YwN2IyMDA2YWQ3OTRkMGFlOGQwNWViNjQ0NzYifQ" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L517-L528", + "version": "repo-lines-v1:sha256:f931d190fe71956803ae65ce36d34596d6b9efe6ca8c8b8b85491a050bc0eaa4:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0ZTBmOGRkODVmMjNhMDhiOTc2N2I5YzBiMGQxODQ4N2FmY2I3MWQ5OWNmNjEyMTc2ZDZiMDExMDEyMTdmNjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYzMDEyZTgzYTcxZjI0ZTVlZjRhZGM5ZTY1YzcyYTViYjgxZTY0MTkzZjI0OTViMThlOGY4MDEyMjUwZGI2YjQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5OTllNjE2OThkZTE2ODU1ZDc5MDViNTk2MzNhMmFmODZiODI4N2MxZTg4OGQxYjBkNmMyYzBmODg5MjJhNTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJjZDgxNTUwNzlmZTA2NTc5ZmViNmVjZTVhMTdmNWM0Zjk5Y2U0ZWYyZmNiZmE4ZGJlODVlOWNlZGI4MWVjYjcifQ" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L962-L1022", + "version": "repo-lines-v1:sha256:4f8f0f61f9b03bf5b6cfc96cddd4ae1820792080fa4b443c97d2cf2d6c7a78f2:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiNmE1OWU3Nzg2NmU1MGRiZDYxZjA5ZmQ1YWM2MjQwN2Q0ZGVlNjdkNGIyNTJhNDExYzRhZTEwZWNmMWQwNmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNkYmExMmRjNjgzMWEzMWU5NzdjMTMzMDU0NWZiMzZhMTFjYjE2MDQ5YjQ5ZGYwN2MzYjAyNjM4YjIwNzA2YzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUzODNlN2M2MmI5ZmI0ZGU3YjNjNTc3ZmFkNjNiOTYxMGRlZTcyNmI4NzljYzZjN2M5ZjYzNTljOTU5YWEwODQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkYjkxZWUwZThhZmIwMWVmNzYyMmM2ZTg0NzA2MDM2YmVmMzAwNTc5YWU1ZjcyYmQzOTViMmY3MjllNmRjOWUifQ" + }, + { + "resource": "repo://TFM/App/MCPServerController.swift#L161-L167", + "version": "repo-lines-v1:sha256:a3e376bbcfd66d32cf7f6c229f389d7cffac6b3a9b1ad55c8b314f14c2d65d8f:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMTU1YzAwMmVmYzg3OGUyOGNkMjg4YmM3YmY3ODg0YjU1YTI5MmZiNTk0ZDY5NTM5OGJhZjEwNmZkMzUwZjdjZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg5M2Y0MGUzNzgxZWI4OTZjODJkMDQ2YTA4MDY3YTExZTM1Y2RmMmYxZGZhZjI2ZmVhOTBiOGRiNjY3NjQxMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNmI2NDY3NmU5OGYwYzUxNTQ2Mzg4ZTc1ZmQ0MmQ5NjBhNTI2YTYzNjZmNTEwNWYzMzI5ZDQ2YmVkZTkxODBhMSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZDVjYTdkOGU5ZTkzMWFlOWFlZDJjNTYzOWQ5OTA2M2E2M2NhYjBmNGE0ODMyMGFjN2EyMmQxZGZjZmE1NjRiMiJ9" + }, + { + "resource": "repo://TFM/App/TermindApp.swift#L498-L503", + "version": "repo-lines-v1:sha256:8e2164a3b41bb1ce5549d814898124f2b0664c02230f2e236d71541efc6c2a97:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZTM3MDQyNjgyNDFmY2E3ZTJhMGM3YWYwZWIxYmViMTAwNGJkYmYyZmM4ZDE0YzM1M2YzZDlkY2U3YjdjMDhjMSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTUyMmM0MmNkMTljNmQwM2Q4MDhjNDUyNzIwNTUyODI1YjdlYTFjNWI0OTFiMjE4NTExY2FjNzQxNWNmOWQ5YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZjY0ODRlOWE3YWJkMGFiMjA3NTgyNzIzYzk5MjM5MzEyYjI3NDY2YTczNzg1ZDFkOGE3YzlkOThmOWUyZTc0OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNzFkZGIxMDVkMmRjMzg3MjEyOGQzZDBmMTM0NjI1ODgxZjBmYjI2ZTI3NGY1YTdlZWRkNWJjZWRmYjAwYjdmNyJ9" + }, + { + "resource": "repo://TFMTests/AppTerminationTests.swift#L12-L161", + "version": "repo-lines-v1:sha256:d3c4906bae2f47d7d1c330c36432cf2f7e2c4a59e0403a04d3967d2a90c292ec:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI0Y2YwOWViMGM1NzQ0ZmI5NzMzMWI5OGZmM2ZiZjA1YTYwMmJhM2Y3ZjY3ZTVkYTViZTkwOTUwMzNmNjI1ODI1IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI5NjYyM2QxMTVkMDFmNmYzYjViYWQxOTU4MDgzZGJmNGNlNDllYTQyMWU2Njg2ZjU4ZTg0OWZlOTU4Yjc4ZmJlIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIyMDNlZGU0ZTU2NDk2YWM0YjY1Mjk0MTY0N2E5NTA5YzcyNDU0NDJiODlhMWM2ZjA3ZWM5YTY1YzEzMzRlMzU0In0" } ] }, @@ -457,7 +489,7 @@ "statement": "Desktop inbound MCP enablement, port and disabled-tool preferences remain device-local; the port setter clamps to 1024–65535, and changing the port while enabled restarts the listener, invalidating existing sessions and tickets.", "evidence": [ { - "resource": "repo://TFM/App/DesktopSettingsKey.swift#L19-L24", + "resource": "repo://TFM/App/DesktopSettingsKey.swift#L21-L26", "version": "repo-lines-v1:sha256:61786b049dac7183986c7c87ae54aad746ea69efbd1124483204b818a925c03d:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYjM5ZWZhOTkxMTY2YTNlNDAyNTMwZmY1ZjQyOTI1Yjk3M2Q1ZjkzZDExMDcyYWI5MzQzNTYyYzQ4NTA1ZDNjNSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOTk1OGUyNjRkNTA2Y2Q1N2E2NjY4ZmRiYmI0NTdlYTUyZTRlOTI0ODc1MzBhNjJlYzc3MmY3YzAyZmNhOTk0MiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYWJjMDZhZDRmZGZiNTk4ZWNkNGZhYzE0ZTA3NjMzNTkxMTc4ZjE5ZDkxNzgwOGI1NzE2MzMwZDI0NDViYTE3OSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYWNmNTVmZmUxNDQ5M2FkYjhiMjQ0MWU1MWQ4NGYyZTRlYjk1ZTBmNTk0OWI5NzNjMjA0NTdhZDg5ZTRlM2Y0ZiJ9" }, { @@ -516,7 +548,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/Application/MCPServer/MCPServerTools.swift", @@ -695,6 +727,6 @@ ], "verification": { "by": "openwiki/0.7.0", - "at": "2026-10-06T18:02:45.153Z" + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/operations/build-test-and-release.json b/openwiki/.claims/operations/build-test-and-release.json index 82e07506..052f9444 100644 --- a/openwiki/.claims/operations/build-test-and-release.json +++ b/openwiki/.claims/operations/build-test-and-release.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:70bd137630ed40d98fd9a13e4fd39b1db016e8aa7e131c550d7d8521a25bf340", + "pageVersion": "sha256:6bf29a856991f4cb7b59362b99986616b669e153eb464116930925ada8345c89", "claims": [ { "id": "claim_d34d7b16495a44a4a946d3dc9e0456f4", @@ -66,11 +66,19 @@ }, { "id": "claim_ec9d22a2932b42bd80eae69e73f89de5", - "statement": "Scripts/check-cjk-localization.py checks canonical manual catalogs and validates zh-Hans, zh-Hant and ja coverage, finished/nonempty translations, positional format arguments, other-only plural shapes and locale-specific spacing.", + "statement": "Scripts/check-cjk-localization.py validates canonical manual catalogs and zh-Hans, zh-Hant and ja coverage, finished/nonempty translations, positional format arguments including literal percents, other-only plural shapes and locale-specific spacing. It imports the normalizer loader and normalization function and compares normalized bytes to originals without writing rather than invoking its CLI.", "evidence": [ { - "resource": "repo://Scripts/check-cjk-localization.py", - "version": "repo-file-v1:sha256:179c42fdaca9bf8b59b7bf3f1bf935848d54fa04d91f8e42912d876c4ed99925" + "resource": "repo://Scripts/check-cjk-localization.py#L100-L121", + "version": "repo-lines-v1:sha256:859479f162087fdbb52728e24aaa2f129f65dce66bdde3d557ab57caf5aea8d6:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFiMzk3OTMzYTI0YTgwMWVlOTIzMzI3YmQ2OWEwMTA0OWMwNzc5MTUwMjFjZjJlYzM5ZGRhNGY4MWFhODM3ZTQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFmYmUyNzI5YTcwNmVlZjA3YzFjZDRhYjNmNTA5MDhhMzI4NWMzNDZmNDdjYjU4MGJlOTc1YTU2NDRmZTI4MjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk0YjEwNjc5N2MzMTM3NDc4OTAyZWUyNDQyM2Y2Y2FlMTNkN2Q2YjUxYjBiZDQ0ODg3YmRkZDlkZTgyOThiY2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNkNDEzNDBjYzIzOWExNTljZTliNjAyMmU5ZjYzMjI4MzJlMmVhZDc2Y2FlOWU0NzU0ZDIxMGViOGZiNmQ1ZTgifQ" + }, + { + "resource": "repo://Scripts/check-cjk-localization.py#L14-L18", + "version": "repo-lines-v1:sha256:e9bb12958f04d916b6cdbe703db840f90d0381adfa51982e7e7a4d6451cabca4:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNjMzNmUzMzdkZmRmOWQ1ZmU0YjBiNGU4ODUyMDc4YzljOGIzYmU0NGUzYTBhNzZlYTA0NDQ1YWIxMGY1NTMzOSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYWEzZTFiN2Q2NzUwNjk3OTMxMDBmYTlhNTIwMmNmOTk2ZWYzOGUwMjZiYTlmZDYwMmZhODVkOGIwYzk0Mzk4OCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTI5ZDY3YWUzOWNkMDFiOWVkNmQ3ZDU3YmVhMWFiOGJiNGUzM2JlNWNlMzRkYjE1N2NlZmY3MTM1MTMyNjhlNSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZGJjNjljOGZkNTU5OGJkYWYwZTY3NzA0ZjU4MGRmNGM4MTgzN2YxZTQzNzZhODQ2MmJhMzA3OGEyYjEwYTc5OSJ9" + }, + { + "resource": "repo://Scripts/normalize-string-catalogs.py#L40-L80", + "version": "repo-lines-v1:sha256:7613d04f8b5bab096788d801e5e0ea12e8fa55acca7938a1f9e2fdcddc9c0cfb:eyJzZWxlY3RlZExpbmVDb3VudCI6NDEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU2OTRmMDVlN2RhMTg0MTkyMDVlNzliZmZmNGRkODEwZmRlM2FjNTYwOTFiY2I0Y2Y1YzM5ZDM4Yjg2N2I3MjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU3NDE0Y2MzNDVjYzZkOTE2NWE3YTk5N2VlM2RkZDI1NWUxMTYzMWY0ZGViNDlhNmUyNjMzZTRkYjY5YzUxYWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjcwNzM5MjVmOThhMjliODQwZGJjNjZkZjVkYzY3YjFmZGEzNGRkNjYxMWJmZjRiMDhlNzg2MWEzYjhmYWQ1NWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM2MWI0NTkyOWYyYmIyM2I2ODVjNTQ3ZjY3ZDhiNmU2NTRlNjQ0OWE2MzlkMmQ3NmY1ZjdlZmNmYTFlZTBjYzkifQ" } ] }, @@ -86,11 +94,11 @@ }, { "id": "claim_5cb32e4cb4ad43229d505772452cf610", - "statement": "AppLanguageTests verifies canonical English, Simplified Chinese, Traditional Chinese and Japanese tags, independent Chinese catalog resolution, and Japanese regional tags, reordered interpolation and other-only plurals.", + "statement": "AppLanguageTests covers canonical four-language overrides, legacy Simplified Chinese migration, unknown preferences remaining system-following without rewriting, removal of AppleLanguages on Follow System, Traditional Chinese regional resolution and display naming, selected-language notices, Japanese regional interpolation, Chinese token terminology, forwarding-template argument order, and English one/other versus CJK other-only counted resources.", "evidence": [ { "resource": "repo://TFMTests/AppLanguageTests.swift", - "version": "repo-file-v1:sha256:644bfcc202f4846774efc6e7747ebf2b66d116e00574ba71ad529716392ed224" + "version": "repo-file-v1:sha256:adad98919a8d3c113abaa52b9cfd824370296bf2669c60bee5af4dc74869be1d" } ] }, @@ -304,27 +312,11 @@ }, { "id": "claim_4f997647e9c54f8caf23bd151004f962", - "statement": "The AppStrings check uses SwiftSyntax to scan the macOS and iOS app Swift source for hard-coded presentation literals and fails with locations when such text should move to shared presentation strings; it is run alongside the Chinese catalog check.", + "statement": "The AppStrings check scans macOS and iOS Swift source with Xcode-hosted SwiftParser/SwiftSyntax for hard-coded presentation literals and fails with locations; it must be run locally alongside the Python CJK checker because the path-filtered Ubuntu Localization workflow runs only normalization tests, CJK tests and the catalog checker, not AppStrings or native Swift tests.", "evidence": [ { - "resource": "repo://AGENTS.md#L80-L87", - "version": "repo-lines-v1:sha256:2835b222d3b3bdfe35ffda03412be88c9346b3abd7446df7acfaf14d31dd7aa9:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiN2ViNmVkNjM3YmU2ODNjNzYxMmY2NzdlYTllMWE1NmI5NjU5OWU3NzJmNmQ3NjkwYjQyMzc0MGNkMTRhZmZmNyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMDFiYTQ3MTljODBiNmZlOTExYjA5MWE3YzA1MTI0YjY0ZWVlY2U5NjRlMDljMDU4ZWY4Zjk4MDVkYWNhNTQ2YiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOTg2ZjBmYzI2Yzg1ZWJjYjA4ODA0ODE3YmIwZDNkYmQ3YTA5YTYxYTU3ZGE5YTA1OTdhYWM5ZTc1ZDBkNzQ5MiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYzRkY2Q2ZjFjNTFjZjNkNGEzYWM4NTBkNTM5N2RlNGJiNGJlMDE5ZWNlMWE4Y2U2M2MzZTIxN2I3MTExMWFjOSJ9" - }, - { - "resource": "repo://Scripts/check-appstrings.sh#L1-L9", - "version": "repo-lines-v1:sha256:adde910147e7d0d622ca42489c4ab1b642ddddd27a30b9b74404bfdd958cf5ec:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYjg3NWY5Mjg1NDZhZWU3ODU1Y2IxZGI5YWZjOGFiM2YxYThhMzRkNDNkZTViYmQ2MmY3MDc2ZDdiYTlmMzkxNyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTg3MmI2ZGY1MTcxN2UwZDZmNDM1Nzc2ODUyYmFiNTQxZTMyMzI0MWZiOWJjYTFlYjNhMWVkYjQ5MjA5YzAxMCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjAsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjAsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSJ9" - }, - { - "resource": "repo://Scripts/check-appstrings.swift#L26-L60", - "version": "repo-lines-v1:sha256:d4e79deebc4939cdb718b608da41d76293da4ccd55a64b75d6fd2cd25edc31e4:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImMzNTVkNTljNDRiY2QxMzk2NmU4N2Y5MDI2NWJhYjZmYzdiNmM1MTA4Nzg4M2Q5YmQyZDM0ODgwYmMyNjYyNWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM1NmQzNzFhY2NmNmY5MThiYTA2ZjZmNGQ4NDhjZWZiYzcxMmQ3ODFiYzM3NDA3N2VhNmYzMjJhMDBmMGJmNzUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhjZmNmZjI1YzkwYmEwN2M0Y2U3Y2UxNDkxOTEwMWU4M2IwMGRjYmE0YmM1ZWI2NjNjZmRhNDlhNDY5N2ZkNTEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUifQ" - }, - { - "resource": "repo://Scripts/check-appstrings.swift#L5-L18", - "version": "repo-lines-v1:sha256:092f3212071893f610272a62e0dee5a7c9296cffe95064fc25cc8ed65bc5ce27:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBkYTY4ZDE3MmNlYjAzZWM2MjVmYTAwZDYyY2NkMGIwNGViYjIzOTNjMjRkMzQ1NTU0YjMwNTdmZGU4ZDQ4NTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFkZDI0NDU4MmI2ZmIwM2QyM2I4NDE1YWJlY2QzNDZiMGM5ZjQ2NmI2Zjc1MmIwNjgxODljZTdlOWEyZGMzNTAiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU3OTQzZmRkMjkzYzI3Yzk5YmQzZmU4MjlmOGZlY2EzYTFkZjc3MDZmYmMyNTIzZjAxNDE1ZDgzNjA3ZTVhMzMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU3NmIxOWU5ODgxNmYxMjE5NjE3ZjQzZTljMTM2ZDZiNTcxMGQ5ZmVmNzAzZmFhNThkMzVkNjY4NGIxMWYwODUifQ" - }, - { - "resource": "repo://Scripts/check-appstrings.swift#L64-L87", - "version": "repo-lines-v1:sha256:e567e2575025771a0f17f7c9e93126a63ce6c603b8f0c2e32035f9f7731d4860:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA1M2I1YzQ2NjY5ZmJhOTI5Y2YxNmEyNmEzM2U2MGVhNWIwYzFkNzg3OTc0ZTMxNzk2OTllMWU5MzMxMTFhMDAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU1ZjZlYjBjY2QyNTMzMWVlNTZkZDAwN2Q2MjczOGQ0NDAxMjIzMWViMmI2NDhmNzhhYjA2ODc5Y2Q5MGNkNWUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" + "resource": "repo://.github/workflows/localization.yml", + "version": "repo-file-v1:sha256:e8814648ef9ea5562999b0422d00e8839a2b9fd86962b6022816b1d656820577" } ] }, @@ -605,6 +597,6 @@ ], "verification": { "by": "openwiki/0.7.0", - "at": "2026-10-09T00:05:26.272Z" + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/operations/storefront-commerce-release.json b/openwiki/.claims/operations/storefront-commerce-release.json index 3c4c6b1b..6d11fa36 100644 --- a/openwiki/.claims/operations/storefront-commerce-release.json +++ b/openwiki/.claims/operations/storefront-commerce-release.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:d5a99c50d2acfa0cfa618499e38eaae356213ee5fa1bbf6312cfa277e3083175", + "pageVersion": "sha256:3848f628519d8c85e0240922e071f7a55949bf7c729ae87a40dbce46ae82e740", "claims": [ { "id": "claim_3d431c8c7b804187a1ef5a7d9178c430", @@ -60,23 +60,23 @@ }, { "id": "claim_6fa732935af2426fb937e410254e3f76", - "statement": "ProAccessController gates Tailscale, Assistant use, remote MCP, the built-in MCP server, Vault sync, unlimited Metrics and optional SFTP/port-forwarding multi-connection acceleration through current entitlement status; a denied require presents the paywall, while ordinary workspaces and SSH terminal uploads do not require Pro.", + "statement": "ProAccessController gates Tailscale, Assistant, remote MCP, the built-in MCP server, Vault sync, unlimited Metrics and optional SFTP/port-forwarding acceleration through entitlement status; denied require presents the paywall, while ordinary workspaces and SSH terminal uploads are free. AppModel defaults to unrestrictedForTesting, but TermindApp explicitly injects live access for normal launches and unrestricted access for tests and DEBUG screenshot demos.", "evidence": [ { - "resource": "repo://Frameworks/TermindDomain/Commerce/ProAccessTypes.swift", - "version": "repo-file-v1:sha256:76025c6e263e81a0ac57544e98af80bff29e236d4a70978541a7a99858bb2c62" + "resource": "repo://TFM/App/AppModel.swift#L209-L242", + "version": "repo-lines-v1:sha256:ff8cc3851059b4caa24ebf4fe199f8dea3713e9ffd025f7264aa3b1ebeacc142:eyJzZWxlY3RlZExpbmVDb3VudCI6MzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNjYjI0M2VlZjdkMmRhNjllZTVhMDk1YWYyYzYwMWRlMGNjN2Y4NTdjNjNkOTEyM2Y1Zjk1OWYyMjQwMDQ0YjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJiMGVkNGE5ZGE0MjhlZjdkOTUyZGJjYjE5NDY3MzUzNjE3Nzg5NzFiMWRjNGM5YjgyZmI1MmVhNTZkNmQxZDMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVlNDJiZGVhNzkxNGMxYzU5M2M0ZGRlNWZhNTZiMjAyMGRkMmZmMjVlZTRiYzkxZDJmNDUyYTNlYTI5YzBlNjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJiNmUzZWZkN2I3NTRiZDk5OTQ2ZDA4Y2ZiMWJlNzM3N2QxNWNjMjEzMzI1ZWZjZWJiODFiNDBjNjZjNmI4MjkifQ" }, { - "resource": "repo://Frameworks/TermindInfrastructure/Commerce/ProAccessController.swift", - "version": "repo-file-v1:sha256:2408f5cf552bb95297647b8f10dbb6dce3b944ca798eb88fe375ffec8ec759be" + "resource": "repo://TFM/App/TermindApp.swift#L138-L141", + "version": "repo-lines-v1:sha256:d7a35235421f01a6dbee1cfc2c3f3947990480cc7da3f8b1eeb7d4eadf443a0b:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDhlNjgyZDk4YTk0YjFlMTg2YTg3Y2Y2NGVmYWUxOWJjMjBjZWM0MDBkNTdlZWNkMjcwZjM0ZTc3OWMyNDliMCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiODc1YTE5OTFkY2Q5ZWFkYjc1NGE1NGY4NjUzYzllNWFjMzU3ZTI5ZmQyZmU3MGFlODlkZTFkMWEyYTk0MjMyYSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNzU5OWMzYWM2ZjU3NDEwODNlODAyYzk2NTUyYTA3MmU3ZjNjZWYxYzJhNTJiMTZkNGU1MmI1Y2Y1MjBiN2EwYiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiY2UwNGRiOWI5ZTM5NGZiZGQzM2Q3YWFmYTQ0MGVhODI5OWUxZDcwOWQ4NDRiZmI1ODU1ZTZkNzQ2NzU0OWQ2OCJ9" }, { - "resource": "repo://TFI/Features/Workspaces/MobileWorkspace.swift", - "version": "repo-file-v1:sha256:38211fbcc14ac724b1dcf4ce775cd5f3a8032776ff5b5970ab5c0d4d93e59d6e" + "resource": "repo://TFM/App/TermindApp.swift#L203-L207", + "version": "repo-lines-v1:sha256:253f2766d0bc85c7ef549b033a0be3882596a934bb7d3b0dd68df8cb7101e876:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZDIwN2U2MzJmNzgyNWU4MzFiYTM0Y2Y0YTk1ZjE2ODg1ZDg2Y2M2MGRhNWMzZjg2M2U4ZThjZTdhMWM4NTU5YiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTEwMzQ4MWM1N2QwZjBkYzBmNzZjZDJjYzhiMzYwNGM0ZTgyOTZkYTQxMjJhZGQxOWU4MTc0NWRjN2UzMzFhNiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjcxNzFkMTM3MzE1Y2ZhYWM1ZmQzYTI2MjE0OWFmYjBjYTJkYjg4NTVmZDM0NGRmYjA1MTlmYjA4Mzk2MGNiZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTFlNmNjYWRjNjNiOTViYWJmNjM4MzE1ZGI1YTZjNzhhYjRiMDUyMTViYTMyMmFlYzM1YTI0NThlZmJiOTJhMyJ9" }, { - "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "resource": "repo://TFM/App/TermindApp.swift#L45-L79", + "version": "repo-lines-v1:sha256:1c4e1f549badfa5ca58d54178883bde270bd5b6970dddbf7b834468148ba8d9a:eyJzZWxlY3RlZExpbmVDb3VudCI6MzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhiY2M2MTFhZjIzYTkxYzNiNzI3ZDBhY2U0ZGUyMjI0MmZjNDkwNmM3MTk0YTYzNDkxOWI2NWQ3MDNkZDAwYjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBjNjQ3OWNjMjMwNDQwZmQ0MWU4MjI0MjE4Njc5Njk4NTU5ZDI4OTNiZDY3YjQzODBhZmFiODExYjM4YTU1YWEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE5MGQ3NmNjNzA0YzU1NzliYjc4YTU5ZmQ4OTgyMDkzOGYzZjU1YmRhODVlZjZhZThkMDhhODM5Zjc2NzcyZGUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI5MGYxODJjOGI0OTgyMDk3OTA2NTZlNmY3YzcyNGVmMTM4YzcxMjBlY2NhMDU4ZDczMmU2ZjU5NTc1ZDQwZWEifQ" } ] }, @@ -191,7 +191,7 @@ "version": "repo-lines-v1:sha256:913ed5a2e5932b8d1c1573b0cb8b5f3e62894055a3bbc201a4f015959dd657bc:eyJzZWxlY3RlZExpbmVDb3VudCI6MjYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNiYzUzMDFiNzY2N2NjNmE5MGQzNzIxMjI5NWE0YmE5NTM3MTM3NDAwNjhjMzA2NmExYTYwOGUwNzg5ZTlkMDEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA3OTMwZTVmMWQwZjllZmQzN2MxY2E3OWNiOTBhNTk2NmQxMzBkM2E5YWE5ZTQ5ODY4YTQyYzI2NzYwMTI5YTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJkODcwOThkYmJlNThkNWY2ZTRhYWZkYWU4ZWU1NTRlZTgzNjc0NzQwNDA4MzgwYzk3Nzc5NDkzNzUzOWQwMWQifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L308-L316", + "resource": "repo://TFM/App/AppModel.swift#L316-L324", "version": "repo-lines-v1:sha256:9fa679389f29e57ac46d0b99df989db851134571fe1f6f46ed910facbf67ddd2:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMTliYWY3YTY2ZTY5MzhmOTA2ZjExZTg0NTQxNDZhODRhMjQ2ZmFlZWM4ZTliOGY0MTlkNjJjMDVkNTBjNTg1YyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNTkwOWE1M2E2MThkMDI4ZTFkMzAwMGMwYTdhYmNlZWNiZTVmYjM3ZjRlNTExZWZlNDY2NDNmYWQ2ZTY2ODcyZiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiM2MxYzlmM2I2NTRmZjQ4ODhjNjBiY2JlODhlMzVhMDE4MzczOGQzYzM1YjA4MjM3NTc1N2YzMGE5Zjc4MDcxMCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNzBkYTJjY2ZmZmI5YzRlYzg1MjMwZTk0MzNkZWYwMWU0MjFhYjgyMmQ3YjllZDBhMDhjZDMwYjA4ODhhYmQ1MCJ9" }, { @@ -333,6 +333,6 @@ ], "verification": { "by": "openwiki/0.7.0", - "at": "2026-10-09T00:05:26.272Z" + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/quickstart.json b/openwiki/.claims/quickstart.json index 69e41d50..e0842a31 100644 --- a/openwiki/.claims/quickstart.json +++ b/openwiki/.claims/quickstart.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:e8204b43a111f591306e3848cd2cc998d1530faad8663a3ba3409116f5efb3fa", + "pageVersion": "sha256:7152669edf8f22db53fba7b05ffdab63be45e83a486e4c62ded216b10e656d2d", "claims": [ { "id": "claim_83870f4f1ad6403ea0d8bd8775729a76", @@ -142,15 +142,27 @@ }, { "id": "claim_54a92b4e0f4b4678a15dea08e22fd005", - "statement": "During Debug tests closing the last macOS window does not terminate the test host; otherwise last-window termination requires menu-bar metrics disabled and metrics not keeping the app alive. A quit request rechecks pending SFTP edits and permits cancellation before starting shutdown.", + "statement": "During Debug tests closing the last macOS window does not terminate the test host; otherwise last-window termination requires menu-bar metrics disabled and metrics not keeping the app alive. Quit first requests application-wide confirmation when any registered, nonclosed window contains workspaces, then rechecks pending SFTP edits; cancellation at either prompt prevents shutdown. Workspace confirmation is application-scoped rather than the single-window veto, defaults Return to Cancel, and requires the explicit destructive second action.", "evidence": [ + { + "resource": "repo://TFM/App/AppModel.swift#L48-L53", + "version": "repo-lines-v1:sha256:21a9855a693e14929a76ad0194c8b1503fd2b2cad02a3e1ed6ff54629b38848d:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiYTYyOWE4OTEyZGFmZjY3MGNkN2ViOTFjZmM0Yzk1ZmM1NTVkYTU5OTQ3YjQzZDM1OGIxNzUyMzEyMWE5MjQ4NyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg5M2Y0MGUzNzgxZWI4OTZjODJkMDQ2YTA4MDY3YTExZTM1Y2RmMmYxZGZhZjI2ZmVhOTBiOGRiNjY3NjQxMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiOWJiOWRiNWFmZWE2Njc5MmIwMjlkNmE2MGI1ZjM0YTFkMzQ4ODhiNjU2MjkyNTA1ZjJhZmJhM2M0ZWE1ZDI1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZDhjNWY5ZDIzOGQwNmYyZDliODc5MmM4ZGFjNjg4OWY4YzVjOTRiN2YwNjQ0M2UxNDE5NTU2N2YwZjk4MTQ5ZSJ9" + }, { "resource": "repo://TFM/App/TermindApp.swift#L398-L406", "version": "repo-lines-v1:sha256:72b0ea3d0ed1f3a746b2322cc7dfcf974afdc9cce02dda03d97e5862dea07ce9:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZjYmYxYTMzZmU5ZDUxYjBlZjQwNzYzMzljYzEwMGFlMjBlOGFiYzk2OTZlMzk4NjBhY2JmNmU1YWQ2ZTQ4ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZiMDQ0OTE0Njk3MjJlODM2M2E5NGU0MDQ0NTM4ZTAxM2EyYTIxN2UzNmIxZTU2OGJhNjBhY2I3YjRjOGYyZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNDYwZjQzMzI5NTZmNTMwOGY0NTY4NDJjNzExOTFhOTAyZjBkZWY4OTQyNGQ5M2MwZTExYzBhMzZlMmM1Njg2MSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZjc3ZDhiZmVlYmRjZDdlZWJlYWRkYzZhMjNlNjNiN2JiOTQ2ODg0ZmMwZjUxZmEyNzE4NWRlMDYxMDMxMjg1MyJ9" }, { - "resource": "repo://TFM/App/TermindApp.swift#L467-L499", - "version": "repo-lines-v1:sha256:209641db46bb153b0476bd566ee4d717edc93bb27063d85095bf219df72b7583:eyJzZWxlY3RlZExpbmVDb3VudCI6MzMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjliOGJlZDVmYjA1YjU1Mzk2MGFiMDVlYTU4ZjI2ZDA5MGQ2MDY1NTE5ODkzZGNmOTllZmQ5ZTc1NGQ3Y2VhOGMifQ" + "resource": "repo://TFM/App/TermindApp.swift#L467-L495", + "version": "repo-lines-v1:sha256:ce0e0de91dea89911de96df9a32b8de776a1f170cbda9c45c75ccd33490cd4f3:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNiMzM3M2JhMmI4NDZhZDNmMTZiNDJlYzZjMzI4ODYyOTQ0NzM5ZWMwZTQ2M2Y2NmVmZjFjNTQxZjQ4MDFlNTgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU0MTA3ZDgzNGVhZTJiNGI3YjlkNmVmYTQyMzNjOThhZjEzZGIwODVkMjJlOTNiYWNlM2M5ODEyNzMxMjBlZTYifQ" + }, + { + "resource": "repo://TFM/Platform/WorkspaceCloseConfirmation.swift#L4-L35", + "version": "repo-lines-v1:sha256:a8b94150d16a2a237d6b3978764631ac951955f9a57611ca15c1717b6b3334c7:eyJzZWxlY3RlZExpbmVDb3VudCI6MzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZjYTg2ZTlmMmEwMzRmNDRkZjkxZDMyMmEwNDBjOWZjYTA0ZWI4ZmJiMTc1YWQyZmJmZDVhODNhMDA3NmEzODIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM5MmQzZWNhOTEwYTkzNTk5NWE4YjM1OTYyODRkMGQxYTY1YWYxNzVlOWQ4NWM2Njg5N2RmMDJmOWNjZTU2NjkiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY3Yzc5MTU2YTY1ZWJjN2M0NGE1OTNkZTgzZDk5Nzg5N2I1YmY3NWQ3OWQ3OWY4MWM2YjJiNzYzYjZjMTFhNzYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJjMjhmOWMzOWJmYTMxYTk1YzdkNTNmOGFlN2MyNmJmYWU4ZWI0YmJjMGE5OGMwNjk5YzdmNTU3YWJjNzUzMTUifQ" + }, + { + "resource": "repo://TFMTests/WorkspaceCloseConfirmationTests.swift#L12-L54", + "version": "repo-lines-v1:sha256:e9b1a0907677873cc84fea64d7dab25dd26926b3f3581064ab80b5681dddd668:eyJzZWxlY3RlZExpbmVDb3VudCI6NDMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjBlOGQ3NjRhODFhM2VjNTlhYzkxMGM0ZjAyMmI1NzIzYjU1ZmFmYjViNjRhMDhjYmRlZjJmZjM4YWJmODg1OTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImExOGM1Mjc0M2UzZWZlOWViNzBlNjY0ZmJlNzcxY2ZhMzA0ZjZhZmE5MDQ5YWRiZDVkMzUwZGQxYmZmZDg2OWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIxNWUzMTE0NzI2ZGJkOTM3ZjI4YWY3MDU0ZGEwMzhkYzg0YzdjNjhlMmRlNDEyYTNhYjVlYWU3ODVhNjBhODcifQ" } ] }, @@ -159,7 +171,7 @@ "statement": "Mac app termination fences new Assistant work, cancels and drains conversations and pending preparations before agent resource closure, while stopping MCP and transfers; AppKit receives one termination reply on cleanup completion or after a three-second fallback.", "evidence": [ { - "resource": "repo://TFM/App/AppModel.swift#L954-L1014", + "resource": "repo://TFM/App/AppModel.swift#L962-L1022", "version": "repo-lines-v1:sha256:4f8f0f61f9b03bf5b6cfc96cddd4ae1820792080fa4b443c97d2cf2d6c7a78f2:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiNmE1OWU3Nzg2NmU1MGRiZDYxZjA5ZmQ1YWM2MjQwN2Q0ZGVlNjdkNGIyNTJhNDExYzRhZTEwZWNmMWQwNmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNkYmExMmRjNjgzMWEzMWU5NzdjMTMzMDU0NWZiMzZhMTFjYjE2MDQ5YjQ5ZGYwN2MzYjAyNjM4YjIwNzA2YzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUzODNlN2M2MmI5ZmI0ZGU3YjNjNTc3ZmFkNjNiOTYxMGRlZTcyNmI4NzljYzZjN2M5ZjYzNTljOTU5YWEwODQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkYjkxZWUwZThhZmIwMWVmNzYyMmM2ZTg0NzA2MDM2YmVmMzAwNTc5YWU1ZjcyYmQzOTViMmY3MjllNmRjOWUifQ" }, { @@ -167,8 +179,8 @@ "version": "repo-lines-v1:sha256:b6a884010a3a8be3a6f465af0a85d69ba9541866b9e1ff17aab730481c270ef4:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZkNjgzYjc5NGVkOWI1NjQ2Y2Q2ZTZjMGEyYmMzYTk4YTFjN2NiYmMzMDUxZWZiN2E0NWQzYWI5MWI0MDRhZmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMxYmJkYmE3MTE3NjI3OTU5NzBjZWQxMDc4MzhjY2EyMGY2OGJkYmM0OWFiODdiNTE5YzcwZmEwOTdiZGU0OWIifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift#L467-L498", - "version": "repo-lines-v1:sha256:137856659fac0d68d91e7b1c93fbd085a6b332ba79101507113db99883cfd2f5:eyJzZWxlY3RlZExpbmVDb3VudCI6MzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcxZGRiMTA1ZDJkYzM4NzIxMjhkM2QwZjEzNDYyNTg4MWYwZmIyNmUyNzRmNWE3ZWVkZDViY2VkZmIwMGI3ZjcifQ" + "resource": "repo://TFM/App/TermindApp.swift#L467-L503", + "version": "repo-lines-v1:sha256:69380c6de488efdb3ff3c80f28c3a257f1218e5b073bab20137d81ac0008a0f3:eyJzZWxlY3RlZExpbmVDb3VudCI6MzcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjcxZGRiMTA1ZDJkYzM4NzIxMjhkM2QwZjEzNDYyNTg4MWYwZmIyNmUyNzRmNWE3ZWVkZDViY2VkZmIwMGI3ZjcifQ" }, { "resource": "repo://TFMTests/AppTerminationTests.swift", @@ -257,7 +269,7 @@ "statement": "RootView registers each window with the shared AppModel and publishes a focused window value while its own WindowModel owns navigation and workspaces; switching destinations preserves mounted workspace native views.", "evidence": [ { - "resource": "repo://TFM/App/RootView.swift#L237-L254", + "resource": "repo://TFM/App/RootView.swift#L228-L245", "version": "repo-lines-v1:sha256:42cc573560e6b9f5813f9e52d2a2c5187c2036a575d2e46b395432555c7cb817:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRiZWEyYWFlNzM1MmYwOWMyZTg5NGE1ODRkNWY5YmMzNzkyOWJmZjIyNjk5MzIxYjMyMGY0MTMwM2E1YmNlY2EiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJiNTI4MTA3MjY3NzU5YWYzYmQyYzJjNDNkODg5OWQwYTJmNWVkZWU1NjE1NjVjZTQ3NjQzMjZhNTczOWVjODUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjkzODYxNjc2YTBjZTEwMDk4YTM5MjEzZDU4ZjcyZDBjZDRlNzk2MjE5MzM1MGNiNmMwY2M3ZjgyOGU0MjhjODIifQ" }, { @@ -566,19 +578,23 @@ }, { "id": "claim_41cac555f2c44a6bad39af88e19af5d5", - "statement": "The desktop delegate's three-second fallback bounds the once-only AppKit quit reply, not completion of the asynchronous shutdown task.", + "statement": "The desktop delegate's three-second fallback begins after quit confirmations, when shutdown is started, and bounds the once-only AppKit quit reply rather than completion of the asynchronous shutdown task.", "evidence": [ { - "resource": "repo://TFM/App/AppModel.swift#L956-L1015", - "version": "repo-lines-v1:sha256:b56f13517073666a24fe04a1179386d65a0fc3fe2594c29841a7abf0f80c7726:eyJzZWxlY3RlZExpbmVDb3VudCI6NjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIyNjQ0ZDEyMmI4N2I5YzI1MDkzYzk3NjMxM2Y4M2IyNmI4NTFjNzZkOGVlYzI0OTQwMjVlZjg5YzdkMmQyNzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFiYzYzMzljNWY0OWY2ZjFiYjliNTNmYjljNDQxNmY1OGNkZTAxNjlkYjRlMGQ1ZDQ0ZmZmYmM4NDdmOGVjYWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjYzNmM5MGE0M2ZiOWQ0NDg2NzI0NmE0MzY5ODk4YWZkOTFiOWUyYjM0OTZmMjU3OTg1YTk0MDFhZjY4ZDI1YWYifQ" + "resource": "repo://TFM/App/AppModel.swift#L962-L1022", + "version": "repo-lines-v1:sha256:4f8f0f61f9b03bf5b6cfc96cddd4ae1820792080fa4b443c97d2cf2d6c7a78f2:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiNmE1OWU3Nzg2NmU1MGRiZDYxZjA5ZmQ1YWM2MjQwN2Q0ZGVlNjdkNGIyNTJhNDExYzRhZTEwZWNmMWQwNmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNkYmExMmRjNjgzMWEzMWU5NzdjMTMzMDU0NWZiMzZhMTFjYjE2MDQ5YjQ5ZGYwN2MzYjAyNjM4YjIwNzA2YzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUzODNlN2M2MmI5ZmI0ZGU3YjNjNTc3ZmFkNjNiOTYxMGRlZTcyNmI4NzljYzZjN2M5ZjYzNTljOTU5YWEwODQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkYjkxZWUwZThhZmIwMWVmNzYyMmM2ZTg0NzA2MDM2YmVmMzAwNTc5YWU1ZjcyYmQzOTViMmY3MjllNmRjOWUifQ" }, { "resource": "repo://TFM/App/TermindApp.swift#L288-L314", "version": "repo-lines-v1:sha256:b6a884010a3a8be3a6f465af0a85d69ba9541866b9e1ff17aab730481c270ef4:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZkNjgzYjc5NGVkOWI1NjQ2Y2Q2ZTZjMGEyYmMzYTk4YTFjN2NiYmMzMDUxZWZiN2E0NWQzYWI5MWI0MDRhZmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMxYmJkYmE3MTE3NjI3OTU5NzBjZWQxMDc4MzhjY2EyMGY2OGJkYmM0OWFiODdiNTE5YzcwZmEwOTdiZGU0OWIifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift#L467-L505", - "version": "repo-lines-v1:sha256:ee585d86e9800cf0bd90fde1d154b9eae60685a6fd67014ebfc1742bb763cd71:eyJzZWxlY3RlZExpbmVDb3VudCI6MzksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg2MmZlMmQ0YmFjM2JhZjAxN2NiYmY5MmFiMTcwZTQ0NWExMWQxNzYyODBkZDEyOTdjZmM2ZDQxZjI2Y2NlMGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlMjAyMDc0MmMzM2IxYjkxZWIyMjYxNmJmYTg4NWMxNjQ2NThlM2Y5YzAzZDNkODZhYzllMjgwMGRkYmNhMzcifQ" + "resource": "repo://TFM/App/TermindApp.swift#L467-L504", + "version": "repo-lines-v1:sha256:8ac2140fc31e6a705706a4d7f26f471bfa6949022eee7b4265dcdd9f759040f8:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjliOGJlZDVmYjA1YjU1Mzk2MGFiMDVlYTU4ZjI2ZDA5MGQ2MDY1NTE5ODkzZGNmOTllZmQ5ZTc1NGQ3Y2VhOGMifQ" + }, + { + "resource": "repo://TFMTests/AppTerminationTests.swift#L163-L200", + "version": "repo-lines-v1:sha256:23741adb64c4324b01aab2e7b6c1ff5b763b7be042588dafeb2e55ddb2254e05:eyJzZWxlY3RlZExpbmVDb3VudCI6MzgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZjA5ZWIwYzU3NDRmYjk3MzMxYjk4ZmYzZmJmMDVhNjAyYmEzZjdmNjdlNWRhNWJlOTA5NTAzM2Y2MjU4MjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImYzYjI4MzBlZWZiNWU4Y2YwOGEyZmUwM2FiY2M3MjE4NTI1MDljMzkxNWI2MDEyNGQ1YWQxZGQ2ZTA4MTUyOGQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQwMWUwZTUzYzVjZjJmYjM3M2ZhNjdiZGYxZWIxODUzZmI5ZWUyMmVkOWUwZWQwZTdiMWQwNDFiOGYwN2Y2ZjcifQ" } ] }, @@ -805,6 +821,6 @@ ], "verification": { "by": "openwiki/0.7.0", - "at": "2026-10-09T00:05:26.272Z" + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/testing/behavioral-boundaries-and-fixtures.json b/openwiki/.claims/testing/behavioral-boundaries-and-fixtures.json index 0159bc33..68aa1a0d 100644 --- a/openwiki/.claims/testing/behavioral-boundaries-and-fixtures.json +++ b/openwiki/.claims/testing/behavioral-boundaries-and-fixtures.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:467dd9cde36fbc898f77544e4e63b07bb479568e1e2ac2a9b4b74c909738007b", + "pageVersion": "sha256:6803ca504dbafc122e8d9c9e31a8de437ebcf1e70c38bf244998b701874b5a22", "claims": [ { "id": "claim_b7fbd64158554b8b8ca80a0a613b3bc3", @@ -168,7 +168,7 @@ "evidence": [ { "resource": "repo://FrameworkTests/AgentReplayPersistenceTests.swift", - "version": "repo-file-v1:sha256:14f58c527d4e7bd48d53770f4aa82830f48392d3de991a82f366d4f59abbc88c" + "version": "repo-file-v1:sha256:8d4f3a8ffcc25f57b952cf08a82e11a341a4a387f6f156ba1ba6460fb55179cf" } ] }, @@ -246,11 +246,11 @@ }, { "id": "claim_3b969ef21d1f4b9a9a5de6d84cf1daed", - "statement": "The same-run SwiftData edit regression uses equal timestamps, reversed UUIDs, compacted production restoration, legacy and unknown tool ownership, backing-row counts, staging adoption and late-write attempts; the empty-run regression separately proves live/restored ordering when the replacement clock moves backward.", + "statement": "The same-run SwiftData edit regression uses equal timestamps, reversed UUIDs, compacted production restoration, a legacy prefix invocation with inferred source ownership, backing-row counts, staging adoption and late-write attempts; it does not seed an unknown cutoff invocation. The empty-run regression separately proves live/restored ordering when the replacement clock precedes the retained run.", "evidence": [ { - "resource": "repo://TFMTests/AgentPersistenceTests.swift", - "version": "repo-file-v1:sha256:61cb2141cc962468a46078875631a0be60a1b726a84fda5ed5093d181ae51247" + "resource": "repo://TFMTests/AgentPersistenceTests.swift#L890-L1154", + "version": "repo-lines-v1:sha256:7399c2eff1450ed7fe6c461d6cab56b04d04cbd109567e18ec9903aa449e510e:eyJzZWxlY3RlZExpbmVDb3VudCI6MjY1LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIxYzlhMjg4MzM2YWFlNTM2ZWUyNDBiMGZjMGU5MTI5YzMwYmYzZGZiNmJiNWQwYzI1YThlNTk3ODk2N2Y1Y2Y3IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlOGVhZjNlMWRiMjg0ZjZhNmZkNGUwNjZhZTJmYjY1NGFlNjhmZGZjZjNlN2U0ZGNhNGMyZGQyMTRhNDliOWQwIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIzYWIzNTY3NzNjOTJiZmM4ZTMzOTFjNTRhNmEzMDUyOGE4ZWMxZjI2MTc3NjlkMjUwMjdmNzI1NzA5MDk0NjRiIn0" } ] }, @@ -273,7 +273,7 @@ "version": "repo-lines-v1:sha256:d337b352f4c959c9d41ff735d806ee5d7b84f73490eb20bbd0c7e4ed6d69643a:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYxMjNjNGMzNzhiOWZiNzhjM2ZjOWNhMzc0YTNhNjQ0OGE5ZjAzMzJiYWY1ZmEyNDIxMGJjMDc0YjlkMmRmNTgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU1ZjljZmVjYTlmZmQyNDRmMzFjNzdjZmNmNjMzYTA2NmZkZWQyYzI3OGVjNWQ2ZmRiZTU2ODM0NGY1ZWM1N2YifQ" }, { - "resource": "repo://TFMTests/ChatComposerStateTests.swift#L2327-L2410", + "resource": "repo://TFMTests/ChatComposerStateTests.swift#L2336-L2419", "version": "repo-lines-v1:sha256:c62222b56de5a198a7a34c403aeae467a2c6d9500b19185607bbfe80920954d7:eyJzZWxlY3RlZExpbmVDb3VudCI6ODQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEzYTZiMTA4ZmJkYTIyOTRkMjBjNmZiOWVmMzk2MDNiMTI3NzFhOTdmMzY2ODM5YWM3ZjUyNmExYWYxNGI3ZWIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU4ZWFmM2UxZGIyODRmNmE2ZmQ0ZTA2NmFlMmZiNjU0YWU2OGZkZmNmM2U3ZTRkY2E0YzJkZDIxNGE0OWI5ZDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ5NzZiYzRlZGFlZTk4NTVlZWFkMjc5MDI4ZmZlMDgwMjE5YjQwMzQzMWM1NDA3NDRkZWYzNjgyZmIyYTdhZDYifQ" } ] @@ -622,11 +622,11 @@ }, { "id": "claim_11f4624193ba4db7b308a4795688b4dc", - "statement": "Replay fixtures check that a queued cancellation adopts an edited staging turn and preserves its authored text, images, attachments and frozen context on fresh-context restoration, while a captured configuration failure adopts staging only after a failed snapshot is saved; a synthetic V15 migration does not invent historical context or replay rows.", + "statement": "Replay fixtures check that queued cancellation adopts edited staging without provider calls and restores authored text, images, attachments and frozen context from a fresh context; captured configuration failure adopts after failed snapshot save. Both join stopAndWait and check fullAccess and start/finish/message clock bounds. Synthetic V15 migration invents no historical context or replay rows.", "evidence": [ { - "resource": "repo://FrameworkTests/AgentReplayPersistenceTests.swift", - "version": "repo-file-v1:sha256:14f58c527d4e7bd48d53770f4aa82830f48392d3de991a82f366d4f59abbc88c" + "resource": "repo://FrameworkTests/AgentReplayPersistenceTests.swift#L120-L273", + "version": "repo-lines-v1:sha256:17c2d7731ea7e262182b7218552fb04245547463d6d43ac23f1edb1939f6c858:eyJzZWxlY3RlZExpbmVDb3VudCI6MTU0LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiIyYzFmMjZkZmI1ZDU3MWY4ODM3MmRmMzQ2N2NlYzk0YjJiY2QyNzU3MWE2NTJhMGQwNmQ3NGRhMDNjNmYzYWJkIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlOGVhZjNlMWRiMjg0ZjZhNmZkNGUwNjZhZTJmYjY1NGFlNjhmZGZjZjNlN2U0ZGNhNGMyZGQyMTRhNDliOWQwIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiJmNzMxNmM2NWFiYzFjNjZmOTFmM2YzODVjNzkzZDIxMzIzZWI5N2U5MWYzYjRlZmFiNDkzYzBhOGJmNWM1M2RiIn0" } ] }, @@ -1072,7 +1072,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/workflows/desktop-drag-and-drop.json b/openwiki/.claims/workflows/desktop-drag-and-drop.json index 9d0b32f5..8b05dd82 100644 --- a/openwiki/.claims/workflows/desktop-drag-and-drop.json +++ b/openwiki/.claims/workflows/desktop-drag-and-drop.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:efccd4e208efbf5ffeb58914325df9ee75620d3924a843bec8659e05bc60e35f", + "pageVersion": "sha256:d24218c79590fcd98003d809565d14803bea7a36c217b18ab04c2fadb38a5b66", "claims": [ { "id": "claim_ef9adaabb13c4328899d3e43b56b9fb5", @@ -79,11 +79,11 @@ "version": "repo-lines-v1:sha256:0b8fd9199e8804fda784fa842f1fd7b8bade62562fd8eef3075a3ad7135693eb:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ1OTY5ZjgzZWY0NmUyNDBiYzYwNWE5YjJiZGRjOGE0YWZjMmJiOTYyZjZkMGJiZTljYjFlYzIzMGI0MzdjM2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU3YzRmZjVmY2EyMGUzODk0Zjc3ZmY2NDk0Y2FmNmVkZDU2NzFiZjE3N2YyMmVmMDE3NDBlNzNhMTYwMTc3OWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMyMmNkZTI0ODQ1Y2UzNDIxOGEzYTZlYzZlZjhmYjM3ZmZjZTU1ZDZkNjRlZjk0NTEzMDIxNzMzOGEyOTU1ZWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVlM2EwNWU5ZDY5OGY0YzY1ZjA0MjdlYjM5MjcxMzU0YjhiMjA3MjVjM2NmYjNhOTg5NGU0NTAxYzJjYjAxNGUifQ" }, { - "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L192-L210", + "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L196-L214", "version": "repo-lines-v1:sha256:01874908a5c92a0ef2908dd9b2bb9b43d6333cc57ca3a8a2320586e492979ad4:eyJzZWxlY3RlZExpbmVDb3VudCI6MTksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjNiYTQxMjgyYjkyN2Y1MzU3ODhjMzBmOWVhNzYyYWNkZjQ0YzQwYmY5OGM5YzU5OWViOWI2NzJmODgzNjY1MWEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk2YjUyOGUyY2Y4NWU1NjdjNDIzNzk3ZjI4ODViYTFhZWRjZThlNmU2MWM1Y2NmZjBiNGFiZTNlYWIzMzUwZmIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkN2Q3ODI0ZGMxZjYwYTdiY2VlYjc4OWJkMzI0ODk4ZTRkNjgzNDM4YzFmZTM4NzQ3MjJmOGY4Yjg5MTFjYjIifQ" }, { - "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L100-L117", + "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L104-L121", "version": "repo-lines-v1:sha256:e1b70d29a86051aa53fea2a196d6399b043c77ee9abdaa20c30d2fd865c8e48f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFiM2Y5OTE2NTU5YmM4Mjc2ZDI1YTRiN2RjMTkyNWNiZjcxYzI1NTQ0ZDY0MzI5N2I4MzNkOGM1MTc5OTE3NzAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImY5Mzc5Yzg0N2Q1YWE4ZTg0ZjYwN2JmMGQ0NzI3YWE5YWJlYjg4NDkyNTQzZDk5NjhjOGJmOWEwNWQ2ZmNiZWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImEwZmQwNTk4NzdkYjNiNmYzNDYzYjBhOTlhZmNlYTY1YzY0NWJjYTFjYzE4MjYwNDVjYzc5YzBhYzJhZDFiMDIifQ" } ] @@ -97,7 +97,7 @@ "version": "repo-lines-v1:sha256:182e79a48134752c46c88db4a7b71bf6453722854b55eb56c7406f28ff38de7d:eyJzZWxlY3RlZExpbmVDb3VudCI6OTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjMwN2JmNzJkZjdjZDYxMzMyYTIxMTNmZTgwZDdkMjI0MmM0NjI1ZjA2Y2FhNzZlMWE3MWM1YmY1ZmQyMTAyYTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ" }, { - "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1068-L1175", + "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1111-L1218", "version": "repo-lines-v1:sha256:2c0dd51a6afd0947d86f23419c4e0fde6df86876384115d8aed6a91663b23a97:eyJzZWxlY3RlZExpbmVDb3VudCI6MTA4LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJkMGQ3NjAzOTE4NzFmY2UzZjc1OGY0ZWMwMGMzNmY1NDA1NTQzNzUxNmZjOTU5ZDZiYTc0ZTg3MGE3ODM1NTc5IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiI0NTM0ZmVjZjliMDllMTA2YTA1MzE4MmQ4ODQ3YmFhZjBkYTM4MmZkOWQ4NWQ1ODhkNTJmMTgwZmQyMWRmZjcxIiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiIwZTNkMzIwYzI5YjAwNjRlZjdlNzYwNzY2ODdkYTQ4YmVmYWZhNmI3YzlmNDg0ZWE3NzE4ZDI2Y2E2OTE1ODkzIn0" } ] @@ -107,7 +107,7 @@ "statement": "The Pane drag source is attached to eligible title content and starts a native session using a retained mouseDragged event; its transparent background recognizer rejects double-clicks, hidden or clipped sources, text controls and parked panes without delaying ordinary mouse events.", "evidence": [ { - "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L759-L790", + "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L763-L794", "version": "repo-lines-v1:sha256:941396f3a1af76d552b16f7105ae2d9e315856640223574a78f593ca935c0b94:eyJzZWxlY3RlZExpbmVDb3VudCI6MzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImYwMjA0MzJkYzQ3NGFmN2VmY2NlYmMwZTQ1ZDAzYzIxYmM3OTY0ZWMzZWY5NDE2Zjg5NmRlMDVlNmM0ODAzMzQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE0YjVlZjVlZTYyODkxMDdmY2VhNDAyMTEwOWFiYTNhM2IyMzc5YTcyODI4ZWE1YTEwZTJjY2FlY2FiZjMyNjgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjlhZmJkZWFlOTg5N2NjZjgwYWIyMzYwMTM0MDMyMDY5ODU1YWVmYjIxODhmZjQwMWRhOTVkYjM2ZWU1MzVmZWQifQ" }, { @@ -197,15 +197,15 @@ "statement": "MainWindowCoordinator deduplicates source/move requests and leaves ownership in the source until native destination registration. Registration consumes a matched request once even if extraction is empty; source close discards pending requests and restored scenes never replay a move.", "evidence": [ { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L240-L250", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L270-L280", "version": "repo-lines-v1:sha256:c5227808da46361610a937bc599dc1298bb128c83a8256599c10314dede9240a:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE5MTAzNjMwNDU0YzZhOGM3NjA1YTVhNGE0NjA3ODBmNDdjZWJkNzAyNjFkYWUxM2U1NmY5OTQzMzhlMjI2NGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVjMDM5NzA5ODE4YWMzNjZkNGY5N2RlM2YzNDJlYTliNGFiMWEzM2E3NjhmMGQ3ODZjNWEzMTQzZjFjYTQ1ZGIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk1YzNjOGYyMzE4ZDNlNzlmNzJmY2ZlNzdmNDc2ZmY1YjM2NDBiMWI4ZTFkODcwNDVkYTNiYzhlOGM3NTZiYzgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBkZjJiOTFiOWJjYTAzNDk1YTFhODRmM2IwODZiOTU4MDhjZjEyMWIyZTVlNWM1NDdiZTNlZDVmOWU4YWRkYTEifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L307-L351", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L337-L381", "version": "repo-lines-v1:sha256:8d55f7f4cea1fe28ed3ff9bcb4443438b1e4370c5e939a9e394ea4bf71dfae27:eyJzZWxlY3RlZExpbmVDb3VudCI6NDUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkMWQzZDYzMmM5MThkZmFiYzJkZmViZTQ0MmMxM2I3MDA2YTgxNzlmZjg5MDYyNDk5ZjBmZTMzNzRjYjFkZjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMxNTFjMDIzNTg2OTk0MDFhZDg1ZTkxZjZjNGU1YWQzYWU3ZmU5NGE3YWNjOTc0YTg5MTQ4MzZlNzIzZDg3NDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc4MGUyZjE0MWIxN2ZkZmNiM2I2MDY2NjE3NTg3ZWY5OGExOTE5MzQ0NDk3MjViYzUzZjZiMjllMTQ4OGNhOGYifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L428-L440", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L458-L470", "version": "repo-lines-v1:sha256:834e2826f2c333ad39ff15294a5120d5cd5f485c0c91883dfb36104c42ea2146:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg4MDA1ZTViMzQxMjg0NzIxNTA5NTE0ODRlN2VkYmNmNjhlYTUyYWE2NDkzYjAyMTdjYjA3OGZmOTdiNTllMDAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhhMmYyZDVlZTRmYWQyYWY1MWEyNjUyOTgyMDliZjYyNTVjYWE3MDlkY2I3NmQxYjY5M2I4OWRjNmU0YmM0OGEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjZkY2VlNWIwMjZkMjYyNTk0MmJmOTE5YmZjMzBlOTZkODY4MDE3YTI5ZmFiYWNhMDgyMmQ1MzAxNTYyYjk3MzciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdhYzQzNTM2MWVkMTYwNmIxNjg2NDFkZmQ4ZjgyMTc4MmE5NjdlZTkzYmI1NmRhYjBiNDI3Yzg1NjY1YTJlMTQifQ" } ] @@ -215,11 +215,11 @@ "statement": "Whole-workspace transfer preserves the same Workspace and full split layout; split-pane transfer wraps the existing Pane in a new Workspace, whereas a lone pane carries its original Workspace, name and template identity without disconnecting or ending its history event or adding reopen history.", "evidence": [ { - "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1012-L1024", + "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1055-L1067", "version": "repo-lines-v1:sha256:315ecac0f0460f320d589dcbff34ee0b23e0b64fda134b351d9978de513918a8:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc4MTk0NTE5ZWZhYzIzYzA2NWIzNTcxNGYzMTdmMTc4YmU5MTM4N2U2MTYwNjYwYTI3MmI0NzhmNWE3NTc1YzgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQ1ZmExNDU5MmY0NDQyODUwMzE1MjFjNWE2OGI2YThkNTE5N2VmYWIyMTUzOTY4NTcyY2Q5MzYwMmQzNmM1YTYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ4OWU5ODNjOTg3NjRjNGIwMjJjMzE3ODczNWU3MjJkZTU5Y2UwZjY4YTg5YzkxNDAyZWQ0NDY0NzkyYWU5ZjQifQ" }, { - "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1342-L1371", + "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1385-L1414", "version": "repo-lines-v1:sha256:a0a8c30995115f068d16a8f20efd48c39a49db0ed801b58b34a16b05d70c4ec7:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg1MGRmNGNiMDJlZjZmZmY0MGVjMDk1Y2RjMWRmZDQ0ODZhZmVkMTNkMWZlNTkyMTgwMzQ0NzUyMzBjNTk4Y2YiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjA1YTkyZGE1ZDMxNjBmNGNhM2UxYTgxNzkxMzgwZGRmZWZjNzU0ZjQ0Mjg5YmNiYWMyZGI3MDczNzYwOGE1YjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFhNmQ4OWYyNDA1OGY0ZWVmNzExNzA4ZTc4ZTBkOTllZjZhY2RmM2RhYzVlZjJiYzdiYTcyMTlhZTk1NWQ1YTkifQ" }, { @@ -241,19 +241,19 @@ "statement": "Drag-created windows suppress native opening animation, use the source's ordinary content size, choose the release-point screen and fit a top-centered initial frame within its visible bounds; restored windows without a request keep their system-restored frame.", "evidence": [ { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L247-L262", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L277-L292", "version": "repo-lines-v1:sha256:1e2754219415b86e30e7530ebf743adfa491b01ed35798ff9bb3346ed84815ee:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0Y2EzOGJlNzAwZmYyMzUxMDJiZjZkZjE5MzZkNDhkNzQ0MDMxYTg5YjQ5YzViNzljNWUyOTAwODdiZDI1MDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlNzRiOGQxZmYwZTY2NDcwNTc1YzdiZjE1Yzc5NTgwZTRjMWE4NjAxZTg1OWU2Y2E3ZmY3OGE3NGVmYjk0MDUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNmOGUyZjc0Y2Q4ODRjZjRlYTI2ODI0YzY1NDdiZGM5ZGQ5MjdlOTcxNGQ1YjJmZTJiNjAyMmMyYjJlMGI2ZDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjlkOWZmMDZjYjAzMWMzNzZhOGI0ZWViOWYwYWMyZGI4OWMzNTljYTZkNzVjZjA1MmQ0YjBkNDRhOTBmMWU5MWYifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L307-L350", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L337-L380", "version": "repo-lines-v1:sha256:9d0be27eb9bd671f1d070ef2eb3ba5809494e8faff898faf9afa4dad8330aef8:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkMWQzZDYzMmM5MThkZmFiYzJkZmViZTQ0MmMxM2I3MDA2YTgxNzlmZjg5MDYyNDk5ZjBmZTMzNzRjYjFkZjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE2ZGJkNDNhODE0ZDU0YzZlOGY1YmYzYzc5NThiYTVmMGJiNDNmNzViNzc5ZWNmYWJjOWEyMzk1ZjRlYzZlMzgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMxNTFjMDIzNTg2OTk0MDFhZDg1ZTkxZjZjNGU1YWQzYWU3ZmU5NGE3YWNjOTc0YTg5MTQ4MzZlNzIzZDg3NDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImZiODU1MjNhOWI3Yjg1ZWIwY2RhNmY1ODQ1OWE5YThiZTgxZmVjZWRmMGYwNGMxZTJjOGU0ODhlZDE1Njg5YzkifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L362-L383", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L392-L413", "version": "repo-lines-v1:sha256:9e6c6a9b40996f94777209d15c5a28131830093be76f35107c77414db64aa119:eyJzZWxlY3RlZExpbmVDb3VudCI6MjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEyYjMyMWFhODY1MzFhNDZlMjRhYjMzNWJlMWViNmRlMTVkNTQ4YjkwNDA0ZWFkN2Q2MzlkYzBkZWJjM2M1M2EiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQwYWRkOTI4YmM2MjRiYTJkNzUzNjI3ZTRiYWI4NGM0MDlkZGQyNjRlZDM1NjM5MDQ1YWFjMGRkMmFkZTVlMzYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlZDBmNTc5Y2E1NmE0MDc4OWM0YWNmMDFlOGRhNGYxNTg0NjA4NDk2MjA5ZmFkYzg4NzdkMTlhMzcwYzVkY2IifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L535-L542", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L565-L572", "version": "repo-lines-v1:sha256:013219a1d45597807fc4f5f32ab31434b02fce6c0f8e74d481aaca80e1f80135:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMTRiOWM3YTI5OTYwOGEyMzg5NWQyOTk5MTQzY2JkYTliMTZiZWQ4MDlhNjQzMzEyZTRiYjM5OGUzZmQ0YWJiNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg5M2Y0MGUzNzgxZWI4OTZjODJkMDQ2YTA4MDY3YTExZTM1Y2RmMmYxZGZhZjI2ZmVhOTBiOGRiNjY3NjQxMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNmNiZWIzNDAyYmMwMzM2MDgxYTY3NGVkNTRiZDljZTQ1M2IyNjhlZTk2YWU0ZDgwMWI3MTVjZTcwZGMzNDhjZCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTcxNmNlZTgwZDM3YzFmNDlhZjZlNWUzYWU4ZTY3Yzk2ZGFjYjc1NTk3ZTAyOGI1OTM2YWY4NTdmODMxODk2NyJ9" }, { @@ -267,7 +267,7 @@ "statement": "The pane content drop target is a separate outermost overlay above terminal/table native content and outside the workspace layout receiver; it is enabled only on active supported panes when window layout/conversation drag state is nil. SSH accepts files, SFTP items and terminal paths; SFTP accepts files and SFTP items; Telnet and Serial accept terminal paths only, while empty panes have no content drop target.", "evidence": [ { - "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L424-L483", + "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L428-L487", "version": "repo-lines-v1:sha256:1cf4f020054920eb029c849554dd7efdffbb543f977d091ae94b167ac79a1dfb:eyJzZWxlY3RlZExpbmVDb3VudCI6NjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQxYzQ5NDZlZmE1YjM5ZTFmMWJlOWMzMDI2ZDE2NDM3YjY0MjMyYzBmNjBjMjg0OTc2OTcwNmVmOGMyMDhiZmYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAxNGI4NjAzODcwZDZiZGEzMDMzMTcxYTExOThjOTBlODBjMTdjM2EyMjYyZDZhZDA0OWZiOGQ3MGE0MGRhODQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNlNmY2MjcyY2M1ODJiODAyYjY3ZTNiYzJjYjVkNjA2OGQ1ZTgxOWJjOGY0YWI4NWFkMGE5NGM4YzVkMzNkNjcifQ" } ] @@ -351,7 +351,7 @@ "version": "repo-lines-v1:sha256:3fc204b94144201fa77d7a124886ce1111c1841d7a9b9b1b6a9e399eab11be12:eyJzZWxlY3RlZExpbmVDb3VudCI6NTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFkOGUzNDE0ZDU4NGYyOWIyZTIyN2YwY2VkMWE5YzVhMjFiYTdhMTBhODA5MDRiZWYzZmE0MjZkMTNjOGJiNGUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJjZTdlZTY5ZTA1Mjg2Mzk3NTg5NTU3YTc1MGYwMzBmOWM0YzUyN2NiMTU5N2JhMzJjZDQ4NTY3MjQzMmUxOTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQxYjU3ZDcyNzJmOTVkNDc1NTU3Njk3OWFkYjdiZGFlNzk1ZTA3YzcyODA3NzU2ZGFkMTYyOWIxNDg4ZDY0MjYifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L826-L833", + "resource": "repo://TFM/App/AppModel.swift#L834-L841", "version": "repo-lines-v1:sha256:3211bbc0146e7aba81189fb9e8e3f3cd93766ff0ab228b78fd76c99982b6ac61:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOTBmOWVmNTU1ZmUzZDUyMjY4ZDlhNGNhMWVlMWViM2I3ZmI3NjcwMDk3YzUzZmY5OGNlOTE3ZTI4NTc3Zjc5NiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNDg5M2Y0MGUzNzgxZWI4OTZjODJkMDQ2YTA4MDY3YTExZTM1Y2RmMmYxZGZhZjI2ZmVhOTBiOGRiNjY3NjQxMyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZmYwZDc1OTUwZGYwZmY5YTNlZTk2NjM5ZGNjOGZmNWVhZWRiNDBkNjgyYmVhOTI2MjJiNTIwZGQ4ODdkOTNlOSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMmRmMDQzNTFiNTJkZDZiNzI0MTRkOWIxODQwOWNkYmNlYWEzODc5M2E4OWMwNjg0ZTAyZmYwZTViOGE1MjIzNyJ9" }, { @@ -424,27 +424,15 @@ }, { "id": "claim_15b78025588f4393b94778c68f475f4b", - "statement": "TermindCommands supplies MainWindowCoordinator's scene factory using the UUID-valued main WindowGroup; on the normal workspace scene path after the diagnosis-consent gate, TermindApp configures a fresh destination model from the request before RootView construction, and MainWindowRegistration consumes the move only after the native window is attached.", + "statement": "TermindCommands supplies MainWindowCoordinator's scene factory using the UUID-valued main WindowGroup; after the diagnosis-consent gate TermindApp configures a fresh destination model before RootView construction. MainWindowRegistration consumes the move only after native attachment, attaching a forwarding WorkspaceWindowCloseDelegate and establishing full-size content coordinates first. It observes initial and new isDocumentEdited values and clears true values without bypassing native close confirmation. The close proxy honors the forwarded delegate's veto, confirms only for an open model owning workspaces, forwards other supported callbacks, and restores the prior delegate on detachment only if still installed. This leaves registration-time live transfer and stable Workspace/Pane identity and connection/history semantics unchanged. Close-confirmation tests exercise the proxy directly; move tests register directly through the coordinator rather than testing edited-state observation.", "evidence": [ { - "resource": "repo://TFM/App/RootView.swift#L73-L85", - "version": "repo-lines-v1:sha256:76a7e7d87845d7e2822acb32c9f1ccbfafc9d645e2221fb3dc549f815ae36a4c:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY5MDFkZDM3NjU2Yzk2MTY4Yzk2ZTUxYzMyNTZiMDdhZWIxZGU0NzY0Yzk4N2VjNmQ3ZThmZTVjMmQ0NWNjZjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNlNzE0MzFmNGM5NzhiOWU2ZjQ4OTJiM2JjN2RiZGY4NTdjZjMwYTdiMzBkNmQwYzY1ZjNiZDE5MjQ4YjhkNjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQyYTc0OTNmYTgzZDczNmE3YzEzNzZjOWU2MDJlMDZjNjMyMzFlNGE4OWVjMzYzYWMyNmJiYTY2NjUyMGUwNTQifQ" + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L589-L628", + "version": "repo-lines-v1:sha256:9264a0364b40c87ccdf0c9f6b6dd0e605dd731a8ce45d308eb7afef0a174674f:eyJzZWxlY3RlZExpbmVDb3VudCI6NDAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlZTkzZTAzMjRhYzVlYTE2MjczOTFlYTI1OGNkYTcwNDcyYTRmYjRhMTdjOGQ1OWUyODg2N2YzYzJmNzk3YmQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjE5ZTc2MzU1Y2YxMTk5NDgwYzNmOTk4MzZiMTQ4NmM4NDFkODkxZmFhZDkwNGM2MDhlMDkzMDIyZDM3NTdhZWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift#L224-L252", - "version": "repo-lines-v1:sha256:a9f931599e4aa7e061feea57c4593b229ccfbdf3edb2fc6fff197103e36424a3:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYxMDk3ZDRjZTdkZDAyOWU5OGI2YWFkMjViMDFiYzEzNzhjOTdlMzA5YmRjMDY0YTM0ZTI2NTQ4MTFmMTgxYWMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1NWUzNDQwZWZmODhjNGJiYTg5MGEyNzcwZTIxYzE4MmVhNzEzNGUzMWY5MDE3OGI5YTI1YTU2YTFlZWQ2YmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQwMmQ5OGU3M2M3ZmQxZDQ4ZjUzNjdjZmY5NGFlNGYzNDlhZTYzZmIyMWRiYzkwZmYxYjZhNDViYjNmOWQ5MzEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQwZDNlZjc5MmEwYzE0NDVmOTU3NTQ1NjgyY2FmMWI1NjM2Y2NjNWIxNzE2MDlhMmVkZDFkODNiMjQyMWRiY2UifQ" - }, - { - "resource": "repo://TFM/App/TermindCommands.swift#L29-L32", - "version": "repo-lines-v1:sha256:8060505668641c6d0b7e265aee0d2d0b2fd4ef184339dab191cb5da15209d401:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMmNhOTdkYTkwZTEwNGUyMTFmODUzZjgzNTY3NDcwOTZmYjAzMzRlYjVkODE3MDI1ZmVkZWY3NjA0NTM3NTc5NSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZiMDQ0OTE0Njk3MjJlODM2M2E5NGU0MDQ0NTM4ZTAxM2EyYTIxN2UzNmIxZTU2OGJhNjBhY2I3YjRjOGYyZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNjFkODdmM2Y0YzAwNzJkMDYyYjFjOTllZmE0MmZmZWI1YWRlZTRiOGY0OWU1ZjRiOTFmNGVhZjBjZWYyZjQxYSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNmYyNWE5ZjEyZmYyZmU5YzIyYzE1YjAyMTg1ZDYzZWRkNzhiYTE2YjRhNGUyYzM1NGU0YzU3OTZmOTMwZjI5YSJ9" - }, - { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L265-L268", - "version": "repo-lines-v1:sha256:888ff190c58d98866db68a469eb57167acf6ea23655281ab8d385f1692b69996:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMjg3ZDYxMjBiNWJiM2Q3N2VkOTFmNzY2NDU4ZDBiNjVmN2JlMDY1MjE4YmNhMDI0MjYxNTk1M2MxOWI4ZGYyMiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZWQ0OWViMzY0MDY1ZTlkOWZjYTIxN2UzMWE3MDNhZGVlNTA3MTkwNTAyOTM3MzliZjhmZDljMjZjMjEyYmRiNCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZjVjZWMyNDgwMDQwZjEzZDdiNGMxNjcyYjE5NDExNzg0MTFjOThkM2ZlNzQxOWNlOGFlYTMwNDQ4MjVjY2NiOSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMGJmYTA4OWJjYTUxYzk2NWUwM2NmNDNmNjI1ZmUyMDg4M2IyOTgwZTkyM2Q1YjQ4NzYxYjcxY2Q1ZTM2ZDBlNCJ9" - }, - { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L575-L594", - "version": "repo-lines-v1:sha256:f9e5f3e56f6e49a7dfcec80b5d6990e48211b74570919dad10e3321a80460f38:eyJzZWxlY3RlZExpbmVDb3VudCI6MjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE1MjM0NjFlMjQwZGI1NWM2ZjdkMGQ4NTU0ZWJlMDM0MWIwOTFmZTk5NWI5YmU1NTNlNjE3MzNlYmM4ZWI1ODUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjJhYzgxZjkyM2QzYmEyMDNmZjZhMmJjOGU5NjFiMmY5NzgzM2I2M2MwYzA5N2RlYTcyMjVlNTU4NDcyNWM2MWIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjoxLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEifQ" + "resource": "repo://TFM/Platform/WorkspaceCloseConfirmation.swift#L49-L58", + "version": "repo-lines-v1:sha256:1334417673ef9cbe3bd175affea8da526314fd71c9664c485136d1f179edc802:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjcyYmI2ZDU3ODMxMmY2MmZkZDc5Yzg4N2EwY2M2ZWQwNGNkOTA5NDBmMWFhYTdlOWE1MDIwZWY2OTI5MWE5NTciLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNkNDZhMjkyYjAxNDZmMmI5Y2VkNjQzOTlmZWRiMGM1MjA2OTMzN2IzYzYyZjY5ZDExNzA2NDkyZjNmZjYwNmIiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjhjMzgwNzYzNTkyN2NiYzZiYjExZmYxZTUwNzllMjUwZTgwNjUzYjI1MDIzYzM1MzkxYjI3ZmZmMTkwZTY2ZWIifQ" } ] }, @@ -457,19 +445,19 @@ "version": "repo-lines-v1:sha256:a9f931599e4aa7e061feea57c4593b229ccfbdf3edb2fc6fff197103e36424a3:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjYxMDk3ZDRjZTdkZDAyOWU5OGI2YWFkMjViMDFiYzEzNzhjOTdlMzA5YmRjMDY0YTM0ZTI2NTQ4MTFmMTgxYWMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1NWUzNDQwZWZmODhjNGJiYTg5MGEyNzcwZTIxYzE4MmVhNzEzNGUzMWY5MDE3OGI5YTI1YTU2YTFlZWQ2YmQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImQwMmQ5OGU3M2M3ZmQxZDQ4ZjUzNjdjZmY5NGFlNGYzNDlhZTYzZmIyMWRiYzkwZmYxYjZhNDViYjNmOWQ5MzEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQwZDNlZjc5MmEwYzE0NDVmOTU3NTQ1NjgyY2FmMWI1NjM2Y2NjNWIxNzE2MDlhMmVkZDFkODNiMjQyMWRiY2UifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L247-L273", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L277-L303", "version": "repo-lines-v1:sha256:6db7e5ac4c73add58489f6562e0375fd66535e81e7280dab440bea967d5b5588:eyJzZWxlY3RlZExpbmVDb3VudCI6MjcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0Y2EzOGJlNzAwZmYyMzUxMDJiZjZkZjE5MzZkNDhkNzQ0MDMxYTg5YjQ5YzViNzljNWUyOTAwODdiZDI1MDMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImNmOGUyZjc0Y2Q4ODRjZjRlYTI2ODI0YzY1NDdiZGM5ZGQ5MjdlOTcxNGQ1YjJmZTJiNjAyMmMyYjJlMGI2ZDAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjA0YjI3ZTk0NjE2MDJkZjMwNGQ2MDIxYmI5OTI1ZDAzZGUyYTMwYjNjN2YyOTViMDg4MGEzYjA4MDI1ZmZlODkifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L307-L360", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L337-L390", "version": "repo-lines-v1:sha256:9365361ab301fd38e7cf83341c960e6c8e1bc3f16d62d67e12e5b76490e85d2b:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkMWQzZDYzMmM5MThkZmFiYzJkZmViZTQ0MmMxM2I3MDA2YTgxNzlmZjg5MDYyNDk5ZjBmZTMzNzRjYjFkZjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMxNTFjMDIzNTg2OTk0MDFhZDg1ZTkxZjZjNGU1YWQzYWU3ZmU5NGE3YWNjOTc0YTg5MTQ4MzZlNzIzZDg3NDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFkZmEzYzQ2ZGJiMGMxMGNlZTVhNjcwZmJlODVhN2ZiOTVkNzIyZTNmMTZmOGRkZDcxY2M1NGI1NGZiZDg5ZDIifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L362-L401", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L392-L431", "version": "repo-lines-v1:sha256:d91c93d97233424edb28debbb70eb1362a462812e32bb3f2fb3d48685343b3b9:eyJzZWxlY3RlZExpbmVDb3VudCI6NDAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEyYjMyMWFhODY1MzFhNDZlMjRhYjMzNWJlMWViNmRlMTVkNTQ4YjkwNDA0ZWFkN2Q2MzlkYzBkZWJjM2M1M2EiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjQwYWRkOTI4YmM2MjRiYTJkNzUzNjI3ZTRiYWI4NGM0MDlkZGQyNjRlZDM1NjM5MDQ1YWFjMGRkMmFkZTVlMzYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjNjZjk0YmMxNWY3MTllOGUzM2FmNmY1MzE5NDk0NTU1NjQ5OWExMjBmNWI4YWFiNGEzMzQwMjFjMzFhMDRhMGIifQ" }, { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L443-L453", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L473-L483", "version": "repo-lines-v1:sha256:ad85ac8844ffb01e79f52e14ca2956d97e99ccde8b3fac7632a425fa1c656c55:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE0YjhkNmFlODU2MTJmMjNiNWVhZWEzYjAzZGM4YzVhMjQ4MDAyNWRkNWFkMjg1MmNkYTU1MjY0Y2VhNTAyZDkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjliNTkyYTk0MDg4MWUwM2Y5Njk2ZmZjN2VjMzA5YjIwYmZjYzUyNTI5ODAzYjU2YzZiNzY0NjQ1MzZkZGY2NjMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImM1ZWFmMTA5YmRmZGMzNTA5OGUwYTdhMmFlODQ2MzhjNzQzYTJlNTU5NzRiNTJhYjA3MTE1OTQ1NzNlNWEzZWMifQ" }, { @@ -483,7 +471,7 @@ "statement": "MainWindowCoordinator consumes a move request at most once when a destination's native window registers, and ignores subsequent registration of the same native/model pair rather than transferring the layout twice.", "evidence": [ { - "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L307-L323", + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift#L337-L353", "version": "repo-lines-v1:sha256:a02031efcd9a90f907daf9de5f05cd1368b89f137edbe29135a8324804171b1b:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVkMWQzZDYzMmM5MThkZmFiYzJkZmViZTQ0MmMxM2I3MDA2YTgxNzlmZjg5MDYyNDk5ZjBmZTMzNzRjYjFkZjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIxMTI3NDFjN2Y3NDMxN2U3NzliOGYyMjRkYzE0MTE4YjBhMmZiM2JiODc2OWU2ZmM5Y2M3ZDEzNmNlYmY4YWYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMxNTFjMDIzNTg2OTk0MDFhZDg1ZTkxZjZjNGU1YWQzYWU3ZmU5NGE3YWNjOTc0YTg5MTQ4MzZlNzIzZDg3NDQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjM4NDk0MTcyMmUyNGM4NjQ2NDk3MmJhMmNjYmQ3ZDI1M2YzYjVjYWZhZjFmNTgwZmMyNDlhMGIyMTQ3ZmIxNjgifQ" }, { @@ -497,7 +485,7 @@ "statement": "Closing a source window records/disconnects only its still-owned workspaces; previously transferred panes stay connected in their destination until that owner closes, preserving the active history event through the source close.", "evidence": [ { - "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1341-L1385", + "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1384-L1428", "version": "repo-lines-v1:sha256:956354a729443fea52681a19a78507477535d8fb0ef7f6e2c6974e8a1ea3449c:eyJzZWxlY3RlZExpbmVDb3VudCI6NDUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAxYmE0NzE5YzgwYjZmZTkxMWIwOTFhN2MwNTEyNGI2NGVlZWNlOTY0ZTA5YzA1OGVmOGY5ODA1ZGFjYTU0NmIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdkMjhiZGQzZTJkNzRhZDQzODI5MzdjNzk1OWFlMzExZGUyZTllNjMwMzcyNTk1NTJiNTFmODRmNzQzMjFlMjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjI2OTg4ZTBhNTUyOTU3OGUyZmNkMWYyZWYxNGE1NzZhN2E3MjEzOGUyMjFiYjVkYzEyN2JlMmVlM2MxNmNiYTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImViMTZjYzU0NTA2YzNjMGI0ZDQ5YWU0OWFkYzUzMTQ4NzI1MWVkMTYyNjUyZDA5OGZlNTg1MmMzMmRkNjkxZDIifQ" }, { @@ -515,7 +503,7 @@ "version": "repo-lines-v1:sha256:0b8fd9199e8804fda784fa842f1fd7b8bade62562fd8eef3075a3ad7135693eb:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ1OTY5ZjgzZWY0NmUyNDBiYzYwNWE5YjJiZGRjOGE0YWZjMmJiOTYyZjZkMGJiZTljYjFlYzIzMGI0MzdjM2IiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU3YzRmZjVmY2EyMGUzODk0Zjc3ZmY2NDk0Y2FmNmVkZDU2NzFiZjE3N2YyMmVmMDE3NDBlNzNhMTYwMTc3OWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMyMmNkZTI0ODQ1Y2UzNDIxOGEzYTZlYzZlZjhmYjM3ZmZjZTU1ZDZkNjRlZjk0NTEzMDIxNzMzOGEyOTU1ZWYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjVlM2EwNWU5ZDY5OGY0YzY1ZjA0MjdlYjM5MjcxMzU0YjhiMjA3MjVjM2NmYjNhOTg5NGU0NTAxYzJjYjAxNGUifQ" }, { - "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L546-L610", + "resource": "repo://TFM/Features/Workspaces/WorkspaceContentView.swift#L550-L614", "version": "repo-lines-v1:sha256:a8adc36c5d1d4bff94353d4f1c124c667655eb556bde5bbd4b3aef33d8a57e4f:eyJzZWxlY3RlZExpbmVDb3VudCI6NjUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM1ZDAxZDFhMTRjNDQ5NGQyYmU3ZDExMWY0ZDI4MDdiNjc3MDQ5NTk1MjRmYzcxNGQyYzVlMjgzNWI4MzQzYmEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImI3MTdmMGQ3MzcxYmU5Y2UzMWM0ZTU0ZDZlNWI2NTY0ZWYyNzZkY2UxZjk5OWM3NzVkZjg4ZTAxZDI1MGUzM2YifQ" } ] @@ -523,6 +511,6 @@ ], "verification": { "by": "openwiki/0.7.0", - "at": "2026-10-06T18:02:45.153Z" + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/workflows/port-forwarding-pool-and-dispatch.json b/openwiki/.claims/workflows/port-forwarding-pool-and-dispatch.json index 3615c263..c77c967a 100644 --- a/openwiki/.claims/workflows/port-forwarding-pool-and-dispatch.json +++ b/openwiki/.claims/workflows/port-forwarding-pool-and-dispatch.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:5ee8772a0e33c456214bfb063fbb5dbb33c2dff0126593dd0f2ab23d78e35c93", + "pageVersion": "sha256:402ee9807551c18684f8fabd21deb9197aa67a88199193965fe323b99246fab3", "claims": [ { "id": "claim_a66f64ff267243989e5b4bbc103576dd", @@ -16,7 +16,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, @@ -46,15 +46,23 @@ }, { "id": "claim_62c62fe878ac485da32c2358658364ad", - "statement": "Accepted local/dynamic sockets are assigned by least asynchronously reported flow count for that rule, with UUID-sorted round-robin ties and generation checks; assignment neither reserves an optimistic count nor retries another worker after rejection.", + "statement": "Accepted local/dynamic sockets are assigned among installed, non-draining published slots by least asynchronously reported flow count for that rule, with UUID-sorted round-robin ties and generation checks; assignment neither reserves an optimistic flow count nor retries another worker after rejection.", "evidence": [ { - "resource": "repo://Frameworks/TermindRemoteInfrastructure/SSH/SSHForwardingConnection.swift#L319-L339", - "version": "repo-lines-v1:sha256:0146c08abeae8270031daea34a25749e0d96e7c3a937821e92e69f70638fa848:eyJzZWxlY3RlZExpbmVDb3VudCI6MjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImFmN2NjZTJiZThkYTJiZDg2NTlmYjFkMTNiZDM2MmFjOWYyYWJlYTU3NjI2Mzc5YTc5NWZhNTU5OWUxNjVmMmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjBjZWYwM2RmM2Y1MmZkNGJlMmQ4MmNmZmQ4NjUwNWNkYzUxZTJlMzk2MWFlYzRiNzZjMTY5MTg0YzQxNGY5NjAifQ" + "resource": "repo://TFM/App/AppModel.swift#L70-L115", + "version": "repo-lines-v1:sha256:ff243e2c108981e17e05e3c8c659b8f9ec61fdb873f0b90fa32db926c25e0a21:eyJzZWxlY3RlZExpbmVDb3VudCI6NDYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI0NmM2MTEwMTIwMzNjMzdhNmYyMDljOWI5ZTY4OTRmNWUyYzU5NDdjNzExZDIyMjdhODU5ZjdkYzk3OTQwNzEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc0NjQ1Njc2MTQ0YzAxNmYyZjA2MzQwZjVhZTk4NjdkYTRlNTY1ODBkZTE0ZGQwYWI1NjE1ODZlNGFmYjIwYzQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMyMWFlMmU4YTZkNjhjMmE5MzQ1NjQ3N2U2MWZjNzIxZjFhMThiMGMwZTIxMjNjNDg2MDRiYmFiNmIzYmFjNzYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjFlNWNkYjFkYTkzZGM5Y2Q4NjFhMDc4OWE2OGRiZmJkYTZkNTY5N2NiZGZhMzgzNGUyMDhjNTRmYTVkZTViYzEifQ" }, { - "resource": "repo://Frameworks/TermindRemoteInfrastructure/SSH/SSHForwardingConnectionPool.swift#L395-L438", - "version": "repo-lines-v1:sha256:97c269f3d0a7648b1af7e3efd9c63d878d03a199c854d67adb03deb5ddd15bbc:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjI4MjM5YTMxMTJiZWYyNjMzYTY4Yjg4Nzk1OWViYWZmNmY1ZjY3ODI3NzJlYTA1YTdmNjU0MzBjNGFjZTllOTUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjMzMTUwOTViMTU2YWFiYjc0YmI0Y2YwYWU5OWE3MzJmNDc1YWMyNjNlOTViNmIwZDNlNWZiOTJhZGQ0MmJiNTMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUxNGNiNjJmZjZiMjc5MDAzYjVhNzlkZTA2YmE0MTAxM2ZmY2FlZWE0M2EyZGViZjUxYTZkZTYwZjFlYjMzZDUifQ" + "resource": "repo://TFM/Features/Ports/PortForwardingView.swift#L191-L195", + "version": "repo-lines-v1:sha256:48c3e341d8f14de2af90f8de2940c8312d641a4075076963cfdf7bf36e516965:eyJzZWxlY3RlZExpbmVDb3VudCI6NSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMDlkMDExZWMzMWZkMDQ3NWNiYzQ5ZjZiMDBmODQ2MjQ2ZTM5MDgxMjI1OWMzZDUxNTBmOWQwNjNjNmFkMzUxYiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYWMwOGQwMWIwNmQ3ZjEwODAyZDdiN2VjODViODYwYjBkOTliMDk1ODRiYzdkZDdjNjE3OGU2Y2IyNWFjNTQ2MCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNTFjZjU5MDQ4ZWQzYjM5NjA4OWFmMmFkYWZmMzMyM2Y1MTMyMGE3NDhjMmMxNDkzMmJlYjdiZGY4MjhjYTlkZSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiYTg2MzNlMmJkZTNkMTAyOGMxOTI0ZDAyNjdlMmI5NzQ0NTIxZTFmYjY5YThkYzNiM2MxYTdkMmJmNDAwNjVjMyJ9" + }, + { + "resource": "repo://TFM/Features/Ports/PortForwardingView.swift#L325-L354", + "version": "repo-lines-v1:sha256:b9c6e49e6169ad5e8bf3f850fa3c86547484b995179ffcce3f755806008d6fd5:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ0NGZlYTcwYThiNTU2NjFlYTc1MmE1YTliNjU0MmIzOGQ4N2FhMjdiN2FjZjY4ZjRjOGIwNWUxZGUwOTBlYWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjY1MDUxZDAxMWQzMDEzNjkzYzhjN2E1NWRlZDFhN2NlMzFmNTRkMGEwODEwZTBlYjkwYTk5YTNmMGUwOTU2NGIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImIwNTU3MWU5ZDlhYzA3NDE3NzcyMjg2MjBlNWQ0NjZlOGMwMTk2OWY2NzZiZGVjMzg4MjVlMjJlYmMyNWM5OTAifQ" + }, + { + "resource": "repo://TFMTests/PortForwardingJourneyTests.swift#L21-L43", + "version": "repo-lines-v1:sha256:0c74bd6a94576675ec4622f4bf429bae63d039ce150018f1e3e82c98da86e5a8:eyJzZWxlY3RlZExpbmVDb3VudCI6MjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiOTM0NTU4ZjY5NjE1ZTYyOTlhMDAyODY0ZjMxOWQ3MTFlYjBmM2NmYWUxODBlZWJlM2I2ZDJhMjVlMTVjYzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjkwYTU3MjRkZmI4MTA0NmFmYjk1YTU0NjM4NTY5MWYxZTRhOGY1NzI2MjM5Njk3YjNmZTkyYmUwY2ZiNGM1ZTgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkZGEwZWJkNTYzYjYyYjBiNjZkZjgzNTU0M2JlYmIxMTM3Njc1ZTQyYjRkMTU5MzAzZmFjNTFlMTZhZTZjNzgifQ" } ] }, @@ -167,6 +175,6 @@ ], "verification": { "by": "openwiki/0.7.0", - "at": "2026-10-06T18:02:45.153Z" + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/workflows/settings-and-cloud-sync.json b/openwiki/.claims/workflows/settings-and-cloud-sync.json index 9a8a6fd5..1309e163 100644 --- a/openwiki/.claims/workflows/settings-and-cloud-sync.json +++ b/openwiki/.claims/workflows/settings-and-cloud-sync.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:72e9d588d53d2699eafa58cb89e24fd163bf96703009dbf0d10d1db4159b9e22", + "pageVersion": "sha256:0f3893b2da6ca60dfb4b08f1ffce1431377fb30611ad1879e669c7452a9c5459", "claims": [ { "id": "claim_dc6281a3ccb7415ea961d1a954248647", @@ -59,7 +59,7 @@ "version": "repo-lines-v1:sha256:a515847f09365651b525cf2fb9841991d7b7bc98faaeb63837b4dd843cbf6db4:eyJzZWxlY3RlZExpbmVDb3VudCI6MTUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjRjZDY0ZTIzNWJjNzZlYTM1MDI3NmE4MGZlMGM1NjM0ZTYwZDFkZmUwN2FjYWQ4NTJlMTFhMWQ2MjkzM2ViMTAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI5YmQ3Yzk5NmI4ZjRjZWZmMWI1MjIyZmNmNTRlMzVlMzQ3Y2Y0ZTU1NTBkNWE1OWI4MDQ2MGViNzI3NDIzMTgiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImMyMWFjNTE4NDc5MWIzNWNkZTM4MDkzNDU1MTVjMGFjMDEwZjA3MGVhZTAyMTRjZGY0MzIwZDE4NzI5N2M3MDEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjE2ZTkwYTMzMzY3YzE2MDc0ODQ4MGZlNzFlN2VhMWJlMGNjMTczNzUwODEzY2JmMjBjNDJhZDQ4N2M2ODMxNWEifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L444-L491", + "resource": "repo://TFM/App/AppModel.swift#L452-L499", "version": "repo-lines-v1:sha256:c92479faef57fe5ee7e7f7a65a7348e3ac5d60690eec7919ceb80d380e606dee:eyJzZWxlY3RlZExpbmVDb3VudCI6NDgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY3MDMyZjUzOTcxNDA0YjMxMjM2MmZkYmQ5MzlmOGRlNjYzMGRiNGVhNTQ1Mjk5MTUxNDVlMjNmY2RjNTc0MGMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdhNmVkNDJiNDliZDBjNGNkMDNiZmMwMzRkNzZiMzI2NjliZmYwOGYwOTE5OTRkMDUyNTIwODllYmEyNTEwNWQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlYWIxYTM2OTk4NGYxYTNmZTIxOWU5Y2MzZGI4YzY3MjI1OGU1ZGM3OGUxYTdlNmQ5OWE4MDcyYjA5Y2ZlMjkifQ" } ] @@ -380,7 +380,7 @@ }, { "resource": "repo://TFM/Features/Settings/SettingsPageView.swift", - "version": "repo-file-v1:sha256:1f9c2221300c6e82d4a539f738405066b0d50137bd597304b70a16e0fe2b0b71" + "version": "repo-file-v1:sha256:14ee756a3acf0aa5377e772147f5953d3d7bd79d251ef2523c6b1ddc8f56b784" }, { "resource": "repo://TFM/Features/Settings/SyncSettings.swift", @@ -564,15 +564,23 @@ }, { "id": "claim_7bfa7a4a69f340e691ded0257c754273", - "statement": "Desktop and mobile composition roots apply accepted remote Settings (including notifications), Metrics, History and Device snapshots to live app models; remote Vault Host route changes or removals trigger workspace, transfer and forwarding cleanup before replacing the Host snapshot and reconciling forwarding configuration.", + "statement": "Both platform composition roots apply committed remote Settings, Metrics, History and Device snapshots to live app models. Remote Vault changes are planned before Host projection replacement: removals close workspaces and clear activity, desktop removals cancel transfers/authentication and revoke tools, desktop route changes invoke willDeleteHost, and mobile route changes disconnect retained workspaces and cancel transfers. Both stop affected forwarding rules and reconcile configuration after replacement; mobile updates live activities.", "evidence": [ { - "resource": "repo://TFI/App/MobileAppModel.swift", - "version": "repo-file-v1:sha256:f83323dded23ec74be6130fa9c8ad66d188b5d0cce9cf7f82b226f4f9d58769c" + "resource": "repo://TFI/App/MobileAppModel.swift#L386-L435", + "version": "repo-lines-v1:sha256:f480718130f1300fec2c1c0a105af308167b7f67aa7b6cd91194551d413a895c:eyJzZWxlY3RlZExpbmVDb3VudCI6NTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlkMDNlMzMwOTU1NzJlNDFkMTQ3ODk0YTcyNzA3YTIyN2FkZDJjZjFhMjFiYjBkNzIyYTAxODI5YzAyMjZmMzkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjEyMThjZDYzYjI1OWYwMWRmZWNlNGQyNTE2NTIxMmZmMmY4NDk1YTEwOWNiNjlhY2RlMmEyMjcyNTQ2YjQyMjIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImJhOTRkMjgwYTVlYzQxYjU3ZmMyOTc2YWFlNmIzY2M4ZTBkNTYzMjBmZmQ1NGQ4ZmQzMWY3OTY1NTFkOTBhOGQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImVjMTc3ZWUzZWRmNmViMzhhY2Q0NGU0ZTFiNTRlNTViNmZjNzUwZjllMDc2OTMwOWZlZTY4NjI5MmVkODViNjQifQ" }, { - "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "resource": "repo://TFI/App/MobileAppModel.swift#L642-L665", + "version": "repo-lines-v1:sha256:88cc4e18980df8bcfb1ec0eb794bfdd8ecb6d8c35945df4922a540eb72ae4b4a:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA1NjE3OGRmODdiNmZhMGMxZWI3YjBlOWIzOWM0NDhjNzcyZmNjNTIxMzMwZWVjZTljMDIyZTAwMGUzNzZhNTEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjhjYWNjN2E0NzlkMjI5N2FiYTI4NWJlYWFlMThhZmI4ZWE3YmQzODEwMTkxNTA3MTIwYmRhNzVjNjBhYTBhZTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjFlNmVmMWExMzFiMWQ1MjlmNzE0MzhjMzM2NmQzZGMyNDQ4N2VjMjA4MDQ5OTAwODBlOGViNWJhYjNjYWNmOWUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJhY2QwYWVmNTU3ZmE3MzEzN2VlMmVmNGJmYjhjZDQyNzk3NTljNTVjYTRiMTdhZmIyZGI0ZDJmNGU5ZWMzNmIifQ" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L398-L457", + "version": "repo-lines-v1:sha256:5c7e4e6df0285089ac788890cb77e84c9728e0d49d84117caab119a55905036b:eyJzZWxlY3RlZExpbmVDb3VudCI6NjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjlkMDNlMzMwOTU1NzJlNDFkMTQ3ODk0YTcyNzA3YTIyN2FkZDJjZjFhMjFiYjBkNzIyYTAxODI5YzAyMjZmMzkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM0MzMwNDY4ZDEyZDFkYzQ0MjM2MWMwNTQzMjMxNTdkZmU5M2NkZTE0NDU0Yjk4NjRiYjNmYTRjMWU1MjVhNzUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImE3ZjdhYWQzY2U0ZWRlZTg1ZDJiOGYwYzA4Y2MwNTQ0M2VjM2YwN2IyMDA2YWQ3OTRkMGFlOGQwNWViNjQ0NzYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImNlOTg0NGZkMzRkMjA2NGE1Yjc1YTJmOWIyZjFmZGUwNmJjZDUwMTEwYWFmMTVlZjI1OGM4OGZkYzgwNGY3MTgifQ" + }, + { + "resource": "repo://TFM/App/AppModel.swift#L505-L534", + "version": "repo-lines-v1:sha256:8a69a369341c3afc72e8252166f850e9757e5739c81120d13b24e72af0ecb574:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM1MGQxYmQzNDkyNzgxNDM5ZWJjMjVmNGNkYzI2NTE2Y2QyZTc4YWRkOWYyZWIxNWJhOWFjMjcwNjI2MDIxOGIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRiNGFiOTgyMWQyZjk2ODA0YWJkNWM3ZDViYzNkMWQyYTQzZmE3MjY5NmUxMmQ2ZWRjYjExNmQ3OWFjZWJlOTIifQ" } ] }, @@ -724,7 +732,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, @@ -864,7 +872,7 @@ "evidence": [ { "resource": "repo://TFM/Features/Settings/SettingsPageView.swift", - "version": "repo-file-v1:sha256:1f9c2221300c6e82d4a539f738405066b0d50137bd597304b70a16e0fe2b0b71" + "version": "repo-file-v1:sha256:14ee756a3acf0aa5377e772147f5953d3d7bd79d251ef2523c6b1ddc8f56b784" }, { "resource": "repo://TFMTests/SettingsNavigationTests.swift", @@ -892,15 +900,19 @@ }, { "id": "claim_0bc2b987d6444fb0bc15dd2ee60457e5", - "statement": "UserDefaults keys are not themselves sync declarations: SettingsSyncSnapshot.recordPayloads journals AI configuration/roles, skills, web search, terminal, SSH, SFTP, workspace and notifications, excluding device-local typography/debug/review state and retired highlighting.", + "statement": "UserDefaults keys are not themselves sync declarations: SettingsSyncSnapshot journals AI configuration/roles, skills, web search, terminal, SSH, SFTP, workspace and notifications, excluding device-local typography/debug/review state and retired highlighting. The desktop language picker is also device-local: AppLanguage.store writes canonical appLanguage and explicit en, ja, zh-Hans or zh-Hant AppleLanguages overrides; Follow System stores system and removes AppleLanguages.", "evidence": [ { - "resource": "repo://Frameworks/TermindApplication/SettingsKey.swift", - "version": "repo-file-v1:sha256:d95804107f546eb4448dd0c8b85463fff7caad88ce6dcd4b73b53a10f262dd19" + "resource": "repo://Frameworks/TermindApplication/AppLanguage.swift#L67-L96", + "version": "repo-lines-v1:sha256:cff3e398ef077ea59403f74eb8eefeda0c19188a05d2ade0fdb4dad75a2e1d22:eyJzZWxlY3RlZExpbmVDb3VudCI6MzAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjZiN2VjMjUwZGZkZjllZDY5ZGU0ZjIwNDU0ZjdkMTZiNjQ3MThmMGZkYzY1ZGZhMTNmOWFhN2FlYWM5YzUzNGQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdmM2JiNGEwNDYyZWIyZmFhMmQ2ZTI1NmMzNWU3Mjc0YmY3MmNkNjFkNjIyNTc1OTdlNmEzY2ZlOTZhM2U0NjciLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjU0YzZlZDRjZDc5YjAzMGQ4ZDZjNDFlZGQ0OTdmNWJiNWE2YTdhYmQ0MTU1NWNhNWMzNDhjNjEyMGQyNzAyNWMifQ" }, { - "resource": "repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L9-L56", - "version": "repo-lines-v1:sha256:43b2001509e0a6fd41b714d06821f5d4da39e04f9658bd15b0260f421bb7457a:eyJzZWxlY3RlZExpbmVDb3VudCI6NDgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImVmYTc3NjU3OTJiMmE3MjlmOThjNzhlOGU4Y2VlMzMxZmNmMzdmODAzZDhlM2NiZTE2NTBhMWQwYTUzMDQzMjAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxMmNhMzQ1Y2NmNzViZjljMDgwNmJjZTY5NWJlOGRlODA4Yjc5OTg0MjUxYTdhNTRkMjAyY2Y2MTAxZGQ0NTEiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjAwNjRiZTUwOTJmZWViMDhlNThkYjBmNTMwZDVhZDI4ODhkMjM4OTg2NGNjYWRiM2QzNzNmYjgzZWE0N2RhNzUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImVmNTA1NzQ0ZjNkMWEyYjM1YTQ1NzdkMmFlOTdmNTA4Mzc3MGUxYWMxYWI5ZmQxMmE3ZjY1OTczZDUwYTY5ZmIifQ" + "resource": "repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L7-L55", + "version": "repo-lines-v1:sha256:4bf97fc192c6b535ab7b1b802dd0380e7ba5e9bda7720831a9ff679908efb284:eyJzZWxlY3RlZExpbmVDb3VudCI6NDksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFiNjM3ODA1YzE0M2NlYzE3YWQxOTY0ZmVhOWYxZGU5MGViNzgzODc1MDFjMjNmZjUzZTYxM2JkMDlmZjZiMzYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImVkZGI4NTc4MDBhNWQ3MjQ3ZmYyM2M1Y2Q1ZTUyYzU0N2JiZjRiYzM2NWI5YTc0MjY5OWVlMzNkYmIyODkyYzAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6Ijc0MDg4Y2ViMTQ1NWIzODJiZjViYTRiYmNhYmViOTI1ZTVmODUzNzU2ZjBmMDQ5Zjk2YjQ3MWY5YjZlMzJmMWUifQ" + }, + { + "resource": "repo://TFM/Features/Settings/SettingsPageView.swift#L403-L456", + "version": "repo-lines-v1:sha256:d3a6e7a4fb7c1aba55553455699157614377c287af24337766a477d64b412aaf:eyJzZWxlY3RlZExpbmVDb3VudCI6NTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjUzNzA0OGVmY2I2OTFkODE0YmUxYzY0M2Q0ZjBlYmE5ODcwNThkMmQ0YTExMGM2NjQzZDczNDMzOTRiYzFjNmUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MjJjNDJjZDE5YzZkMDNkODA4YzQ1MjcyMDU1MjgyNWI3ZWExYzViNDkxYjIxODUxMWNhYzc0MTVjZjlkOWIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjRmYmRiMWRjZGJkNTk4NGQxZjNjNTEwZDBjZjlhOWJjZDQxMjQ3ODg5M2JiNDAxODkwZTcxYWM0OWJkYTk3ZGEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImMyMzQ1MDExMjEyZjIyMmJmZTAyNjQwMGNiNjM4NjcwMjcwNjY3M2RhOTViMGNkZGJjMDdiY2QwNjI2MDgzOWEifQ" } ] }, @@ -990,7 +1002,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:28:49.393Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.claims/workflows/ssh-session-lifecycle.json b/openwiki/.claims/workflows/ssh-session-lifecycle.json index 04a6419e..ff4b52e0 100644 --- a/openwiki/.claims/workflows/ssh-session-lifecycle.json +++ b/openwiki/.claims/workflows/ssh-session-lifecycle.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "pageVersion": "sha256:ae1ca5ae23c1add6438e1bf6d4829d70e67b8a0823ccb72dfe8ecfb3263c8805", + "pageVersion": "sha256:deecd62571bf0f3aba05745e201fcb859cbe5e2c1be399be178b235d62300208", "claims": [ { "id": "claim_69a9de4ce918417c8a44c6c87c9f6564", @@ -158,11 +158,11 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "version": "repo-file-v1:sha256:70e07cf8eccc2a01eedb0fd4b9b58f117ebc24b62d26dacb0133a8ad6748bca8" } ] }, @@ -176,7 +176,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, @@ -224,7 +224,7 @@ }, { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" } ] }, @@ -240,15 +240,19 @@ }, { "id": "claim_35062280526e42febcba9b5983e964b8", - "statement": "Desktop termination checks pending external edits, closes window-owned sessions and app services, waits for Assistant/MCP and SFTP drains before closing Agent background commands and connections, and answers the native termination request after cleanup or a three-second fallback.", + "statement": "Desktop Quit checks open workspaces across registered nonclosed windows before rechecking pending external edits; either warning can veto before cleanup. Approved shutdown is idempotent, fences Assistant admission, retires window sessions and app services, waits for preparation/conversation, MCP and SFTP drains, then closes Agent background commands, connections and Tailscale; the native reply occurs exactly once after cleanup or a three-second fallback.", "evidence": [ { - "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "resource": "repo://TFM/App/AppModel.swift#L962-L1022", + "version": "repo-lines-v1:sha256:4f8f0f61f9b03bf5b6cfc96cddd4ae1820792080fa4b443c97d2cf2d6c7a78f2:eyJzZWxlY3RlZExpbmVDb3VudCI6NjEsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImRiNmE1OWU3Nzg2NmU1MGRiZDYxZjA5ZmQ1YWM2MjQwN2Q0ZGVlNjdkNGIyNTJhNDExYzRhZTEwZWNmMWQwNmMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImNkYmExMmRjNjgzMWEzMWU5NzdjMTMzMDU0NWZiMzZhMTFjYjE2MDQ5YjQ5ZGYwN2MzYjAyNjM4YjIwNzA2YzIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjUzODNlN2M2MmI5ZmI0ZGU3YjNjNTc3ZmFkNjNiOTYxMGRlZTcyNmI4NzljYzZjN2M5ZjYzNTljOTU5YWEwODQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImRkYjkxZWUwZThhZmIwMWVmNzYyMmM2ZTg0NzA2MDM2YmVmMzAwNTc5YWU1ZjcyYmQzOTViMmY3MjllNmRjOWUifQ" }, { - "resource": "repo://TFM/App/TermindApp.swift", - "version": "repo-file-v1:sha256:41a1df0076d9c73e1485dae94a9d032f2a2b7718c98b662d3dda1bbc08166abb" + "resource": "repo://TFM/App/TermindApp.swift#L467-L510", + "version": "repo-lines-v1:sha256:25d575a1909deae410a6954ec410050cf33584e768e20922597bab7bda1a69c8:eyJzZWxlY3RlZExpbmVDb3VudCI6NDQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIyMmJlMTBlOWQxYjNjMGE2OTYxNzZmMGYyMDQ3MGVhODMzN2FmYzI4MWZmMTIzYmNmMmRmMjkzMTNkMDIwMTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg2MmZlMmQ0YmFjM2JhZjAxN2NiYmY5MmFiMTcwZTQ0NWExMWQxNzYyODBkZDEyOTdjZmM2ZDQxZjI2Y2NlMGYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijc4NWYxZjhkMWY1ODkwZmNjMTE1ZWMwNWRlNGU3NzA1ODdiOWRjNDlkZDhlM2IxNTViYmUwYzI4NWI2ZWQyYmQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdlMjAyMDc0MmMzM2IxYjkxZWIyMjYxNmJmYTg4NWMxNjQ2NThlM2Y5YzAzZDNkODZhYzllMjgwMGRkYmNhMzcifQ" + }, + { + "resource": "repo://TFMTests/AppTerminationTests.swift", + "version": "repo-file-v1:sha256:61e14261971e275798d4cde5275e5c3e0dc0d5552beace53b81e1ed3842358eb" } ] }, @@ -304,15 +308,27 @@ }, { "id": "claim_794d08c35cdd4a7eaf7110c942621f13", - "statement": "Closing the last desktop window does not necessarily terminate the application: the metrics menu preference and running metrics govern whether it quits, independently of the window-owned workspace transport lifetime.", + "statement": "Native window close uses a delegate veto distinct from application Quit, respects the previous delegate, guards reentrant confirmation and bypasses the workspace warning for empty or closed models. Cancel preserves workspaces for retry; actual native closure idempotently retires only the owning window's sessions. Closing the last window does not necessarily terminate the application: the metrics menu preference and running metrics govern whether it quits independently of workspace transport lifetime.", "evidence": [ { "resource": "repo://TFM/App/TermindApp.swift#L398-L406", "version": "repo-lines-v1:sha256:72b0ea3d0ed1f3a746b2322cc7dfcf974afdc9cce02dda03d97e5862dea07ce9:eyJzZWxlY3RlZExpbmVDb3VudCI6OSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZjYmYxYTMzZmU5ZDUxYjBlZjQwNzYzMzljYzEwMGFlMjBlOGFiYzk2OTZlMzk4NjBhY2JmNmU1YWQ2ZTQ4ZiIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTZiMDQ0OTE0Njk3MjJlODM2M2E5NGU0MDQ0NTM4ZTAxM2EyYTIxN2UzNmIxZTU2OGJhNjBhY2I3YjRjOGYyZSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNDYwZjQzMzI5NTZmNTMwOGY0NTY4NDJjNzExOTFhOTAyZjBkZWY4OTQyNGQ5M2MwZTExYzBhMzZlMmM1Njg2MSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZjc3ZDhiZmVlYmRjZDdlZWJlYWRkYzZhMjNlNjNiN2JiOTQ2ODg0ZmMwZjUxZmEyNzE4NWRlMDYxMDMxMjg1MyJ9" }, { - "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1469-L1475", - "version": "repo-lines-v1:sha256:699fb77e40f568b08d8df5be2700b0f3f23df780ae892000e54e62c249b1c4e8:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMzkwY2NjNTIzMGVmMWE1NmExMmNhMmI2ODlkYjY2YjM1NDZlZjgxYTdjOGQ2NzQ0NmI4YjcxNDNjZGY2YjAzMyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiYjFlMmJjY2M1ZGZlOTMwMDI4M2NjOGUxNmU1OTBhMDAwNjVlZWY4OTk4YTMxZjQ4NzA0N2Q4ZDRkM2QyNDMxYyIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYzM1OWRkMDg4NDA1ZjhlZTkxOWRmNmQ2NjQ2YWFhNDE4NDY0ZTcyNzIxNTQyNTNhNmFmMWM4ZmY5NTgyOTVmMSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZjRhNjY4ZWYwMjFjMDFlNTYxZDNiMzgxMTczZjk2YWJmZTA1NmE2MzJjNWQ4ODI2MDdhZDg1YzhjNWQ2YWU1ZiJ9" + "resource": "repo://TFM/Features/Workspaces/WindowModel.swift#L1416-L1475", + "version": "repo-lines-v1:sha256:7fa73d5371c7b42c88f4887b92bd89f8737ee87507afbbe1a8c7c24b198eef0a:eyJzZWxlY3RlZExpbmVDb3VudCI6NjAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjJjYWZjOTA3ODNhMmViZGYxZTVkMjA5ZWNiYTI5M2M2NWI5ZDBmYTY2ZmJjN2Y4M2VjM2M5YzJkNTVhMjUxNTAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImIxZTJiY2NjNWRmZTkzMDAyODNjYzhlMTZlNTkwYTAwMDY1ZWVmODk5OGEzMWY0ODcwNDdkOGQ0ZDNkMjQzMWMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY4NDg1NDZhYzY0MDljOTA2MmY1NmFhOTZjMjljMWMyZmJhMGQ0YmQ4NTZlOTkyOGNhZjgxNTcyNzk3NmRiNzAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImY0YTY2OGVmMDIxYzAxZTU2MWQzYjM4MTE3M2Y5NmFiZmUwNTZhNjMyYzVkODgyNjA3YWQ4NWM4YzVkNmFlNWYifQ" + }, + { + "resource": "repo://TFM/Platform/MainWindowCoordinator.swift", + "version": "repo-file-v1:sha256:0166b9eab07357665f202205a39a6de25af79bfdda707632e8324cb33f129314" + }, + { + "resource": "repo://TFM/Platform/WorkspaceCloseConfirmation.swift", + "version": "repo-file-v1:sha256:3e201552edd7b4377a9c1cb93e05dc544f71395ef782586cda429c58ddd7f384" + }, + { + "resource": "repo://TFMTests/WorkspaceCloseConfirmationTests.swift", + "version": "repo-file-v1:sha256:ecaef1ced74bfac4055edc6c3496204a2f5e864932da272ab3c51363bfa6081c" } ] }, @@ -363,11 +379,11 @@ "version": "repo-lines-v1:sha256:3b0def59648e9eb2086c17450a9b285c81cdb38229aedf0dba80674b64c6394e:eyJzZWxlY3RlZExpbmVDb3VudCI6NjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjAwOTkyNWE4ZWRjNmVmZDAwMmY5MGU0N2ZmNTkwNjAxNzJkODEwOGQ1ZGQ0MTAyNjc1OGFkZWYzMTc5YmM4ZTIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjllMGZlOTg1Y2Y1NDhmMmRkODUwNmFlY2I0N2QzMjE1ODBiMzgxZmU3YzE5MjQyNWRlY2U0MTE3NWI0ZmEzMzEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJlNWE4YjFiMmM2MDZmZDQ1NjE1NjNhMmQ1MjFmMGE2NjY1ZmJhZWZlMzNiOGM3YjFiN2M1YjYwMmM0NDg2MWEifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L1250-L1378", + "resource": "repo://TFM/App/AppModel.swift#L1258-L1386", "version": "repo-lines-v1:sha256:50799fa405ab8264baa8109125faf2c1347bf9f127097f37974da625d84e75db:eyJzZWxlY3RlZExpbmVDb3VudCI6MTI5LCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJmMzE0MDM1MjUwZTQwMTY4Y2M0ZjkyZWViOWYyOTJhYjJkZjk1NzQwMmMyZDM5NjI1NTg1NjZkNWI5YWZkZGY1IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiI0ODkzZjQwZTM3ODFlYjg5NmM4MmQwNDZhMDgwNjdhMTFlMzVjZGYyZjFkZmFmMjZmZWE5MGI4ZGI2Njc2NDEzIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MywicHJlY2VkaW5nQ29udGV4dEhhc2giOiIzZDJkYmRkMjNhZmRkYzhlNGY1NDczNWFkOWZhMzM4YzI4M2IzOTIwZWU3NzVlMmUyNzZhMWIwNGM5YzFiYWY4IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MywiZm9sbG93aW5nQ29udGV4dEhhc2giOiI1YWI2YjUzZGI3NjdlYjRjNzA4ODFlM2RlYjAxM2I0ODJmYTMzNjdjYTNjYjZlYjBiODAyNTFlYWM4MjAzNjZlIn0" }, { - "resource": "repo://TFM/App/AppModel.swift#L1017-L1045", + "resource": "repo://TFM/App/AppModel.swift#L1025-L1053", "version": "repo-lines-v1:sha256:d1595462854c333817f28196642e2471279dccce5a8380f7471fe7a077f3b3f9:eyJzZWxlY3RlZExpbmVDb3VudCI6MjksImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE1MjY3ZDlhYTNmYWRmN2QxNjYzN2U4YjA0YTM3ZjI0YTE1MDAyMWJjMGQ4YTI0MjYxMTI1NDUzMTg0MzIyMzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4OTNmNDBlMzc4MWViODk2YzgyZDA0NmEwODA2N2ExMWUzNWNkZjJmMWRmYWYyNmZlYTkwYjhkYjY2NzY0MTMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImZhZTY0MWM2ZjE2Njk2ODRlNDUxOGYwYWVjZjNjNDQxZjYxOWFiODI1YWY3MTA0ZjE0MDc1NjQyOGUxOGIxMzkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjdhYTMwMThkNjllYjJmNDVjODVhNGUwNmIwNzI0NjYzODQ5NzhiZDhjZWZjYmQ0NzBlODk1Y2EwYzIyNzgxNGYifQ" } ] @@ -378,7 +394,7 @@ "evidence": [ { "resource": "repo://TFM/App/AppModel.swift", - "version": "repo-file-v1:sha256:42e025d10f660373b02cba5834271b9d07742b518eb381eb0ff513d21c099fff" + "version": "repo-file-v1:sha256:bd43f31a4ca2de3488045ee63d072fa8f2a03f8e6201b5f7acd712d20933c1e0" }, { "resource": "repo://TFM/Features/Workspaces/Pane.swift", @@ -687,7 +703,7 @@ "version": "repo-lines-v1:sha256:ab5c98840ca309c246aaef5b065b0f59aa7c965067e8b90f07f7eb931842cc59:eyJzZWxlY3RlZExpbmVDb3VudCI6NzYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjViYzdlODkxNTAwOTRhYjkzZTU0NDViOTI2OTg5OTNkMzhjNmY4YjQ4N2RlY2FkMDZlYzIwZDgyMGI0ZmQ4YWUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU3ZDYxMTFmY2M2ZWU1NjI2OWNlMmRmZTk4OGYxYTMxYjgyOWYxNmY2YjZmYjlhOWJmNWI3OTRkOTM2YmJlNjUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjY0NTg2YWUyNTc2ZmI3ZmE1NTQ1MmMwZTk4Y2JiN2ExODMyZWI4Y2ZlYjJmNDQ2NzdhMjZkZWQ2NDU5MzdiY2QiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ3Njg2M2MwN2I3NTVkMjczYmUyMjE3MTMzMzkwNzg0MTU4ZWJmNmU0ZTc3YmQ5OTU3ZGQ0MzFkNzAzYjcyYjkifQ" }, { - "resource": "repo://TFM/App/AppModel.swift#L1017-L1061", + "resource": "repo://TFM/App/AppModel.swift#L1025-L1069", "version": "repo-lines-v1:sha256:de3fb3804ac08842d7b1ffadc8e1b4a1fa41d8d82db7859dbc8ceaaacc774e99:eyJzZWxlY3RlZExpbmVDb3VudCI6NDUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE1MjY3ZDlhYTNmYWRmN2QxNjYzN2U4YjA0YTM3ZjI0YTE1MDAyMWJjMGQ4YTI0MjYxMTI1NDUzMTg0MzIyMzUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU5MDlhNTNhNjE4ZDAyOGUxZDMwMDBjMGE3YWJjZWVjYmU1ZmIzN2Y0ZTUxMWVmZTQ2NjQzZmFkNmU2Njg3MmYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImZhZTY0MWM2ZjE2Njk2ODRlNDUxOGYwYWVjZjNjNDQxZjYxOWFiODI1YWY3MTA0ZjE0MDc1NjQyOGUxOGIxMzkiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImVmM2MyYmU5Yzg4ODZjMmM1YzI4MjYzOTk4MTBmZTYyMDY0YWZhZWEyN2RmMTNmNzQ5OGI4M2QyMmVlODBjODkifQ" }, { @@ -744,12 +760,20 @@ }, { "id": "claim_b2cded9422b344f991dfef928b107c8c", - "statement": "Desktop SSH and SFTP share synchronous authentication continuation: resolve host by route/library/temporary precedence, trim and validate username, stage one-use submissions, clear the prompt and mark the journey connecting, then apply model-owned save policy and connect only on success; callbacks are never retained.", + "statement": "Desktop SSH and SFTP share synchronous controller authentication continuation with route/library/temporary host lookup and trimmed nonempty username validation. Identity submissions stage and resume before optional model-owned saving; proxy submissions save before staging and use the entered username; protected-key passphrases merge through passphraseSubmission. Only successful continuation connects, failed saves do not start another attempt, and callbacks are never retained.", "evidence": [ { "resource": "repo://Frameworks/TermindApplication/Connections/RemoteSessionAuthenticationContinuation.swift", "version": "repo-file-v1:sha256:fb404d95c65adc7bb08344b02dc37189264258bbc66f557b312938e20f7557d6" }, + { + "resource": "repo://TFM/Features/Terminal/SSHSessionModel.swift#L675-L708", + "version": "repo-lines-v1:sha256:4be75f1ae1342d50581b7a550610439feab20ed81bd4c3409f885d6188b159f5:eyJzZWxlY3RlZExpbmVDb3VudCI6MzQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjM4MmVhNWIyOTAyOTBjZmZkY2EyNjAyZmYxYTYwNzA2ZmE0YjE4Nzg3Y2E3NTQ4MDUwNTMzZWQ3OTYzMGM3YTgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU2YjA0NDkxNDY5NzIyZTgzNjNhOTRlNDA0NDUzOGUwMTNhMmEyMTdlMzZiMWU1NjhiYTYwYWNiN2I0YzhmMmUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6Ijk5OWJkZWZlZWFjYTYzNDBlMDI5MGRjODZlODg5Yzg5ZDVlMTg5YWE1YmE0NGI1ODViM2I3MDczNGY0YTYyZDgiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjQ4Yjk5ZGE2NGQ4ZWNmNGQ1N2NiMDA3NmMyYTI3NzM1NjY1ZDhlNWNiMzBjN2U3ZGE3ZmZmM2FiZGZkNzU3OWMifQ" + }, + { + "resource": "repo://TFMTests/DesktopAuthenticationSubmissionTests.swift", + "version": "repo-file-v1:sha256:34e9e90bcc75e1e98b12bae970fa6e0209eda1e86e12c454c02d5f4e4c19d4a9" + }, { "resource": "repo://TFMTests/RemoteSessionAuthenticationContinuationTests.swift", "version": "repo-file-v1:sha256:349329e984e8bdb5f7857a5436b29fd5bcfebcd553d7f56468fb3f8f8f6d9e68" @@ -772,7 +796,7 @@ } ], "verification": { - "by": "openwiki/0.7.1", - "at": "2026-10-08T13:46:25.102Z" + "by": "openwiki/0.7.0", + "at": "2026-10-10T23:08:14.218Z" } } diff --git a/openwiki/.last-update.json b/openwiki/.last-update.json index 11f430af..bcd13398 100644 --- a/openwiki/.last-update.json +++ b/openwiki/.last-update.json @@ -1,7 +1,7 @@ { - "updatedAt": "2026-10-09T00:18:38.437Z", + "updatedAt": "2026-10-10T23:56:22.439Z", "command": "update", - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", "model": "gpt-6.1-sol", "status": "complete", "language": "en" diff --git a/openwiki/.page-manifest.json b/openwiki/.page-manifest.json index 271d6956..a619a551 100644 --- a/openwiki/.page-manifest.json +++ b/openwiki/.page-manifest.json @@ -2,18 +2,18 @@ "schemaVersion": 1, "pages": { "/openwiki/architecture/desktop-ui.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:633662bc406c2f1cada88105b6b8554fa2b3eeae597a9c8fe35596966e1399ef", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:17272c6d1e7e71dddbd782cb168a96b94f69ad656911ba6e0a31d0f4c55ad75e", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/architecture/engineering-invariants.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:f781a4372018996ce5220a6c2cd1060320eae0b95a9bfe975bcbcc8811ad1609", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:7c497b5410b0b636d3960e0d37b238f16eb679a2e3513576c8764a3211da4707", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/architecture/mobile-ui.md": { "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", @@ -23,60 +23,60 @@ "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" }, "/openwiki/architecture/module-map.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:895556453e1d1159e77f9b5e3877d6bc4b8a7ea715f35b72d332478e66cc1dc9", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:79ee4201d94b6450f1485e22a3f95940db40a12347c05e4f1cd13a9db25b70b5", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/architecture/native-app-directories.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:4cf9745c57e23b170637ade78db1cfc5d66eaf6a77c614727911d4608c73b3b4", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:8c105fdaaedeb4598b200ee265c613034c4ec9808e0d71e89eb0df8f953c73a6", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/architecture/platform-runtime.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:c32f8234a8b6a9ced069d542dad15cecdb5d353f9e1c6034452f7ab82dbc5119", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:267e535cd563a3cf3299e79802f1bace5c06b2678a69296ef1178fd21434416c", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/architecture/shared-module-directories.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:6223dc5ca0ceea965a4e429192024c8206e2779298a056d39bd49a64e9f663af", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:78602505605cd93f6a858145bd424c0d2bbd85077f1ce2a1c581bc85f5190430", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/architecture/supporting-system-directories.md": { + "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", + "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", "pageVersion": "sha256:461e81326b943f4056452e95a67a0bf967f59c5afb090aca7d16043e9d071a21", "completedBy": "openwiki/0.7.0", - "completedRunId": "34d03cbd-7880-41d8-a35e-18bc20033939", - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14" + "completedRunId": "34d03cbd-7880-41d8-a35e-18bc20033939" }, "/openwiki/architecture/system-boundaries.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:9873df397941c0bfd1bd9ea5bb241a61d4e6b93f59df8afb75f807d871fe2341", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:b2b3051a7c3fda79b5fd3db1b349441735a2c20c9ac9ea99751270a074df3d40", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/concepts/agent-runtime-and-assistant.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:7da7643ceb02ed8cb75346346cd3224bf2dc34d7374f7045e79f760cf86957fb", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:7fe20e0875f4d5634a25749f89a26ddd5403e2c6a96515417a863d603cc459a0", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/concepts/commerce-localization-and-product-surfaces.md": { - "pageVersion": "sha256:bfcc767924aef1756c700892275c7dd731393511422da859052a8d6bd27080e0", + "pageVersion": "sha256:18191558f929a45782672980dc9d51c3ef65638d80380ec8454be69e54ebf44d", "completedBy": "openwiki/0.7.0", - "completedRunId": "34d03cbd-7880-41d8-a35e-18bc20033939", - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14" + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/concepts/ghostty-terminal-integration.md": { "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", @@ -86,11 +86,11 @@ "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" }, "/openwiki/concepts/persistence-and-secrets.md": { - "gitHead": "6d9bd9a3f6bd9b41329bd35d2b7cd9390e19b945", - "sourceFingerprint": "sha256:88d859e7254a0e5bd2ddcfe45650ca5e4ca2117ddc50f17db3b3170876658763", - "pageVersion": "sha256:be00dbc71a86d8b877773417c737dcd4b88e15560f038624c8dbece9b799a372", - "completedBy": "pi", - "completedRunId": "2927dd9c-e0f5-4bfb-949d-22ecc52108f5" + "pageVersion": "sha256:09cb18ddf2ce306d18ee2f24e6faa2655b8914e5afe47d045d58c817231510ec", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/concepts/remote-services-and-forwarding.md": { "gitHead": "6d9bd9a3f6bd9b41329bd35d2b7cd9390e19b945", @@ -121,18 +121,18 @@ "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" }, "/openwiki/concepts/ui-design-principles.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:5d2d18d31f2c7a1c6dab2916e7b91d3ae815773f66fe4b24bc577a3e54a928dc", - "completedBy": "pi", - "completedRunId": "6fec6ed1-d0a6-4652-b746-a55b8d259a7d" + "pageVersion": "sha256:4cf6a62fc6c355a004b9d390a8367264ee27fcdf63e68df111db19ccdfc0a303", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/integrations/mcp-server-and-bridge.md": { - "gitHead": "6d9bd9a3f6bd9b41329bd35d2b7cd9390e19b945", - "sourceFingerprint": "sha256:88d859e7254a0e5bd2ddcfe45650ca5e4ca2117ddc50f17db3b3170876658763", - "pageVersion": "sha256:c8eeb91fb7537a16680adc1ffdbc8f81ed369c6badc52b5cb20602c8642bc9f3", - "completedBy": "pi", - "completedRunId": "2927dd9c-e0f5-4bfb-949d-22ecc52108f5" + "pageVersion": "sha256:21e8cfac6118ff91704ee983ab1f286de7df0089fc2eebb0f1c857ee9b49f542", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/integrations/tailscale.md": { "gitHead": "6d9bd9a3f6bd9b41329bd35d2b7cd9390e19b945", @@ -142,11 +142,11 @@ "completedRunId": "48ef25f2-64e1-4190-834d-a609e0efb04d" }, "/openwiki/operations/build-test-and-release.md": { - "pageVersion": "sha256:70bd137630ed40d98fd9a13e4fd39b1db016e8aa7e131c550d7d8521a25bf340", + "pageVersion": "sha256:6bf29a856991f4cb7b59362b99986616b669e153eb464116930925ada8345c89", "completedBy": "openwiki/0.7.0", - "completedRunId": "34d03cbd-7880-41d8-a35e-18bc20033939", - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14" + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/operations/cloudkit-release-readiness.md": { "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", @@ -163,11 +163,11 @@ "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" }, "/openwiki/operations/marketing-assets-and-app-icons.md": { + "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", + "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", "pageVersion": "sha256:248e7f369c99e774ab2d5d0b4f59cb5e2b5f1f1a2d70e415f762d7d5bd07a871", "completedBy": "openwiki/0.7.0", - "completedRunId": "34d03cbd-7880-41d8-a35e-18bc20033939", - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14" + "completedRunId": "34d03cbd-7880-41d8-a35e-18bc20033939" }, "/openwiki/operations/port-forwarding-benchmarks.md": { "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", @@ -177,32 +177,32 @@ "completedRunId": "3ebe4178-ea04-4abb-8b86-1a3f6e359392" }, "/openwiki/operations/storefront-commerce-release.md": { - "pageVersion": "sha256:d5a99c50d2acfa0cfa618499e38eaae356213ee5fa1bbf6312cfa277e3083175", + "pageVersion": "sha256:3848f628519d8c85e0240922e071f7a55949bf7c729ae87a40dbce46ae82e740", "completedBy": "openwiki/0.7.0", - "completedRunId": "34d03cbd-7880-41d8-a35e-18bc20033939", - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14" + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/quickstart.md": { - "pageVersion": "sha256:e8204b43a111f591306e3848cd2cc998d1530faad8663a3ba3409116f5efb3fa", + "pageVersion": "sha256:7152669edf8f22db53fba7b05ffdab63be45e83a486e4c62ded216b10e656d2d", "completedBy": "openwiki/0.7.0", - "completedRunId": "34d03cbd-7880-41d8-a35e-18bc20033939", - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14" + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/testing/behavioral-boundaries-and-fixtures.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:467dd9cde36fbc898f77544e4e63b07bb479568e1e2ac2a9b4b74c909738007b", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:6803ca504dbafc122e8d9c9e31a8de437ebcf1e70c38bf244998b701874b5a22", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/workflows/desktop-drag-and-drop.md": { - "gitHead": "6d9bd9a3f6bd9b41329bd35d2b7cd9390e19b945", - "sourceFingerprint": "sha256:88d859e7254a0e5bd2ddcfe45650ca5e4ca2117ddc50f17db3b3170876658763", - "pageVersion": "sha256:efccd4e208efbf5ffeb58914325df9ee75620d3924a843bec8659e05bc60e35f", - "completedBy": "pi", - "completedRunId": "2927dd9c-e0f5-4bfb-949d-22ecc52108f5" + "pageVersion": "sha256:d24218c79590fcd98003d809565d14803bea7a36c217b18ab04c2fadb38a5b66", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/workflows/file-drag-and-drop.md": { "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", @@ -212,18 +212,18 @@ "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" }, "/openwiki/workflows/port-forwarding-pool-and-dispatch.md": { - "gitHead": "6d9bd9a3f6bd9b41329bd35d2b7cd9390e19b945", - "sourceFingerprint": "sha256:88d859e7254a0e5bd2ddcfe45650ca5e4ca2117ddc50f17db3b3170876658763", - "pageVersion": "sha256:5ee8772a0e33c456214bfb063fbb5dbb33c2dff0126593dd0f2ab23d78e35c93", - "completedBy": "pi", - "completedRunId": "2927dd9c-e0f5-4bfb-949d-22ecc52108f5" + "pageVersion": "sha256:402ee9807551c18684f8fabd21deb9197aa67a88199193965fe323b99246fab3", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/workflows/settings-and-cloud-sync.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:72e9d588d53d2699eafa58cb89e24fd163bf96703009dbf0d10d1db4159b9e22", - "completedBy": "pi", - "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" + "pageVersion": "sha256:0f3893b2da6ca60dfb4b08f1ffce1431377fb30611ad1879e669c7452a9c5459", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" }, "/openwiki/workflows/sftp-transfer-and-external-edit.md": { "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", @@ -233,11 +233,11 @@ "completedRunId": "bab0e06f-81a2-4327-8080-e849db32092c" }, "/openwiki/workflows/ssh-session-lifecycle.md": { - "gitHead": "20f67cdc0b0e88115415c20af2cda7e12f51dfa6", - "sourceFingerprint": "sha256:fdfe15e9e5a8fbec7c1714e4c633ff07f075729c5cc8288057b6d7740a4d4b14", - "pageVersion": "sha256:ae1ca5ae23c1add6438e1bf6d4829d70e67b8a0823ccb72dfe8ecfb3263c8805", - "completedBy": "pi", - "completedRunId": "6fec6ed1-d0a6-4652-b746-a55b8d259a7d" + "pageVersion": "sha256:deecd62571bf0f3aba05745e201fcb859cbe5e2c1be399be178b235d62300208", + "completedBy": "openwiki/0.7.0", + "completedRunId": "631e3242-d854-43dc-bf2f-63d25c17c643", + "gitHead": "3c47baabd38b0f7cd54299eb3878b9eb880be246", + "sourceFingerprint": "sha256:9f231eada935df73901e54f255cae982ddd61032412ea3c1c7dea1c460413f50" } } } diff --git a/openwiki/architecture/desktop-ui.md b/openwiki/architecture/desktop-ui.md index 20475242..2220671b 100644 --- a/openwiki/architecture/desktop-ui.md +++ b/openwiki/architecture/desktop-ui.md @@ -1,11 +1,11 @@ --- type: architecture guide -title: macOS UI and Workspace Ownership -description: How macOS window navigation, sidebar modes, workspace sessions and Vault editing routes divide responsibility between AppModel and WindowModel. +title: Desktop UI and Window Ownership +description: How desktop navigation and persistent workspace views divide app-wide and per-window state, and how native window registration, close vetoes and application Quit manage their lifetimes. tags: [macos, ui, navigation, workspaces, swiftui, appkit, vault] verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z sources: - id: openwiki-source-e09fb7bc8e7ca0579f6d446e resource: repo://Frameworks/TermindApplication/FeatureModels/DeviceDirectoryModel.swift @@ -79,10 +79,10 @@ sources: resource: repo://TFMTests/WorkspaceNavigatorRenameTests.swift - id: openwiki-source-a48980e81bab99fcd643df54 resource: repo://TFMTests/WorkspaceWindowMoveTests.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } --- -# macOS UI and Workspace Ownership +# Desktop UI and Window Ownership TFM uses SwiftUI for composition and AppKit-backed surfaces where native identity, focus, text handling or incremental rendering matters. Its central design is **one Navigator column and one persistent detail deck per window**, not a separate horizontal tab system for every feature. @@ -150,6 +150,47 @@ Scene-request handoff: the source keeps the session until the target's native wi A workspace move waits for native destination registration before changing ownership. Repeated requests to move the same workspace or pane from the same source reuse the pending scene request. If no scene factory exists, the request fails without removing the source workspace; closing the source before registration cancels its pending requests and disconnects its sessions, so a late destination cannot resurrect them. A pane moved to another window uses the same request mechanism: a split pane becomes a new workspace around the existing live pane, while a lone pane carries its original workspace and name. The detailed release, cancellation and animation rules are in [TFM Drag and Drop](../workflows/desktop-drag-and-drop.md). [Source: `TFM/Platform/MainWindowCoordinator.swift` and `TFM/Features/Workspaces/WindowModel.swift`.] +### Weak registration is discovery, not window ownership + +`RootView` retains its `WindowModel` in `@State`. `AppModel` registers models in weak boxes, and `MainWindowCoordinator.Entry` holds weak references to both the native `NSWindow` and its model. Pending move requests also hold their source weakly. These registries discover and route to living windows rather than keeping abandoned scenes alive. App registration is idempotent and refuses closed models; native registration prunes dead entries and avoids duplicate window/model pairs. `RootView.onAppear` and the native bridge can therefore both register safely. The bridge retains its close-delegate proxy and attaches it when it gains a native window; this is not a view-disappearance cleanup hook. + +### Native window close and application Quit have different scopes + +Workspaces are session owners, **not edited documents**. `MainWindowRegistrationView` observes `isDocumentEdited` and resets it to false on its own attached window, suppressing edited decoration without disabling close confirmation. Detaching removes that observation. Native close is owned by `WorkspaceWindowCloseDelegate.windowShouldClose`, not a SwiftUI dismissal confirmation dialog that could also participate in Quit. + +The proxy preserves the previous native delegate weakly, forwards other supported delegate selectors, and restores it when moving off that window. For close, it rejects reentrant confirmation, honors the forwarded delegate's veto first, then permits an empty or already-closed model without a workspace prompt. Otherwise it asks to close **this window**, even when Vault, Assistant, Metrics or Settings hides its open workspaces. Cancel preserves the model, sessions and close history and permits a later retry. Both window and application alerts make Cancel the Return-key default; only the explicitly chosen destructive second button approves the operation. + +Window commands use `performClose(nil)` rather than bypassing the native veto. `closeSelectionOrWindow` consumes the focused pane when a workspace is selected, including its last pane; only a later invocation with no selected workspace falls back to native window dismissal. `closeWindow` resolves only that model's registered native window. Close All Windows snapshots registered windows and calls `performClose` on each, so one veto does not prevent other approved windows from closing. Transfers remains an auxiliary close target, not a workspace model. + +```mermaid +sequenceDiagram + participant Caller as Close button or window command + participant Native as NSWindow + participant Proxy as WorkspaceWindowCloseDelegate + participant Prior as Forwarded delegate + participant Coordinator as MainWindowCoordinator + participant Model as WindowModel + Caller->>Native: performClose + Native->>Proxy: windowShouldClose + Proxy->>Prior: ask existing delegate if supported + alt Existing veto or confirmation canceled + Proxy-->>Native: false + Note over Native,Model: Window and sessions stay open + else Empty model or explicit close approval + Proxy-->>Native: true + Native->>Coordinator: willClose notification + Coordinator->>Coordinator: remove entry and source requests + Coordinator->>Model: unregister and closeForWindow + Model->>Model: record recovery then disconnect and clear state + end +``` + +Native window dismissal asks permission before the close notification triggers teardown. + +Once `willCloseNotification` arrives, the coordinator records layout, removes the native entry and requests sourced from that model, unregisters the model from `AppModel`, and calls idempotent `closeForWindow()`. That method records recoverable window descriptors before marking the model closed, disconnecting its workspaces, clearing their lifecycle routers, and releasing its workspace collection, drag/presentation state, transcript cache and toolbar focus callback. Other windows' sessions remain untouched. Ordinary destination switching is not this teardown path. + +**Quit** instead belongs to `AppDelegate.applicationShouldTerminate`. `AppModel.hasOpenWorkspaces` checks all live, nonclosed weakly registered models, including workspaces hidden by another destination. The delegate returns `.terminateLater` and asks one application-scoped workspace confirmation when needed; cancellation resets its waiting flag and replies false. Approval proceeds to recheck edited SFTP files and, if necessary, a separate pending-upload warning. Only after those gates does termination save the main-window fallback and begin app-wide shutdown, replying true when cleanup finishes or its three-second bound expires. This is separate from the per-window native veto. See [Platform Runtime](platform-runtime.md) for process shutdown and [SSH Session Lifecycle](../workflows/ssh-session-lifecycle.md) for transport teardown. + The Transfers window is an auxiliary app-owned window, not a workspace. Its open/key flags drive the View toggle and close-command routing: when Transfers is key, Close Window closes it and Close Workspace is disabled, preventing an old workspace focus value from closing a session. Its notifications do not update main-window size fallback. SSH and SFTP context actions can open a companion session at the source session’s current directory. Dispatch resolves the owning pane again by session ID rather than using whichever pane is now focused. A live SSH session with a known directory or a connected SFTP channel with a nonempty path must still resolve to a saved host; otherwise the action cannot open a companion. Current-workspace placement first selects and focuses that source pane. @@ -321,6 +362,7 @@ Before changing a UI owner, trace both what happens when its view disappears and | Change | Focused seams | | --- | --- | | Scene ownership, sizing, workspace moves, window close or return routes | `TermindApp`, `MainWindowCoordinator`, `MainWindowSizingTests` (stored fallback validation, source-size inheritance, restored frames, sidebar width handshake and visible full-screen behavior), `MultiWindowTests`, `WorkspaceWindowMoveTests` (live pane identity and history, lone-pane name, late-registration rejection), `WindowStateTests`, `SettingsNavigationTests`. | +| Native close veto versus Quit | `WorkspaceCloseConfirmationTests` pins scope-specific wording, Cancel as default, explicit destructive approval, all-window Quit detection, empty-window bypass, retry after veto and delegate forwarding/restoration. `MultiWindowTests` checks pinned-destination cancellation, one-pane-at-a-time close, per-window session isolation, edited-decoration suppression until detach, unregistered fallback safety and independent Close All vetoes. `AppTerminationTests` covers shutdown draining and bounded reply behavior. | | Sidebar modes, Navigator rename or layout drops | `WindowSidebarTests`, `WorkspaceNavigatorRenameTests` (visible-window), `WorkspaceNavigatorDropTests` (some visible-window cases), `WorkspaceLayoutDropTests`. | | Shared Assistant Archive state and surface-aware blank-area menus | `AssistantConversationMenuTests.archiveStateIsSharedWhileSidebarActionKeepsWorkspaceInFront` and `WindowStateTests` compact/full-screen navigation cases. | | Hidden workspace layout or drag geometry | `PersistentWorkspaceDeckLayoutTests`; split/pane interaction and broadcast tests. | diff --git a/openwiki/architecture/engineering-invariants.md b/openwiki/architecture/engineering-invariants.md index 5a3102b0..55bcde5f 100644 --- a/openwiki/architecture/engineering-invariants.md +++ b/openwiki/architecture/engineering-invariants.md @@ -216,8 +216,12 @@ sources: resource: repo://TFM/Platform/MainWindowCoordinator.swift - id: openwiki-source-385c953ef1c6bf095d1c1244 resource: repo://TFM/Platform/TerminalFontCatalog.swift + - id: openwiki-source-dad03c68d6b805a4170ffc02 + resource: repo://TFM/Platform/WorkspaceCloseConfirmation.swift - id: openwiki-source-29722107a3a9d938ca19ebb4 resource: repo://TFM/UI/Components/WorkspaceLayoutDragSource.swift + - id: openwiki-source-715de8d16be5a3d06ae915ad + resource: repo://TFMTests/AgentPersistenceTests.swift - id: openwiki-source-73c75477db0674192248aa00 resource: repo://TFMTests/AppTerminationTests.swift - id: openwiki-source-d3d065330c730839f5cc6112 @@ -230,10 +234,12 @@ sources: resource: repo://TFMTests/TerminalThemeTests.swift - id: openwiki-source-a08489338be00125d646c77e resource: repo://TFMTests/VisibleWindowTestTrait.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } + - id: openwiki-source-d798805795e92534f3b13250 + resource: repo://TFMTests/WorkspaceCloseConfirmationTests.swift +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- # Engineering invariants @@ -735,8 +741,12 @@ Preparation has distinct cancellation and failure-save phases; `stop()` requests - History edits validate before persistence and publish their live replacement only after truncation commits. The boundary is the selected user message in canonical run/message order, not its whole run: retain the same-run prefix and owned tool graph with a neutral completed summary, remove the suffix and later runs, and prune compactions by removed message IDs. Compacted display history still informs ownership - and opening-title preservation. The repository rejects an edit if the removed suffix contains tool messages or tool cards, or if the - selected user message has agent-message provenance; do not silently erase executed work or collaboration-origin turns. See [Persistent Data](../concepts/persistence-and-secrets.md) for graph cleanup and rollback. + and opening-title preservation. Editing **can remove executed tool history**: the transaction deletes suffix-owned invocation, + result and review rows while preserving prefix-owned work; legacy cards without recoverable source/call ownership are assigned + to the suffix at or after the edit boundary. The selected user message must still have no agent-message provenance, so + collaboration-origin turns cannot be edited. Editing history does not undo remote tool effects. Keep live and reopened graphs + aligned; `TFMTests/AgentPersistenceTests.swift` covers suffix-tool removal with and without transcript call provenance, + same-run prefix preservation, staging adoption and late-write suppression. See [Persistent Data](../concepts/persistence-and-secrets.md) for graph cleanup and rollback. - An edited replacement initially owns a staging run whose ID equals its message ID. The first real snapshot transaction adopts only that staged row and deletes the staging run; historical replay rows keep their owners. Live running, failed and interrupted projections use the same provenance and preserve the staging chronology lower bound. Retired run IDs reject late saves/events in the repository instance. @@ -843,13 +853,16 @@ RevenueCat is the production purchase/access adapter; the controller gates featu temporary trust clears on pause/removal/config change. Free cap atomically three cards including paused, never delete on downgrade; Pro unlimited. - Desktop status item/popover receives model explicitly; last window close retains app/monitors, Quit cancels. Sleep/inactive login independently suspend; resume only awake/active with Data Protection Keychain available. Native lifecycle stays in `TFM/Platform`. -- On macOS Quit, the native delegate first rechecks dirty remote edits and offers Cancel/Quit Anyway; confirmed termination - requests one idempotent `AppModel.shutdownForTermination()` task. It fences new Assistant admissions, cancels captured +- On macOS Quit, the native delegate first asks an **application-scoped Workspace confirmation** if any registered live window + owns Workspaces, including those hidden behind another destination. Refusal cancels termination before cleanup. This is distinct + from pane close and the native per-window close veto. It then rechecks dirty remote edits and offers Cancel/Quit Anyway; + only after both gates does confirmed termination request one idempotent `AppModel.shutdownForTermination()` task. It fences new Assistant admissions, cancels captured preparations and stops all stored conversations **before** awaiting saves/event drains. MCP and SFTP stop in parallel; Agent background commands and connections close after the Assistant drains. Windows, forwarding, metrics, speech, transfers and outbox are also retired. The delegate replies after cleanup or a three-second timeout, whichever wins, exactly once: the deadline bounds app termination, not a promise that every save has completed. See - `TFMTests/AppTerminationTests.swift` for blocked-save, admission-fence and timeout ordering cases. + `TFMTests/AppTerminationTests.swift` for blocked-save, admission-fence and timeout ordering cases, and + `TFMTests/WorkspaceCloseConfirmationTests.swift` for application/window scope and hidden Workspace checks. ## Settings replication diff --git a/openwiki/architecture/index.md b/openwiki/architecture/index.md index d6e4acfb..f46368ea 100644 --- a/openwiki/architecture/index.md +++ b/openwiki/architecture/index.md @@ -1,11 +1,11 @@ # Files -- [macOS UI and Workspace Ownership](desktop-ui.md) - How macOS window navigation, sidebar modes, workspace sessions and Vault editing routes divide responsibility between AppModel and WindowModel. +- [Desktop UI and Window Ownership](desktop-ui.md) - How desktop navigation and persistent workspace views divide app-wide and per-window state, and how native window registration, close vetoes and application Quit manage their lifetimes. - [Engineering Invariants](engineering-invariants.md) - Cross-cutting safety rules for dependency boundaries, state lifetimes, localization, secrets, commerce, persistence, and testing, with the scope of their regression checks. - [iOS and iPadOS UI and Scene Ownership](mobile-ui.md) - How iOS and iPadOS scenes own navigation and workspace routes while shared services own connection journeys and Metrics; covers native presentation continuity and key-window test boundaries. - [Module map and composition roots](module-map.md) - Maps Termind's native products, six shared framework dependency targets, composition lifetimes, supporting products and pinned external package inputs. -- [Native App Responsibilities](native-app-directories.md) - Ownership guide for macOS and iOS app composition, window and scene state, app-local adapters, native feature UI, and platform services. -- [Platform Startup and Runtime Composition](platform-runtime.md) - How macOS and iOS compose shared storage and runtime services, gate scene creation on local consent, and manage window, session, background and shutdown lifetimes. -- [Shared module ownership guide](shared-module-directories.md) - Ownership and public seams of Termind's six shared frameworks and product-bundled resources, from domain values and application ports to sync, remote protocols, replay and presentation. +- [Native App Ownership Reference](native-app-directories.md) - Ownership guide for macOS and iOS app composition, window and scene state, app-local adapters, native feature UI, and platform services. +- [Platform Runtime and Application Lifecycle](platform-runtime.md) - How macOS and iOS compose shared storage and runtime services, gate scene creation on local consent, and manage window, session, background and shutdown lifetimes. +- [Shared Module Ownership Reference](shared-module-directories.md) - Ownership and public seams of Termind's six shared frameworks and product-bundled resources, from domain values and application ports to sync, remote protocols, replay and presentation. - [Supporting Systems Ownership Guide](supporting-system-directories.md) - Ownership and integration boundaries for external Swift packages, the MCP bridge, Live Activities, tests, forwarding benchmarks, the Astro site, assets, and maintenance scripts. -- [System boundaries and dependency direction](system-boundaries.md) - Target dependency direction, enforced import scopes, and ownership of shared ports, persistence, external packages, and native app composition in Termind. +- [System Boundaries](system-boundaries.md) - Target dependency direction, enforced import scopes, and ownership of shared ports, persistence, external packages, and native app composition in Termind. diff --git a/openwiki/architecture/module-map.md b/openwiki/architecture/module-map.md index e461ccb4..2ea4c75a 100644 --- a/openwiki/architecture/module-map.md +++ b/openwiki/architecture/module-map.md @@ -14,14 +14,10 @@ sources: resource: repo://Frameworks/TermindApplication/Agent/AssistantExecutionController.swift - id: openwiki-source-e09fb7bc8e7ca0579f6d446e resource: repo://Frameworks/TermindApplication/FeatureModels/DeviceDirectoryModel.swift - - id: openwiki-source-6f388236eaa573c3c2ba7706 - resource: repo://Frameworks/TermindApplication/Repositories/DeviceDirectoryRepository.swift - id: openwiki-source-9b6177e37e7dd97d0cb1adee resource: repo://Frameworks/TermindApplication/Terminal/TerminalFindController.swift - id: openwiki-source-d593fb9c62c3736497a39486 resource: repo://Frameworks/TermindApplication/Transfers/RemoteFileSystem.swift - - id: openwiki-source-7367aa06bc3b7ce470e78113 - resource: repo://Frameworks/TermindDomain/DeviceRecord.swift - id: openwiki-source-e0d1a5c458c249d9d88ff665 resource: repo://Frameworks/TermindInfrastructure/Cloud/DeviceSyncCoordinator.swift - id: openwiki-source-894539337bc9140a109608f0 @@ -86,14 +82,18 @@ sources: resource: repo://TFM/Features/Terminal/SSHSessionModel.swift - id: openwiki-source-22b9fca540c3029c5ace451e resource: repo://TFM/Features/Workspaces/WindowModel.swift + - id: openwiki-source-68b5f4529ef349aac826b1d9 + resource: repo://TFM/Platform/MacDeviceName.swift - id: openwiki-source-d3d065330c730839f5cc6112 resource: repo://TFMTests/ArchitectureBoundaryTests.swift + - id: openwiki-source-1c9687bbe3333120d80ab3e8 + resource: repo://TFMTests/MacDeviceNameTests.swift - id: openwiki-source-0f8d0f9d08d6674698dd054c resource: repo://WorkspaceActivitySupport/WorkspaceActivityAttributes.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- # Module and Product Map @@ -146,6 +146,8 @@ flowchart TB Application --> Domain ``` +Direct dependencies among the six shared framework targets. + Both `TFM` and `TFI` directly depend on all six frameworks; `TermindDomain` has no shared-target dependency. These are explicit `PBXTargetDependency` edges, not inferred from directory proximity or imports. The diagram also explains why Persistence is not interchangeable with general Infrastructure: Infrastructure can use Persistence, while Persistence must not depend on Infrastructure or Remote Infrastructure. Shared frameworks never depend on an app target. `FrameworkTests` depends on all six shared frameworks, while `TFMTests` and `TFITests` depend on their respective app plus selected shared frameworks; the benchmark does not depend on either app or a test target. ## Product and package boundaries @@ -187,7 +189,7 @@ The MCP bridge translates stdio requests into authenticated HTTP calls to the de The Assistant is a useful change-placement example: `AssistantExecutionController` accepts an injected registry factory, authorization callback, run repository, and title generator; app-specific host services provide tools without making the Application framework construct SSH machinery. On mobile the app model wires `AgentHostServices.live` with its connection coordinator and SFTP scheduler. For speech it supplies `IOSSpeechAudioInput` to the shared transcription service and cancels speech on interruption. The remote-filesystem port similarly separates file policy from the SFTP transfer coordinator; Vault sync reads Persistence-owned journals rather than moving CloudKit policy into the app. -The device directory illustrates why target edges alone are insufficient: `DeviceDirectoryModel` registers the current installation on initialization, caches lookup results, and reports storage errors. The SwiftData repository refuses to resurrect a remotely deleted device during launch registration, but an explicit rename can upsert it; successful writes notify `DeviceSyncCoordinator` through History mutations. Remote application of device records invalidates the model's cache on both platforms. The device and connection-history coordinators share a CloudKit zone but keep separate kind-filtered engines and sync state; the device coordinator is independent of the Vault **record format and journal**, not exempt from the app's Pro/iCloud start/pause policy. See [System Boundaries](system-boundaries.md) for the sync contract. +The device directory illustrates why target edges alone are insufficient: native composition supplies the installation ID from `VaultSyncJournal` and acquires the display name outside the shared model. Desktop `TermindApp` delegates to the app-owned `MacDeviceName`, which prefers a trimmed, nonempty `kern.hostname` and falls back to `Device.current.safeDescription` when that hostname is unavailable or blank; mobile supplies `Device.current.safeDescription` directly. `MacDeviceNameTests` pins hostname priority and nil, empty, and whitespace fallback behavior. `DeviceDirectoryModel` registers the current installation on initialization, caches lookup results, and reports storage errors. The SwiftData repository refuses to resurrect a remotely deleted device during launch registration, but an explicit rename can upsert it; successful writes notify `DeviceSyncCoordinator` through History mutations. Remote application of device records invalidates the model's cache on both platforms. The device and connection-history coordinators share a CloudKit zone but keep separate kind-filtered engines and sync state; the device coordinator is independent of the Vault **record format and journal**, not exempt from the app's Pro/iCloud start/pause policy. See [System Boundaries](system-boundaries.md) for the sync contract. This split is also a placement rule: shared behavior belongs with its existing owner; native lifetime or platform API usage stays in the app. Similar names such as `Agent`, `Transfers`, `Terminal`, or `Metrics` in different modules do not imply duplicated responsibilities. Application owns `TerminalFindController` policy and state; native session/workspace owners inject binding actions and focus callbacks. Dismissal retains the query and can suppress focus restoration, while native resize can suspend engine search and later resume only a presented nonempty query. `MobileWorkspaceSearchModifier` owns the feature-level SSH/Telnet versus SFTP binding choice; the reusable UIKit search bridge does not. `TerminalFindControllerTests` covers retention, empty-query navigation, focus-free dismissal and resize suspension; native shortcut tests pin F3/Escape routing. diff --git a/openwiki/architecture/native-app-directories.md b/openwiki/architecture/native-app-directories.md index 9264b0c2..45247b84 100644 --- a/openwiki/architecture/native-app-directories.md +++ b/openwiki/architecture/native-app-directories.md @@ -1,9 +1,11 @@ --- type: architecture directory guide -title: Native App Responsibilities +title: Native App Ownership Reference description: Ownership guide for macOS and iOS app composition, window and scene state, app-local adapters, native feature UI, and platform services. tags: [architecture, macos, ios, directories, lifecycle] sources: + - id: openwiki-source-a51ca858a040a90d4ab9c1c4 + resource: repo://Frameworks/TermindApplication/AppLanguage.swift - id: openwiki-source-37bc639f87948ef96358b680 resource: repo://Frameworks/TermindInfrastructure/RecentlyClosedStore.swift - id: openwiki-source-c4b6e224fdbea6dcd393344d @@ -106,8 +108,6 @@ sources: resource: repo://TFM/Application/Serial/SerialPortService.swift - id: openwiki-source-d35453512ef488d0cacdc06e resource: repo://TFM/Features/Assistant/AssistantView.swift - - id: openwiki-source-391dd36408ff1c7fc7c1c335 - resource: repo://TFM/Features/Settings/MCPSettings.swift - id: openwiki-source-cfe2a6b22338023c37a5b503 resource: repo://TFM/Features/Settings/SettingsPageView.swift - id: openwiki-source-61a7b49f0fef771653753f14 @@ -136,8 +136,12 @@ sources: resource: repo://TFM/Platform/MainWindowCoordinator.swift - id: openwiki-source-824d6a1342361bda8b3634b7 resource: repo://TFM/Platform/MetricsMenuBarController.swift + - id: openwiki-source-dad03c68d6b805a4170ffc02 + resource: repo://TFM/Platform/WorkspaceCloseConfirmation.swift - id: openwiki-source-f6a967b4f14bf50d1af51b8b resource: repo://TFM/UI/Managed/ManagedCardPage.swift + - id: openwiki-source-793d7861000adc19f0c3df01 + resource: repo://TFMTests/AppLanguageTests.swift - id: openwiki-source-d3d065330c730839f5cc6112 resource: repo://TFMTests/ArchitectureBoundaryTests.swift - id: openwiki-source-0f45576cff16b6adfcaeefc0 @@ -150,16 +154,30 @@ sources: resource: repo://TFMTests/SettingsNavigationTests.swift - id: openwiki-source-ea148dc5de8cabfc00dc07ae resource: repo://TFMTests/WindowSidebarTests.swift -generated: { by: "pi", at: "2026-10-06T18:02:45.153Z" } + - id: openwiki-source-d798805795e92534f3b13250 + resource: repo://TFMTests/WorkspaceCloseConfirmationTests.swift +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- -# Native App Responsibilities +# Native App Ownership Reference `TFM` is the macOS app and `TFI` is the iPhone/iPad app. Both compose the [shared frameworks](shared-module-directories.md), but their directories own platform-specific interaction and lifetime decisions. A directory named `Features` is **not** necessarily views only: desktop sessions live under `Features/Terminal` and window workspaces under `Features/Workspaces`, while mobile connection runtimes live under `Features/Workspaces` and their **per-native-window owner** is `App/MobileWorkspaceSessionModel`. By contrast, `Features/Shell` defines navigation values, not remote shell execution. Paths below are relative to the respective app root. +## Task-to-owner quick reference + +| Coding task | Native owner and boundary | +| --- | --- | +| Change desktop startup or application Quit | `TFM/App/TermindApp.swift`: `TermindApp` composes shared state; `AppDelegate` owns application-wide confirmation and termination. | +| Change desktop window dismissal | `TFM/Platform/WorkspaceCloseConfirmation.swift`: `WorkspaceWindowCloseDelegate` vetoes native close; `MainWindowRegistrationView` attaches it. `MainWindowCoordinator` cleans up only after the window actually closes. | +| Change language selection | Desktop `Features/Settings/SettingsPageView.swift` persists through shared `AppLanguage.store` and displays a next-launch notice. `TermindApp` captures the launch locale; Settings does not own a relauncher. | +| Change desktop navigation or Settings return | `Features/Workspaces/WindowModel.swift` owns routes; `App/RootView.swift` mounts the persistent deck and destination overlays. | +| Move a running desktop workspace to another window | `Platform/MainWindowCoordinator.swift` requests the scene and transfers only on native registration. | +| Change mobile native-window ownership or restoration | `TFI/App/MobileWorkspaceSessionModel.swift`, `MobileAppModel` and `Platform/MobileWorkspaceSceneRouting.swift`, not the workspace list/detail views. | +| Change app-wide transfers or sync | App composition retains the services; feature screens command them without taking over their lifetime. See [Settings and Cloud Sync](../workflows/settings-and-cloud-sync.md). | + ## Entrypoints and state ownership | Boundary | macOS (`TFM`) | iOS/iPadOS (`TFI`) | @@ -197,7 +215,11 @@ App-level services outlive a window or scene's navigation selection; **both desk Settings is a **window destination**, not a separate shared-service owner. `WindowModel.settingsRoute` remembers the destination it replaced, moves Back to a parent route before closing, then resets to index and returns to the old destination (or Vault if a workspace was removed). `SettingsPageView` assembles forms over app-owned settings: remote MCP editing is Pro-gated and a separate local MCP server route configures the app's server controller, not the HTTP listener. Leaving an editor discards qualifying empty provider, MCP and skill drafts; substantive skill drafts survive, and a new MCP server must lock a unique namespace. This makes a route change different from shutting down sync, transfers or the local server. -`AppDelegate` handles application activation, reopening, quiet login launch and termination. On quit it rechecks pending SFTP edits, can cancel termination if the user refuses to discard them, then saves window size and begins shutdown. Its reply is sent once after cleanup or a three-second timeout. `TFMTests/SettingsNavigationTests.swift` tests route return, missing-workspace fallback and draft handling; `TFMTests/WindowSidebarTests.swift` tests window isolation and the serial-pane fallback. `TFMTests/MultiWindowTests.swift` covers move-on-registration and cancelled moves, `MainWindowSizingTests.swift` checks requested versus restored layout, `MetricsMenuNavigationTests.swift` checks the menu-to-window handoff, and `AppTerminationTests.swift` exercises shutdown ordering. +`AppDelegate` handles application activation, reopening, quiet login launch and termination. Window dismissal instead belongs to [`WorkspaceWindowCloseDelegate`](../../TFM/Platform/WorkspaceCloseConfirmation.swift), attached by `MainWindowRegistrationView`: it respects the existing delegate's veto, forwards other delegate methods and asks for confirmation only when that window still owns workspaces. Cancel preserves the model and live sessions. Window-close commands use `performClose` so the veto remains in the native path; `MainWindowCoordinator` unregisters and closes the model on `willClose`, not when confirmation is requested. Application Quit has a separate scope: `AppDelegate` first checks `app.hasOpenWorkspaces` across registered windows and asks the application-wide confirmation, then rechecks pending SFTP edits and can cancel again if the user refuses to discard them. Both workspace alerts default Return to Cancel and require the explicit destructive action. Once approved, Quit saves window size and begins shutdown; its reply is sent once after cleanup or a three-second timeout. + +Desktop language selection is persistence, not a shutdown action. `SettingsPageView.AppSettings` calls shared `AppLanguage.store`: it writes `SettingsKey.appLanguage`, sets `AppleLanguages` for an explicit language and removes that override for Follow System. `TermindApp` captures `launchLanguage` during initialization and supplies that locale to both its main and external-authentication scenes. The picker does not automatically quit or relaunch the app; when the selection differs from the Settings view's captured language, it displays the next-launch notice in the **selected** language. Change persistence in shared `AppLanguage`, the notice in Settings, and launch-locale injection in `TermindApp`. + +`TFMTests/SettingsNavigationTests.swift` tests route return, missing-workspace fallback and draft handling; `TFMTests/WindowSidebarTests.swift` tests window isolation and the serial-pane fallback. `TFMTests/MultiWindowTests.swift` covers move-on-registration and cancelled moves, `MainWindowSizingTests.swift` checks requested versus restored layout, `MetricsMenuNavigationTests.swift` checks the menu-to-window handoff, and `AppTerminationTests.swift` exercises shutdown ordering. `WorkspaceCloseConfirmationTests.swift` checks distinct window/Quit scopes, Cancel defaults, explicit approval, all-window workspace detection, veto preservation, delegate forwarding and empty-window dismissal. `AppLanguageTests.swift` checks persisted language tags, Follow System override removal and selected-language notice localization. ### Mobile navigation, restoration and lifecycle diff --git a/openwiki/architecture/platform-runtime.md b/openwiki/architecture/platform-runtime.md index f67b4aa2..7f8b3a58 100644 --- a/openwiki/architecture/platform-runtime.md +++ b/openwiki/architecture/platform-runtime.md @@ -1,9 +1,11 @@ --- type: architecture -title: Platform Startup and Runtime Composition +title: Platform Runtime and Application Lifecycle description: How macOS and iOS compose shared storage and runtime services, gate scene creation on local consent, and manage window, session, background and shutdown lifetimes. tags: [architecture, runtime, macos, ios, lifecycle, dependency-injection] sources: + - id: openwiki-source-5f99e8af445e073ffe9bc257 + resource: repo://Frameworks/TermindApplication/Agent/AssistantExecutionController.swift - id: openwiki-source-1ebc16ba1a0b0955956023b5 resource: repo://Frameworks/TermindApplication/Connections/TailscaleConnections.swift - id: openwiki-source-875da2c4f1040409e53757af @@ -86,6 +88,8 @@ sources: resource: repo://TFM/App/TermindApp.swift - id: openwiki-source-6a1a95f7d01cc8e15a397f6b resource: repo://TFM/App/TermindCommands.swift + - id: openwiki-source-cfe2a6b22338023c37a5b503 + resource: repo://TFM/Features/Settings/SettingsPageView.swift - id: openwiki-source-0a0c39623be1c4e870d0f538 resource: repo://TFM/Features/Settings/SyncSettings.swift - id: openwiki-source-ee09ece1fddff7d72a2b5cf4 @@ -96,6 +100,8 @@ sources: resource: repo://TFM/Platform/MacDeviceName.swift - id: openwiki-source-22e92f96394f292a731fc6fb resource: repo://TFM/Platform/MainWindowCoordinator.swift + - id: openwiki-source-dad03c68d6b805a4170ffc02 + resource: repo://TFM/Platform/WorkspaceCloseConfirmation.swift - id: openwiki-source-73c75477db0674192248aa00 resource: repo://TFMTests/AppTerminationTests.swift - id: openwiki-source-39248df19c48bb72235c6e8d @@ -110,13 +116,15 @@ sources: resource: repo://TFMTests/RepositoryOutboxTests.swift - id: openwiki-source-a08489338be00125d646c77e resource: repo://TFMTests/VisibleWindowTestTrait.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } + - id: openwiki-source-d798805795e92534f3b13250 + resource: repo://TFMTests/WorkspaceCloseConfirmationTests.swift verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } --- -# Platform Startup and Runtime Composition +# Platform Runtime and Application Lifecycle Both production entrypoints assemble a local diagnosis controller with installation-local consent and a reporter initially enabled **only** if consent was already granted. An unset choice gates the normal root UI, not container construction: the desktop `WindowGroup` shows `DiagnosisConsentView` instead of constructing `RootView`/`WindowModel`, and `MobileStartupView` shows that view instead of binding a scene session. Screenshots and default test assemblies use an inert controller without defaults or reporter, so they do not require a consent choice. On iOS, a failed model construction still takes the unavailable-store branch rather than showing consent. See [Persistence and Secrets](../concepts/persistence-and-secrets.md) for storage details. @@ -170,6 +178,8 @@ A useful consequence is that sharing a desktop `AppModel` does **not** make its `TermindPersistence.makeContainer` fixes the shared versioned SwiftData schema and migration plan for both platforms; the entrypoint chooses whether the container is memory-backed or file-backed and retains it, rather than defining separate platform model sets. `TermindApp.init()` is the desktop composition root. It sets up the scrubber web client and chooses the stored launch language (or a screenshot-demo language), then, **before opening the disk-backed store**, checks for another nonterminated app with the same bundle identifier. This protects against older installed versions that do not implement store locking. `TermindPersistence.makeContainer` independently acquires an exclusive, nonblocking `.lock` file lease **before SwiftData opens or migrates** the store. The container retains that lease for its lifetime; same-process containers for the canonical URL share it, and the lock file is not unlinked on close. Both an older running instance and a competing lock raise `PersistentStoreAccessError.inUse`: the app presents a database-in-use alert and exits after acknowledgment. Other container or preparation failures call `fatalError` instead of replacing production data with an empty store. The macOS guard and lock are skipped for in-memory launches; the iOS entrypoint instead captures startup failures as a `Result` and displays an unavailable-data screen. +The desktop root captures `launchLanguage` once during initialization and injects its locale into both main and external-authentication scenes. The Settings picker stores a new `AppLanguage` and shows a next-launch notice when it differs from the captured selection; it does **not** automatically quit or relaunch the application. The new selection applies on the next launch, rather than replacing the root's captured locale in place. See [Settings and Cloud Sync](../workflows/settings-and-cloud-sync.md). + After the container opens, the desktop entrypoint cleans previous context attachments, prepares settings and metrics persistence, makes the Keychain-backed secret store, and assembles `AppModel` with repositories, sync coordinators, logging, connection history, the device directory, and a common `RemoteConnectionCoordinator`. The directory uses the Vault journal's stable installation ID and `MacDeviceName.current`: the trimmed `kern.hostname`, falling back to DeviceKit's `Device.current.safeDescription` when unavailable or empty. **Debug test and screenshot launches still run the real app entrypoint, but not the normal disk-backed runtime.** XCTest detection uses `XCTestConfigurationFilePath` in DEBUG; it selects an in-memory container, a private metrics defaults suite, disabled file logging, unrestricted test Pro access, no Vault/History/Device/Metrics/Settings cloud coordinators, no local host defaults or production memory store, and a window size store without persisted defaults. A screenshot-demo configuration also uses an in-memory container but returns early with demo hosts, AI, metrics and onboarding instead of constructing the normal production services. Neither path is a fallback for a failed production database open: construction errors still fail startup. @@ -223,41 +233,63 @@ The model keeps only weak registrations for open `WindowModel`s. Registration in A host-opening action is centralized in `AppModel`, which chooses the requested protocol, but it opens into the caller's `WindowModel`. That window assigns one connection-history event to each saved-host SSH, Telnet or SFTP pane. Closing the pane/workspace ends the event before disconnecting, while moving a live pane preserves it and reopening creates a new event. External temporary and Serial panes are not recorded; a crash may leave `endedAt` absent. A batch can deliberately create separate window-local workspaces or one workspace with several panes. External SSH/SFTP/Telnet URLs likewise create a temporary workspace in the target window without adding a saved Host; Telnet URLs containing an advisory password require explicit confirmation first. -`TermindCommands` registers SwiftUI's `openWindow(id: "main", value:)` factory with `MainWindowCoordinator`. The coordinator assigns a request UUID and keeps layout and optional move information until the new native window is registered. `RootView` installs a `MainWindowRegistration` bridge in its background; its `NSView` waits for its native `NSWindow`, then registers the native window and per-scene model using native content/frame sizing. Registration disables native tabbing without imposing custom titlebar geometry. Registration injects app services, consumes the matching request and transfers a live workspace or pane only after the destination window exists. A restored scene has no pending request, so it retains its system-restored frame instead of inheriting the shared size fallback or replaying a move. The coordinator records normal window sizing at appropriate boundaries and unregisters and closes the model when its native window closes. Restoring a window thus does not replay a moved workspace or reopen its live connection. The AppKit delegate handles Dock reopen/new-window behavior and may retain the process after the last window according to menu-bar metrics configuration. +`TermindCommands` registers SwiftUI's `openWindow(id: "main", value:)` factory with `MainWindowCoordinator`. The coordinator assigns a request UUID and keeps layout and optional move information until the new native window is registered. `RootView` installs a `MainWindowRegistration` bridge in its background; its `NSView` waits for its native `NSWindow`, then registers the native window and per-scene model using native content/frame sizing. Registration disables native tabbing; the bridge establishes `.fullSizeContentView` and a transparent titlebar before the coordinator reads or applies sizing. Registration injects app services, consumes the matching request and transfers a live workspace or pane only after the destination window exists. A restored scene has no pending request, so it retains its system-restored frame instead of inheriting the shared size fallback or replaying a move. The coordinator records normal window sizing at appropriate boundaries and unregisters and closes the model when its native window closes. Restoring a window thus does not replay a moved workspace or reopen its live connection. The AppKit delegate handles Dock reopen/new-window behavior and may retain the process after the last window according to menu-bar metrics configuration. ### Desktop lifecycle and termination -The AppKit delegate starts native appearance/login handling and, outside tests, starts metrics activity and the metrics menu once `AppModel` is available. For quiet login launches it hides the normally constructed window rather than running window-less; a one-shot key-window observer and three-second disarm prevent a later user-opened window from being hidden. On app activation it refreshes both entitlement state and the current `DeviceRecord` display name from `MacDeviceName.current`; the synchronized directory can therefore update without rewriting connection events. TFI refreshes its name directly from DeviceKit's `Device.current.safeDescription` on active-scene transitions. On termination the desktop app first checks for pending external edits; after user confirmation, it saves window sizing and invokes `AppModel.shutdownForTermination()`. +The AppKit delegate starts native appearance/login handling and, outside tests, starts metrics activity and the metrics menu once `AppModel` is available. For quiet login launches it hides the normally constructed window rather than running window-less; a one-shot key-window observer and three-second disarm prevent a later user-opened window from being hidden. On app activation it refreshes both entitlement state and the current `DeviceRecord` display name from `MacDeviceName.current`; the synchronized directory can therefore update without rewriting connection events. TFI refreshes its name directly from DeviceKit's `Device.current.safeDescription` on active-scene transitions. On Quit, the delegate schedules confirmation in a main-actor task before any cleanup begins. -Shutdown is deliberately idempotent and bounded by the delegate. `applicationShouldTerminate` returns `.terminateLater` while it rechecks external SFTP edits; if pending changes exist and the user cancels the alert, it replies `false`, clears the pending-termination flag and does **not** start cleanup. An accepted quit saves the ordinary window size before cleanup. `shutdownForTermination()` immediately fences assistant admission, cancels pending run preparation, stops the app-wide conversation store (including conversations no longer shown in a registered window) and awaits terminal saves; it also suspends memory and edit monitoring, shuts down metrics and speech, closes registered windows, and cancels SFTP work, external authentication, forwarding and the outbox. It concurrently awaits MCP and SFTP cancellation, then closes agent background commands and connection pools **after** the assistant drains. Repeated calls return the same cleanup task. The delegate replies `true` to macOS when cleanup finishes or after a once-only three-second fallback; that bound applies to the quit reply, **not** a guarantee that every asynchronous drain finished. `applicationWillTerminate` repeats cleanup safely. +`applicationShouldTerminate` returns `.terminateNow` if no app model is attached. Otherwise it latches `awaitingTermination` and returns `.terminateLater`; duplicate requests while confirmation or cleanup is pending return `.terminateLater` without scheduling another task. The task first asks `WorkspaceCloseConfirmation.application` if any nonclosed registered window has workspaces (regardless of connection status). Only explicit destructive confirmation permits quitting: Cancel is the Return-key default. Next it awaits `sftpEdits.recheck()` and, if unuploaded changes remain, asks a separate SFTP quit-anyway question. Either veto replies `false`, resets the latch and leaves shutdown unstarted, so Quit can be retried. The three-second cleanup fallback does **not** bound these confirmation or recheck stages. See [SFTP Transfer and External Edit](../workflows/sftp-transfer-and-external-edit.md). + +An accepted quit saves normal window sizing before invoking `shutdownForTermination()`. Shutdown immediately fences assistant admission, stops notification observers/routing and clears the Dock badge. It cancels pending preparations and stops every conversation in the app-wide store before awaiting any of them, including conversations no longer displayed in a registered window. The preparation drain also joins captured failure persistence for conversations not yet stored. Memory and edit monitoring are suspended, metrics and speech are stopped, registered window models are closed, and SFTP transfers, external authentication, forwarding and the outbox are canceled/stopped. + +The retained cleanup task concurrently starts MCP stop and SFTP cancellation waits, joins preparation cancellation and conversation terminal persistence, and awaits both service drains. Only then does it close agent background commands, then agent connection pools, then shut down Tailscale. Repeated shutdown calls reuse that task rather than repeat the asynchronous resource teardown. `AppTerminationReplyCoordinator` races task completion against a three-second fallback and replies `true` exactly once. This bounds the native quit reply after accepted confirmation, **not** completion of every drain; `applicationWillTerminate` safely invokes shutdown again and stops native metrics/appearance observers. ```mermaid sequenceDiagram - participant Delegate as AppKit delegate + participant Native as AppKit + participant Delegate as AppDelegate + participant Edits as SFTP edit service participant Model as AppModel - participant Assistant as Conversation store + participant Assistant as Assistant execution and conversations participant Services as Runtime services - Delegate->>Delegate: Recheck pending external edits - alt Quit cancelled - Delegate-->>Delegate: Reply false without cleanup - else Quit accepted - Delegate->>Model: Save window size and start shutdown - Model->>Assistant: Fence admission and cancel preparations - Model->>Assistant: Stop stored conversations - Model->>Services: Close windows and stop runtime work - Model->>Assistant: Await terminal persistence - Model->>Services: Await MCP and SFTP cancellation - Model->>Services: Close agent commands then connection pools - alt Cleanup completes first - Model-->>Delegate: Cleanup task completes - else Three second fallback wins - Delegate-->>Delegate: Timeout reached + Native->>Delegate: applicationShouldTerminate + Delegate-->>Native: terminateLater and latch request + Note over Delegate: Duplicate requests return terminateLater only + Delegate->>Delegate: Schedule main-actor confirmation task + alt Open-workspace confirmation vetoed + Delegate-->>Native: Reset latch and reply false + else Workspace confirmation accepted or unnecessary + Delegate->>Edits: Await recheck + Edits-->>Delegate: Current pending changes + alt Pending edits and quit-anyway vetoed + Delegate-->>Native: Reset latch and reply false + else Quit accepted + Delegate->>Model: Save window size and start shutdown + Model->>Assistant: Fence admission and cancel preparations + Model->>Assistant: Stop all stored conversations + Model->>Services: Stop monitoring and close window models + Model->>Services: Begin MCP and SFTP drains + Model->>Assistant: Await preparation and terminal-save drains + Model->>Services: Await MCP and SFTP drains + Model->>Services: Close agent commands then pools then Tailscale + alt Cleanup completes first + Model-->>Delegate: Cleanup task completes + else Three-second fallback wins + Delegate->>Delegate: Fallback fires + end + Delegate-->>Native: Reply true once end - Delegate-->>Delegate: Reply true once end ``` -Desktop termination orders assistant draining before agent connection teardown, but the native quit reply can precede completion of that drain. +Quit confirmation precedes the SFTP recheck and bounded reply race; assistant persistence drains precede agent connection teardown, but the native reply may precede drain completion. + +### Native window-close bridge + +Closing one main window is a different scope from Quit. `MainWindowRegistrationView` attaches `WorkspaceWindowCloseDelegate` to the native window, preserves the previous delegate and forwards its other selectors. `windowShouldClose` honors the forwarded delegate's veto first, lets empty or already-closed models close without prompting, and asks the window-specific confirmation for populated models. Its reentrancy guard vetoes recursive close requests while the alert is showing. A veto preserves the workspaces and permits a later retry; detaching restores the original delegate. This native veto intentionally stays outside SwiftUI's dismissal dialog, which also participates in Quit. + +The same bridge observes `NSWindow.isDocumentEdited` with initial and new values and resets `true` to `false`: workspaces are not documents, so the edited titlebar decoration is suppressed **without removing the native close confirmation**. These are separate responsibilities, not a document-unsaved-state mechanism. ## Mobile composition @@ -380,7 +412,8 @@ Tests exercise the lifetime contracts rather than merely constructing views. For - `MobileHomeScreenQuickActionTests` covers recent-host shortcut selection, payload decoding and one-shot consumption by the scene delegate. - `DatabaseStartupGuardTests` checks that the macOS preflight permits the current process but refuses a second PID, including older versions without the lock. `PersistentStoreLockTests` checks refusal before open/migration, retention until all same-process containers close, release after forced exit without losing Vault data, and isolation of in-memory and separate stores. - `AppTestHostTests` checks default test-host window suppression, Dock-reopen refusal and survival after the last fixture window closes; `.requiresVisibleWindow` gates tests that actually show windows or take focus. -- `AppTerminationTests` verifies shutdown cancels stored and preparing assistant work, joins even an unstored captured failure before closing agent resources, fences new work, and replies once whether cleanup or the three-second fallback wins. +- `WorkspaceCloseConfirmationTests` verifies application-wide workspace detection, distinct Quit/window alert scopes, Cancel as the Return-key default, explicit destructive acceptance, empty-window bypass, forwarded native veto, retry without workspace loss and delegate restoration. These tests cover alert and native-window contracts; `AppTerminationTests` covers cleanup/reply ordering rather than driving the delegate's modal Quit alerts. +- `AppTerminationTests` verifies shutdown cancels stored and preparing assistant work, joins even an unstored captured failure before closing agent resources, fences new work, and replies once whether cleanup or the three-second fallback wins. See [Behavioral Boundaries and Fixtures](../testing/behavioral-boundaries-and-fixtures.md) for fixture policy. - `RepositoryOutboxTests` verifies a Host deletion request commits the hiding marker and complete outbox atomically. - `DockMenuTests` verifies Dock-created desktop windows receive distinct `WindowModel`s in the Welcome state and retain per-window UI state independently. - `ExternalConnectionURLTests` verifies external desktop connections are temporary and unsaved, including the Telnet-password confirmation boundary. diff --git a/openwiki/architecture/shared-module-directories.md b/openwiki/architecture/shared-module-directories.md index da43e152..bd376d65 100644 --- a/openwiki/architecture/shared-module-directories.md +++ b/openwiki/architecture/shared-module-directories.md @@ -1,6 +1,6 @@ --- type: architecture directory guide -title: Shared module ownership guide +title: Shared Module Ownership Reference description: Ownership and public seams of Termind's six shared frameworks and product-bundled resources, from domain values and application ports to sync, remote protocols, replay and presentation. tags: [architecture, directories, frameworks, domain, persistence, presentation, ssh] sources: @@ -18,12 +18,16 @@ sources: resource: repo://Frameworks/Resources/PrivacyInfo.xcprivacy - id: openwiki-source-0dc45e296e0ec3d99731a2f2 resource: repo://Frameworks/TermindApplication/Agent/AssistantChatCollaboration.swift + - id: openwiki-source-685b7f8e7dbcf91b4e2a50e2 + resource: repo://Frameworks/TermindApplication/Agent/AssistantConversationOperations.swift - id: openwiki-source-5f99e8af445e073ffe9bc257 resource: repo://Frameworks/TermindApplication/Agent/AssistantExecutionController.swift - id: openwiki-source-ce9687e1e441c1da9a8f908a resource: repo://Frameworks/TermindApplication/Agent/AssistantModelSelection.swift - id: openwiki-source-367221086eefa5586eade075 resource: repo://Frameworks/TermindApplication/Agent/AssistantNotifications.swift + - id: openwiki-source-1c48f72c0c7a3164d7d5c715 + resource: repo://Frameworks/TermindApplication/Agent/ConversationModel.swift - id: openwiki-source-cb178f0d078ab3218679c80e resource: repo://Frameworks/TermindApplication/Connections/RemoteSessionConnectionController.swift - id: openwiki-source-e09fb7bc8e7ca0579f6d446e @@ -150,6 +154,8 @@ sources: resource: repo://Frameworks/TermindRemoteInfrastructure/Transfers/SFTPTransferCoordinator.swift - id: openwiki-source-017f5e2abe9b13df755a5322 resource: repo://FrameworkTests/AgentReplayPersistenceTests.swift + - id: openwiki-source-247755d873c5d94b0a364217 + resource: repo://FrameworkTests/AssistantConversationOperationsTests.swift - id: openwiki-source-6ec3fb4cd6a582cdf278bab9 resource: repo://FrameworkTests/AssistantExecutionControllerTests.swift - id: openwiki-source-bf7a64e7c819eb9279e2f9e4 @@ -198,13 +204,13 @@ sources: resource: repo://TFM/Features/Terminal/SSHSessionModel.swift - id: openwiki-source-d3d065330c730839f5cc6112 resource: repo://TFMTests/ArchitectureBoundaryTests.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- -# Shared module ownership guide +# Shared Module Ownership Reference The six shared modules live under `Frameworks`. Directory placement expresses responsibility, but compiled target membership comes from Xcode's file-system-synchronized groups. `Frameworks/Resources` belongs to product and test bundles, **not** to every framework bundle. This is a **static build/bundle boundary**, distinct from runtime dependency injection: apps construct concrete services and pass them into Application ports; a framework's source directory does not imply that its resources live in that framework's bundle. AgentKit's loop, provider adapters, approval gate and host-independent built-ins are a separate remote Swift package. Xcode declares `https://github.com/AkinoKaede/AgentKit.git` with an up-to-next-major requirement starting at **0.19.1**, and `Package.resolved` currently pins **0.19.1**; the sibling `../AgentKit` checkout is for package development, **not** the tree Xcode builds. To ship a cross-repository change, test and release AgentKit first, then update the Xcode minimum and resolved revision together with the Termind-side integration; build with `-onlyUsePackageVersionsFromResolvedFile` to avoid silently advancing dependencies. See the [Module Map](module-map.md) for target dependencies, [System Boundaries](system-boundaries.md) for injection, and [Native App Directories](native-app-directories.md) for platform owners. @@ -262,6 +268,8 @@ sequenceDiagram end ``` +Assistant submission crosses Application capture and Persistence save boundaries before runtime execution or failure projection. + Submission and failure ordering: a rejected selection write stops before capture; saved failures precede observable failure. Collaboration adds an application-owned `AssistantChatCoordinator` and `AssistantChatCollaborationStoring` for created-chat relationships and delivery receipts. The SwiftData implementation deduplicates receipts by source run/tool call; the target message's provenance and queued receipt become delivered in the message-save transaction, not on an earlier in-memory runtime event. Application notification coordination consumes conversation interaction/completion events with injected preferences, focus and sender; consumed suppressed events are not replayed when focus changes. ## TermindPersistence — stored representations @@ -270,7 +278,17 @@ Persistence has **no functional subdirectories**. Its root owns the complete Swi On macOS, file-backed `makeContainer` acquires a per-store nonblocking advisory lock **before** SwiftData opens or migrates the database. Containers in the same process share a lease for canonicalized URLs until the last container closes; another process gets `PersistentStoreAccessError.inUse` instead of opening the store. The stable `.lock` file is not unlinked on release. In-memory containers do not acquire this lock; passing both `inMemory: true` and a URL violates a precondition. Keep container creation through this factory so journals and migration stay under the same store lifetime. -`SwiftDataAgentRunRepository` deduplicates snapshots by message ID across runs and saves frozen replay/context and attachment backing separately from ordinary transcript messages (compaction has its own rows). Restore orders by run and sequence, projects compaction and recovers persisted tool results missing from the last transcript snapshot. Construction marks unfinished runs interrupted. Editing truncates a suffix and stages a replacement user run for adoption by the real run without duplication; conversation deletion removes run/tool/message/replay backing and device-local model selection, and tombstones reject late saves. V42 also stores message origins; inserting an originated message advances a queued delivery receipt in the **same save**. Title writes redact model-influenced text. Application decides when to run, compact or display errors; Persistence decides what survives restart. +`SwiftDataAgentRunRepository` deduplicates snapshots by message ID across runs and saves frozen replay/context and attachment backing separately from ordinary transcript messages (compaction has its own rows). Restore orders by run and sequence, projects compaction and recovers persisted tool results missing from the last transcript snapshot. Construction marks unfinished runs interrupted. Editing truncates a suffix and stages a replacement user run; a real run adopts the existing message and its replay backing in one save rather than duplicating them, including failed or interrupted preparation and cancellation before runtime dispatch. `ConversationModel` replaces the corresponding staging summary and message-to-run ownership in its live projection. Conversation deletion removes run/tool/message/replay backing and device-local model selection, and repository-local tombstones reject late saves. V42 also stores message origins; inserting an originated message advances a queued delivery receipt in the **same save**. Title writes redact model-influenced text. Application decides when to run, compact or display errors; Persistence decides what survives restart. + +### Agent history changes: choose the owner by task + +| Task | Shared owner and regression seam | +| --- | --- | +| Commit an edited turn before changing live history | Application's `AssistantConversationOperations.edit` calls the injected repository's `truncateConversation` before `ConversationModel.applyPersistedUserMessageEdit`. `AssistantConversationOperationsTests` exercise conversation mutation policy. | +| Preserve replacement ownership and captured payload across restart | Persistence's `SwiftDataAgentRunRepository.save` adopts staging backing; Application's `ConversationModel` projects the real run. `AgentReplayPersistenceTests` cover failed/interrupted adoption, queued cancellation without a model call, and captured configuration failure, reloading from a fresh context to check one real run and retained text, attachments and frozen context. | +| Restore recorded results and older history | `AgentReplayPersistenceTests` cover frozen tool-result model projection, snapshot deduplication/deletion and V15 migration without invented historical context. | + +Keep replacement adoption and replay backing changes coordinated across Application and Persistence, not in native transcript views. For capture and cancellation control flow see [Assistant Runtime](../concepts/agent-runtime-and-assistant.md); for stored representations and transactional detail see [Persistent Data, Migrations, and Secrets](../concepts/persistence-and-secrets.md). `GRDBMemoryStore` is an actor with a separate lazily opened `memory.sqlite` and its own migrations for full-text message search, learning activity and memory-reference dates. Constructing disabled memory creates no database files. See [Persistent Data, Migrations, and Secrets](../concepts/persistence-and-secrets.md). diff --git a/openwiki/architecture/system-boundaries.md b/openwiki/architecture/system-boundaries.md index 28c0f31c..508c8a44 100644 --- a/openwiki/architecture/system-boundaries.md +++ b/openwiki/architecture/system-boundaries.md @@ -1,6 +1,6 @@ --- type: architecture boundaries -title: System boundaries and dependency direction +title: System Boundaries description: Target dependency direction, enforced import scopes, and ownership of shared ports, persistence, external packages, and native app composition in Termind. tags: [architecture, modularity, dependencies, persistence, agentkit, swift] sources: @@ -10,24 +10,16 @@ sources: resource: repo://Frameworks/TermindApplication/FeatureModels/AIConfigurationModel.swift - id: openwiki-source-e09fb7bc8e7ca0579f6d446e resource: repo://Frameworks/TermindApplication/FeatureModels/DeviceDirectoryModel.swift - - id: openwiki-source-9ee5ed32f6974f5906438e59 - resource: repo://Frameworks/TermindApplication/Repositories/ConnectionHistoryRepository.swift - id: openwiki-source-6f388236eaa573c3c2ba7706 resource: repo://Frameworks/TermindApplication/Repositories/DeviceDirectoryRepository.swift - id: openwiki-source-9b6177e37e7dd97d0cb1adee resource: repo://Frameworks/TermindApplication/Terminal/TerminalFindController.swift - - id: openwiki-source-e4f5ea9b30063cdc7d027956 - resource: repo://Frameworks/TermindDomain/Connections/ConnectionHistoryEntry.swift - - id: openwiki-source-7367aa06bc3b7ce470e78113 - resource: repo://Frameworks/TermindDomain/DeviceRecord.swift - id: openwiki-source-82f1da82c84bec6e60959357 resource: repo://Frameworks/TermindInfrastructure/Commerce/RevenueCatProPurchaseService.swift - id: openwiki-source-fe1dc993163bb9ff61397be7 resource: repo://Frameworks/TermindPersistence/HostPersistence.swift - id: openwiki-source-303e34b5782a7a14e28cc9f8 resource: repo://Frameworks/TermindPersistence/PersistentStoreLock.swift - - id: openwiki-source-4217f56e034013cb8704d525 - resource: repo://Frameworks/TermindPersistence/SwiftDataConnectionHistoryRepository.swift - id: openwiki-source-0b6ab59ec6a684de8101ff8e resource: repo://Frameworks/TermindPersistence/SwiftDataDeviceDirectoryRepository.swift - id: openwiki-source-7a9c6d90cba4ebd1d1398673 @@ -70,23 +62,25 @@ sources: resource: repo://TFITests/MobileShortcutTests.swift - id: openwiki-source-c52cd56852d12b2dee153082 resource: repo://TFM/App/AppDependencies.swift + - id: openwiki-source-4dbea0ff2808163cb4ecdd8f + resource: repo://TFM/App/AppModel.swift - id: openwiki-source-d5cca6ff91d9aaa5da323b40 resource: repo://TFM/App/DatabaseStartupGuard.swift - id: openwiki-source-6570374eaac2e0278fa84640 resource: repo://TFM/App/TermindApp.swift - - id: openwiki-source-68b5f4529ef349aac826b1d9 - resource: repo://TFM/Platform/MacDeviceName.swift + - id: openwiki-source-d35453512ef488d0cacdc06e + resource: repo://TFM/Features/Assistant/AssistantView.swift - id: openwiki-source-d3d065330c730839f5cc6112 resource: repo://TFMTests/ArchitectureBoundaryTests.swift - id: openwiki-source-c15cd5bef4646aaf64df407e resource: repo://TFMTests/ConnectionHistoryTests.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- -# System boundaries and dependency direction +# System Boundaries Termind is built as two native applications around six shared static frameworks. The boundary is intentional: reusable layers model policy and ports, infrastructure implements those ports, and `TFM` (macOS) and `TFI` (iOS/iPadOS) compose the result with native lifecycle and UI. A safe change starts by placing the responsibility with its owner rather than importing a convenient concrete dependency into a lower layer. @@ -179,6 +173,43 @@ The app targets are composition roots. `TermindApp` creates the desktop `ModelCo Do not use this as permission to move live state into the app-wide layer. Durable stores and cross-window coordinators are app-wide; desktop navigation and workspaces are window-owned; mobile navigation is scene-owned; live SSH/SFTP transports remain with the workspace/session that opened them. Refer to [Platform Runtime, App Composition, and Lifetimes](platform-runtime.md) for the detailed ownership map. +### Native confirmations versus shared operations + +Native confirmation is not shared execution policy. Desktop `AssistantView` presents the destructive-delete alert and delegates the accepted action through the window commands. `AppModel` calls shared `AssistantConversationOperations`, removes the conversation from window libraries and transcript caches only when deletion succeeds, and reports storage or scratch-cleanup errors to the windows. Application owns the mutation rules: archive and delete first cancel and join pending agent preparation, refuse a streaming or compacting conversation, and serialize mutations per conversation. Metadata, order, and edited transcript changes are persisted before they are applied to observable state. Delete commits through `AgentRunPersisting` before removing the AgentKit scratch scope; a scratch-removal failure returns successful deletion with a separate cleanup error rather than undoing durable deletion. + +Persistence owns the actual conversation graph deletion: `SwiftDataAgentRunRepository` removes runs, messages and their backing, tool results/invocations/reviews, conversation order, model selection, and compactions. Its in-memory deleted-conversation/run fences reject late snapshots and run events in that repository instance, preventing asynchronous writes from resurrecting deleted history. These are storage safeguards, not UI confirmation policy. + +Desktop quit likewise belongs to the native app. `AppDelegate.applicationShouldTerminate` returns `.terminateLater`, asks for workspace-close confirmation, rechecks edited SFTP files, and offers a separate cancel/quit-anyway alert for pending uploads. Only after acceptance does it save window size and begin `AppModel.shutdownForTermination()`. The model reuses one cleanup task, cancels pending assistant preparations and all stored conversations before awaiting their drains, and starts MCP shutdown and SFTP cancellation waiting concurrently. After preparation and conversation drains and those service waits finish, it closes agent background commands, then agent connections, then Tailscale. It also stops native notifications, window sessions, forwarding, metrics and other app services. Shared owners still implement cancellation, durable terminal saves, and resource closure; the app selects their ordering. + +```mermaid +sequenceDiagram + participant Native as AppDelegate + participant App as AppModel + participant Agent as Assistant execution and conversations + participant Services as MCP and SFTP services + participant Resources as Agent resources and Tailscale + participant Reply as AppTerminationReplyCoordinator + Native->>Native: Confirm workspaces and pending edits + Native->>App: shutdownForTermination + App->>Agent: Cancel preparation and stop all conversations + App->>Services: Start stop and cancellation waits + Native->>Reply: Race cleanup against three-second timeout + App->>Agent: Await preparation and conversation drains + App->>Services: Await service waits + App->>Resources: Close background commands then connections then Tailscale + Reply-->>Native: Reply once on cleanup or timeout +``` + +The native termination reply is bounded even when shared cleanup hangs: the first cleanup completion or default three-second timeout wins, and a late completion cannot reply twice. The timeout is a limit on waiting, not a guarantee that every durable save or resource close finished before process exit. + +### Authentication continuation versus feature policy + +`RemoteSessionConnectionController.continueAuthentication` centralizes the desktop SSH/SFTP continuation mechanics without retaining its synchronous callbacks. It resolves the prompt host and selected identity, trims the submitted username, stages SSH authentication, clears the prompt and resumes the journey, then invokes the feature's optional save policy before reconnecting. A submission or save failure calls the feature failure callback and does **not** start another attempt. Proxy authentication uses its own store operation before staging/resuming; passphrase failure keeps a prompt with the failure message. Desktop `SSHSessionModel` chooses whether to save a connection route or an individual host identity and how to reconstruct the error prompt. Thus shared continuation does not acquire native presentation or route-persistence policy. + +`ArchitectureBoundaryTests.forwardingAndMobileAuthenticationRemainFeatureOwned` enforces a narrower, complementary contract: forwarding must not read `UserDefaults`, `SettingsKey`, or `ExternalAuthentication`, must use `SessionReconnectController` and enforce `.forbidden` interaction, and selected mobile app/root/session composition files must not own `SSHKeyboardInteractiveQueue` or `keyboardAuthentication`. Additional connection owners retain `SSHAdditionalConnectionSnapshot` references rather than putting `SSHConnectionConfiguration` into the transfer context. These checks do not prohibit all feature-level infrastructure imports; they protect selected policy and credential-ownership seams. See [SSH Session Lifecycle](../workflows/ssh-session-lifecycle.md) for connection-level flow and [Engineering Invariants](engineering-invariants.md) for change constraints. + +Focused regressions are `TFMTests/AppTerminationTests.swift` (all-conversation cancellation, drain-before-close ordering, one-time cleanup, assistant admission fencing, and timeout reply), `TFMTests/RemoteSessionAuthenticationContinuationTests.swift` (stage/save/connect ordering and failed-save suppression), and `FrameworkTests/AssistantConversationOperationsTests.swift` (active-run/compaction guards and storage-failure preservation of visible state). The shutdown tests use injected `AppShutdownDependencies` rather than requiring live transports. + ### Mobile Find: policy, presentation, and feature binding The mobile Find path illustrates the same boundary without moving UIKit into shared code: diff --git a/openwiki/concepts/agent-runtime-and-assistant.md b/openwiki/concepts/agent-runtime-and-assistant.md index d1bd970f..3e6c5846 100644 --- a/openwiki/concepts/agent-runtime-and-assistant.md +++ b/openwiki/concepts/agent-runtime-and-assistant.md @@ -1,6 +1,6 @@ --- type: assistant runtime concept -title: Agent runtime and assistant ownership +title: Agent Runtime and Assistant description: How AgentKit owns the neutral runtime while Termind assembles assistant execution, host tools, persistence, and UI across macOS and iOS. tags: [assistant, agent-runtime, approval, persistence, skills, memory, ssh, sftp] sources: @@ -112,13 +112,13 @@ sources: resource: repo://TFMTests/MCPServerToolsTests.swift - id: openwiki-source-5a540932657f4174b82cb1eb resource: repo://TFMTests/TerminalSuggestToolTests.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- -# Agent runtime and assistant ownership +# Agent Runtime and Assistant Termind's Assistant is a durable conversation product built around a host-neutral agent loop. **AgentKit**, a separate Swift package, owns the model-provider adapters, run loop, context pipeline, approval gate, MCP client, generic built-in tools, secret broker, and generic scratch/skill/memory tool interfaces. **Termind** owns the app/device integration: SSH and SFTP tools and transport pools, live pane-inspection tools, Vault-backed host resolution, application policy and prompts, SwiftData conversation persistence, Hermes memory lifecycle, and the UI that renders events and collects approvals or SSH challenges. Neither the macOS nor the iOS app target implements the AgentKit provider loop: both assemble its shared runtime through `AssistantExecutionController` and `AssistantToolRegistry`. @@ -276,6 +276,29 @@ For an already-started runtime, stopping requests runtime cancellation, marks ru The queued-runtime handoff has its own cancellation persistence path. `ConversationModel.start` installs an owner before scheduling its event consumer; if stop wins while that owner is still queued, it constructs a cancelled snapshot from captured request data, starts a separate repository save and `stopAndWait()` joins the consumer that awaits it. If the consumer has entered `AgentRuntime.start`, runtime cancellation and its terminal snapshot path take over. Thus a queued stop need not create a *running* snapshot or call the provider, but a successful queued-cancellation save is durable; the presentation may show cancellation before the save finishes, and the queued save currently swallows repository errors. Do not confuse this handoff with preparation interruption, which is an earlier, separately saved state. +```mermaid +flowchart TD + Stop["Stop or stopAndWait"] --> Prep{"Preparation phase"} + Prep -->|Ordinary| Invalidate["Invalidate capture and cancel tracked work"] + Invalidate --> Join["Join history and preparation tasks"] + Join --> Save["Save interruption and recheck identity"] + Prep -->|Saving failure| Failure["Join existing failed save"] + Stop --> Owner{"Runtime owner phase"} + Owner -->|Queued| Queued["Save captured cancelled snapshot"] + Owner -->|Starting or started| Runtime["Request AgentRuntime cancellation"] + Stop --> Present["Settle presentation and return human steering"] + Queued --> Consumer["Join retained event consumer"] + Runtime --> Consumer +``` + +Cancellation has separate preparation and runtime owners: `stop` requests cancellation and settles presentation, while `stopAndWait` also joins their tracked work. + +### Context estimates and reported usage + +`contextEstimate` is an incremental gauge, not a provider billing ledger. `usage` holds the newest turn's provider report rather than cumulative conversation spend; it is not persisted, so reopened conversations start with estimates until another report arrives. Without measured usage it estimates system prompt, captured tool schemas, conversation text, tool results and attachments. Attachment chips share the estimator's text divisor; images use a tile approximation. Provider usage supplies a measured total (`inputTokens + outputTokens`), but the category breakdown is still an estimate rescaled to sum to that total—not provider-reported attribution per category. Cached input is already part of the normalized input total, not an extra amount to add. Provider regressions cover empty-choice/candidate usage chunks, Anthropic cache-read/cache-creation normalization and cumulative-field merging by maxima rather than summation. + +The conversation records schemas once during assembly instead of rebuilding a registry on each keystroke. Draft-text edits do not rebuild or change the history estimate; attachments do update it. Raw text and reasoning deltas become visible immediately while context-gauge publication is throttled, with final message events publishing the settled estimate. History editing clears stale measured usage and recomputes for the retained prefix and replacement. A context-window share is unavailable when the window is unknown and clamped when overfull. See the token and composer regressions below before changing this accounting. + ## Approval, secrets, and trust boundaries Approval is evaluated after schema validation and tool preflight, before execution. Invalid arguments never reach the approval UI or the tool. The policy layer is stronger than permission mode: @@ -380,7 +403,9 @@ Creation relations are a separate durable sidecar from individual message origin ### Message-boundary edits and replacement provenance -`AssistantConversationOperations.edit` validates an ordinary replacement user message before persistence and applies the live mutation only after truncation succeeds. Editing is refused if the removed suffix contains any tool call, result or tool lifecycle card (even a proposed, denied or read-only call); a compacted visible suffix and persisted invocation rows are checked too. A retained prefix can keep its own tool graph. This prevents rewriting history after an action was attempted. SwiftData checks cancellation before its synchronous transaction and uses the same canonical order for restore and truncation: runs by start time then UUID, messages by run order then sequence, timestamp, role and UUID. The selected message and everything after it are removed, not automatically its entire run. For `opening → assistant → steering → assistant`, editing the steering turn retains the opening and preceding assistant/tool batch. The retained partial run becomes a neutral `.completed` summary with no old failure and ends at its last retained non-tool message; wholly later runs, including empty ones, are removed. +`AssistantConversationOperations.edit` validates an ordinary replacement user message before persistence and applies the live mutation only after truncation succeeds. An editable target must still be a non-compaction user row in the model-facing transcript with `origin == nil`; streaming, compaction, archive, history mutation and captured-failure persistence block editing. Ordinary preparation is deliberately supersedable. Origin-bearing cross-chat messages are not human-authored turns and cannot be edited, even though their transcript role is `.user`; SwiftData also rejects an origin-backed target independently of the UI guard. + +**Tool activity in the removed suffix does not prevent editing.** Calls, results and cards are removed even for proposed, denied, read-only or completed tools; retained prefix tools keep their graph. This is a history replacement, not an undo of an executed SSH command, transfer or other external effect. SwiftData checks cancellation before its synchronous transaction and uses the same canonical order for restore and truncation: runs by start time then UUID, messages by run order then sequence, timestamp, role and UUID. The selected message and everything after it are removed, not automatically its entire run. For `opening → assistant → steering → assistant`, editing the steering turn retains the opening and preceding assistant/tool batch. The retained partial run becomes a neutral `.completed` summary with no old failure and ends at its last retained non-tool message; wholly later runs, including empty ones, are removed. Tool cleanup follows ownership before time. Known source-message and run-local call identities distinguish retained prefix tools from removed suffix tools, with results and reviews deleted alongside removed invocations. Restore infers missing legacy source IDs from the raw assistant call grammar; deterministic recovered tool-result IDs receive run ownership. The live projection additionally uses retained result ownership and the full visible prefix when compaction has hidden raw messages from the model transcript. Only invocations/cards without recoverable ownership fall back to `createdAt >= target.createdAt`, conservatively including the exact cutoff. This includes legacy rows and modern provider-native events persisted before their source message reaches a snapshot. Compaction records are removed only when their explicit boundary message is removed; nil-boundary fork bases survive, and live compaction marks are rebuilt. Editing after a retained opening does not clear its title, even when that opening is compacted out of the model transcript; live plans at or after the cutoff are removed. @@ -388,6 +413,39 @@ The replacement is durable before a runtime exists: its message ID also identifi `ConversationModel` mirrors adoption on `runStarted`, successfully saved captured failure, preparation interruption and queued prestart cancellation; `runFinished` retains the adjusted chronology. A committed failed, interrupted or prestart-cancelled replacement therefore has the real run's provenance rather than an abandoned staging summary. Preparation cancellation awaits the interrupted save and rechecks identity before adopting. Missing-model/credential failure instead holds preparation in a `savingFailure` phase: stop, archive and delete wait for its save; it calls `recordFailedRun` only after save success and retires preparation after success or error. A failed save leaves the staging owner intact and shows an error rather than claiming a durable failed run. The queued prestart path projects cancellation before its save completes; use `stopAndWait()` to join it. The persist-before-live contract above specifically concerns the history mutation boundary, not every transient presentation update. +```mermaid +sequenceDiagram + participant UI as Native Assistant UI + participant Ctrl as Execution controller + participant Ops as Conversation operations + participant Repo as SwiftData repository + participant Live as Conversation projection + UI->>Ctrl: prepareAgentEdit synchronously + Ctrl->>Ctrl: preserve authored suffix and attachments + Ctrl->>Ctrl: capture fresh context and configuration + UI->>Ctrl: performAgentEdit in UI task + Ctrl->>Ctrl: check capture identity + Ctrl->>Ops: tracked history edit + Ops->>Repo: truncate selected user boundary + alt transaction fails + Repo->>Repo: rollback and restore retired IDs + Repo-->>Ctrl: error without live history mutation + else commit succeeds + Repo->>Repo: keep prefix and stage replacement + Repo-->>Ops: committed + Ops->>Live: apply persisted replacement + Ctrl->>Ctrl: recheck identity before preparation + opt current capture produces a real snapshot + Ctrl->>Repo: runtime or preparation terminal snapshot + Repo->>Repo: adopt staged row and retire staging ID + end + end + UI->>Repo: reopen conversation later + Repo-->>Live: restore replay backing and owned tool results +``` + +Edit/replay boundary: durable truncation precedes the live replacement, and the real run adopts the staged row without duplicating retained history. + The repository instance retires the edited target run, removed later runs and adopted staging run so late snapshots or durable tool events cannot resurrect the discarded suffix. Truncation and snapshot save roll back SwiftData changes and restore the previous in-memory retired-ID set on error. These rules use the existing schema and Termind's Application/Persistence adapters, without changing AgentKit or its package pin. See [Persistent Data](persistence-and-secrets.md) for the storage contract. ### Restore and metadata @@ -447,6 +505,7 @@ New submissions capture whether they have a memory-session identity before prepa The focused tests establish behavior that is important when changing this area: - `TFMTests/AgentRuntimeTests.swift` covers schema-before-approval/execution, approval mode routing, Guardian timeout/failure fail-closed behavior, single-use secret handles, steering delivery, tool cancellation, registry shadowing, and durable-event filtering. +- `TFMTests/AgentTokenUsageTests.swift` covers provider usage normalization, cumulative merging, measured-total rescaling, context-window shares, incremental/reference parity and draft edits that do not rebuild the gauge. `ChatComposerStateTests.swift` checks immediate raw-delta visibility with throttled context publishing and edits that remove downstream tools across every card lifecycle state. - `TFMTests/AgentPersistenceTests.swift` covers redaction, interrupted-run recovery, replay deduplication, tool-result repair, graph deletion, same-run edit prefix/tool preservation, canonical equal-time ordering, empty retained runs, staging adoption, late-write rejection, fork persistence and compaction restoration. `ChatComposerStateTests.swift` checks the compacted live prefix, tool cards, title, plans and adopted run summaries. - `FrameworkTests/AssistantChatCollaborationPersistenceTests.swift` checks atomic cross-context receipt confirmation, monotonic delivered state, and creation-relation reopen; `FrameworkTests/AssistantChatCollaborationReviewTests.swift` and `AssistantChatCollaborationEdgeTests.swift` check source/target isolation, retries, planning restrictions, bounded listing, receipt failures and wait/interjection races. `FrameworkTests/AgentReplayPersistenceTests.swift` checks failed/interrupted staging adoption and reloads through a fresh `ModelContext` and repository to verify durable provenance and replay backing. - `FrameworkTests/AssistantExecutionControllerTests.swift` covers capture across a memory barrier, missing credentials, first-send guards and payload folding, plan implementation, edit persistence/cancellation barriers, failed/interrupted replacement provenance, superseded captures, navigation, compaction refusal, memory revocation, and neutral legacy context. Blocked and failing saves verify that captured failure keeps preparation owned through stop and archive/delete until save completion and only adopts staging on success. `FrameworkTests/AssistantModelSelectionTests.swift` checks draft model seeding, restored choices, unavailable-model repair, and persistence-first model selection. diff --git a/openwiki/concepts/commerce-localization-and-product-surfaces.md b/openwiki/concepts/commerce-localization-and-product-surfaces.md index 31b545d6..faa3537a 100644 --- a/openwiki/concepts/commerce-localization-and-product-surfaces.md +++ b/openwiki/concepts/commerce-localization-and-product-surfaces.md @@ -1,9 +1,11 @@ --- type: product-surface-concept -title: Commerce, Localization and Product Surfaces +title: Commerce, Localization, and Product Surfaces description: Explains RevenueCat-derived Pro access and remote paywalls, native language and copy ownership, and the separate app, widget, screenshot, and public-site product surfaces. tags: [commerce, revenuecat, localization, product-surfaces, marketing, widgets] sources: + - id: openwiki-source-8c4bdfb086f2eff661e05807 + resource: repo://.github/workflows/localization.yml - id: openwiki-source-ed77bc20b48d193bace3dde6 resource: repo://.gitmodules - id: openwiki-source-8037e2358a2c4f9b2c722a11 @@ -38,8 +40,6 @@ sources: resource: repo://Frameworks/TermindPresentation/Strings/AppStrings.swift - id: openwiki-source-667f8dcadd921c1910c5026c resource: repo://Frameworks/TermindPresentation/Strings/LocalizedStringResource%2BPresentation.swift - - id: openwiki-source-a3debd0d113ee290e9ff3df7 - resource: repo://Frameworks/TermindPresentation/Strings/Navigation.swift - id: openwiki-source-f0eb100f79cea6b455207748 resource: repo://Frameworks/TermindPresentation/Strings/Presentation.swift - id: openwiki-source-eae5d91412d0e2847dacd5b6 @@ -56,6 +56,8 @@ sources: resource: repo://Scripts/normalize-string-catalogs.py - id: openwiki-source-60d4159ef162d09de4f4b469 resource: repo://Scripts/run-screenshot-demo.sh + - id: openwiki-source-269bf9e8dd1a18fb2bc38e80 + resource: repo://Scripts/Tests/test_cjk_localization.py - id: openwiki-source-9fc915d39c61b81a60426eb3 resource: repo://StoreKit/Termind.storekit - id: openwiki-source-906678adb91c2fce2d3fcaed @@ -96,6 +98,8 @@ sources: resource: repo://TFM/Features/Commerce/ProPaywallView.swift - id: openwiki-source-bf08592e56aa88a7189a7fb5 resource: repo://TFM/Features/KnownHosts/KnownHostsPageView.swift + - id: openwiki-source-cfe2a6b22338023c37a5b503 + resource: repo://TFM/Features/Settings/SettingsPageView.swift - id: openwiki-source-793d7861000adc19f0c3df01 resource: repo://TFMTests/AppLanguageTests.swift - id: openwiki-source-d3d065330c730839f5cc6112 @@ -104,10 +108,10 @@ sources: resource: repo://TFMTests/PresentationCopyTests.swift - id: openwiki-source-0f8d0f9d08d6674698dd054c resource: repo://WorkspaceActivitySupport/WorkspaceActivityAttributes.swift -generated: { by: "openwiki/0.7.0", at: "2026-10-09T00:05:26.272Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - by: openwiki/0.7.0 - at: 2026-10-09T00:05:26.272Z + at: 2026-10-10T23:08:14.218Z --- ## Commerce ownership and configuration @@ -178,7 +182,42 @@ The platform views observe purchase/restore start, completion, cancellation and App UI copy, including platform-specific strings and the native paywall **fallback** text, belongs to semantic `LocalizedStringResource` definitions in `Frameworks/TermindPresentation/Strings` and the shared manual `Frameworks/Resources/Localizable.xcstrings`; `InfoPlist.xcstrings` owns bundle metadata. Keep diagnostic codes, protocol values like remote paywall `state`, identifiers and user-authored content outside translated UI copy. RevenueCat's dashboard paywall text and StoreKit product metadata are **separate** copy owners. The app catalogs must have finished `zh-Hans`, `zh-Hant` and `ja` units for changed English keys. `Scripts/normalize-string-catalogs.py --write` canonicalizes catalog format and manual extraction without deleting stale keys or changing translations and refuses duplicate JSON keys before writing; `Scripts/check-cjk-localization.py` checks both app catalogs by default for normalization, valid JSON, Chinese/Japanese completion, positional format arguments, other-only plural shape and locale-specific spacing. The separate AgentKit runtime catalog needs its own localization build step; native catalog checks do not establish website copy correctness. `Scripts/check-appstrings.sh` detects selected app-local UI literals via SwiftSyntax and `ArchitectureBoundaryTests` guards the shared copy owner, but neither proves all UI text is localized. -`AppLanguage` writes an explicit `en`, `zh-Hans`, `zh-Hant` or `ja` choice to `AppleLanguages`, clears that override for Follow System, migrates legacy Simplified Chinese tags and leaves unsupported preferences following the system. `AppLocalization` resolves imperative copy from the appropriate main-bundle localization, including regional Traditional Chinese. Resolve resources at display time, rather than caching translated strings across language changes; use the resource directly with SwiftUI APIs that accept it, especially `confirmationDialog`, and `swiftUIKey` only where `LocalizedStringKey` is required. Mobile `operationConfirmation` resolves resources against the view locale before passing verbatim strings to either an anchored SwiftUI confirmation or an injected centered UIKit renderer; an explicit Cancel action remains visible. `PresentationCopyTests` checks menu, count, accessibility and bridge resolution; current `AppLanguageTests` tests preference migration, regional catalog lookup, assistant/security-token terminology, forwarding placeholder order and selected plural resources. **It no longer tests explicit-language Pro offer copy.** Full catalog completion is the Python check, not these tests. +### Catalog and formatting contracts + +`AppStrings` is the hand-authored Swift API shared by TFM and TFI; its resources are created on demand and each app compiles the catalog into its main bundle. Keep `STRING_CATALOG_GENERATE_SYMBOLS = NO` rather than generating competing symbols for manual English keys. Every shared catalog entry uses `extractionState: "manual"`. A single target's stale extraction is not evidence of an unused key: check both apps and shared modules before removal. + +The available checker is **`python3 Scripts/check-cjk-localization.py`**, not `Scripts/check-zh-localization.sh`. [Repository guidelines](../../AGENTS.md) still refer to that older shell script and a `jq` requirement; those instructions are stale. The Python checker imports the catalog normalizer and defaults to `Localizable.xcstrings` and `InfoPlist.xcstrings`, as does the current [localization CI workflow](../../.github/workflows/localization.yml). + +- Entries marked `shouldTranslate: false` are exempt. Otherwise all three CJK languages must contain finished, nonempty translation units (an empty key may have an empty value). +- Interpolation must preserve the **position, type and multiplicity** of format arguments, including literal `%%`. Reordered translations need positional specifiers: `%lld on %@` may become `%2$@で%1$lld件`, but changing types or reordering implicit arguments fails. For metadata keys, the English localized value supplies the format contract rather than the key name. +- When English has plural variations, Chinese and Japanese must have exactly `other`; selected count resources have English `one` and `other`. Translate every nested unit, not only a top-level string. +- Japanese rejects spaces between Japanese scripts and Latin text, digits or placeholders. Chinese also checks punctuation and spacing around separators when Han text is present. The `Type %@` key-card subtitle has a narrow label/value spacing exception; this is not permission to add arbitrary mixed-script spaces. + +`Scripts/Tests/test_cjk_localization.py` exercises these failure cases and exceptions. CI runs it and the normalizer tests before the checker. These are structural checks, not translation-quality or visual-layout approval. + +### Language selection and launch lifecycle + +`AppLanguage` offers Follow System, English (`en`), Japanese (`ja`), Simplified Chinese (`zh-Hans`) and Traditional Chinese (`zh-Hant`). Explicit choices are persisted in `SettingsKey.appLanguage` and as a one-element canonical `AppleLanguages` array. Follow System stores `system` but **removes** `AppleLanguages`, restoring Bundle negotiation rather than writing a system-language array. + +Reading a legacy preference migrates only Chinese identifiers whose resolved script is `Hans` (tested examples include `zh`, `zh-CN`, `zh_CN` and regional `zh-Hans` tags). Unsupported preferences, including noncanonical regional Traditional Chinese stored values, return `.system` without rewriting the stored value. This preference migration differs from **catalog lookup**: `AppLocalization` resolves Chinese by script (`Hant` selects `zh-Hant`, otherwise `zh-Hans`), maps Japanese regional identifiers to `ja`, and English to `en`. It loads that main-bundle `.lproj` when available and otherwise falls back to the main bundle. Tests cover Traditional Chinese system locales such as `zh-TW`, `zh-HK` and `zh-MO`, and ensure Simplified and Traditional catalogs do not cross-fallback. The Traditional Chinese picker label becomes `正體中文` only for device region `TW` when the Taiwan flag can be displayed; otherwise it remains `繁體中文`. + +On desktop, `TermindApp` captures the stored language at initialization and injects its locale into scene content; Settings does not replace that launch-time locale. The picker writes the preference without automatically relaunching. When its selected language differs from the Settings view's captured `launchLanguage`, it shows “The new language will be used after Termind relaunches.” resolved explicitly in the **selected** language (Follow System uses `.autoupdatingCurrent`). Normal UI adopts the new explicit choice on a subsequent launch, not an immediate whole-app language refresh. + +```mermaid +flowchart TD + Launch["App initialization reads preference"] --> LocaleOwner["Capture launch language and inject scene locale"] + Pick["Settings language selection"] --> Choice{"Follow System"} + Choice -->|yes| Remove["Store system and remove AppleLanguages"] + Choice -->|no| Store["Store canonical tag and AppleLanguages array"] + Remove --> Notice{"Different from captured Settings language"} + Store --> Notice + Notice -->|yes| Copy["Resolve relaunch notice in selected locale"] + Copy --> Wait["Continue without automatic relaunch"] + Wait --> Launch +``` +The desktop picker persists the next launch's choice while rendering only the notice in the newly selected language. + +Resolve resources at display time, rather than caching translated strings across language changes; use the resource directly with SwiftUI APIs that accept it, especially `confirmationDialog`, and `swiftUIKey` only where `LocalizedStringKey` is required. Mobile `operationConfirmation` resolves resources against the view locale before passing verbatim strings to either an anchored SwiftUI confirmation or an injected centered UIKit renderer; an explicit Cancel action remains visible. `PresentationCopyTests` checks menu, count, accessibility and bridge resolution; current `AppLanguageTests` tests preference migration, regional catalog lookup, selected-language relaunch notices, Japanese interpolation, assistant/security-token terminology, forwarding placeholder order and selected plural resources. **It no longer tests explicit-language Pro offer copy.** Full catalog completion is the Python check, not these tests. Ordinary product labels illustrate the split: iOS Vault search opens Snippets and Known Hosts, whose pages distinguish an empty collection from empty search results. Known Hosts offers add/edit/copy-fingerprint/delete and an editor title that changes with the entry; the macOS empty state additionally offers import or manual add and deletion warns that trust will need verifying on the next connection, with count-sensitive confirmation. Snippets has new snippet/package/subpackage actions and a keep-or-remove-snippets package deletion choice. Shared `SnippetCardContent` supplies colored icons, valid SSH-target counts, risk chips and relative timestamps to both apps, while each owns the surrounding surface and actions. `SnippetTimestamp` supplies a stable local fallback for legacy baseline dates. Card timestamps use the view locale; hostnames, fingerprints, paths and scripts remain user content. `MobileCardTests` checks add/edit titles; opt-in `MobileCardRenderingTests` mounts real views in three app languages with light/dark and selected large-type captures. Inspect actual views as well as catalogs after UI changes. @@ -199,5 +238,5 @@ Once source is available, follow the documented website workflow (`cd com.akinok - Native entitlement policy and gates: update resolver, feature gates and app reconciliation together; run `RevenueCatCommerceTests`, `ProPaywallStateTests`, `ProAccessTests`, Metrics tests and platform-specific gate tests. Check live entitlement configuration and account migration separately from the local StoreKit metadata fixture. [RevenueCat configuration](../../Frameworks/TermindInfrastructure/Commerce/RevenueCatConfiguration.swift) and [service configuration](../../Frameworks/TermindInfrastructure/Commerce/RevenueCatProPurchaseService.swift) explain the native SDK boundary, not public-site promises. - RevenueCat dashboard-owned paywalls and legal copy: inspect the remote layouts, visibility rules, translations, offer terms and legal destinations independently. Compare them with the native [paywall variable contract](../../Frameworks/TermindPresentation/ProPaywallState.swift), [mobile wrapper](../../TFI/Features/Commerce/MobileProView.swift), [desktop wrapper](../../TFM/Features/Commerce/ProPaywallView.swift) and [state tests](../../FrameworkTests/ProPaywallStateTests.swift). These tests verify status mapping, not remote wording or live purchases. - Apple storefront metadata: verify live product availability, localized descriptions, prices, introductory-offer eligibility, Family Sharing and purchase/restore behavior in App Store Connect and Sandbox. Neither website prices nor the local fixture establish the offer a particular user receives. See [Storefront Commerce Release Checks](../operations/storefront-commerce-release.md). -- App catalogs: run `Scripts/normalize-string-catalogs.py --write` after app catalog edits, `Scripts/check-appstrings.sh`, `Scripts/check-cjk-localization.py`, `PresentationCopyTests` and `AppLanguageTests`; review actual screens separately. See [Behavioral Boundaries and Fixtures](../testing/behavioral-boundaries-and-fixtures.md). +- App catalogs: run `python3 Scripts/normalize-string-catalogs.py --write` after app catalog edits, `Scripts/check-appstrings.sh`, `python3 Scripts/check-cjk-localization.py`, `python3 -m unittest discover -s Scripts/Tests -p 'test_cjk_localization.py'`, `PresentationCopyTests` and `AppLanguageTests`; review actual screens separately. See [Behavioral Boundaries and Fixtures](../testing/behavioral-boundaries-and-fixtures.md) and [Build, Test, and Release](../operations/build-test-and-release.md). Language preferences and sync ownership are discussed in [Settings and Cloud Sync](../workflows/settings-and-cloud-sync.md); visual copy checks complement [UI Design Principles](ui-design-principles.md). - Independently authored website claims: obtain the website source and inspect the deployed site before comparing its FAQ, offers, prices, privacy/security disclosures and localized routes with native entitlement and sync behavior. Build and visually check the routes actually present in that checkout. Keep unavailable content explicitly unverified; native string tests and RevenueCat state tests do not validate it. diff --git a/openwiki/concepts/index.md b/openwiki/concepts/index.md index 1795a0e9..eaf7b6ef 100644 --- a/openwiki/concepts/index.md +++ b/openwiki/concepts/index.md @@ -1,11 +1,11 @@ # Files -- [Agent runtime and assistant ownership](agent-runtime-and-assistant.md) - How AgentKit owns the neutral runtime while Termind assembles assistant execution, host tools, persistence, and UI across macOS and iOS. -- [Commerce, Localization and Product Surfaces](commerce-localization-and-product-surfaces.md) - Explains RevenueCat-derived Pro access and remote paywalls, native language and copy ownership, and the separate app, widget, screenshot, and public-site product surfaces. +- [Agent Runtime and Assistant](agent-runtime-and-assistant.md) - How AgentKit owns the neutral runtime while Termind assembles assistant execution, host tools, persistence, and UI across macOS and iOS. +- [Commerce, Localization, and Product Surfaces](commerce-localization-and-product-surfaces.md) - Explains RevenueCat-derived Pro access and remote paywalls, native language and copy ownership, and the separate app, widget, screenshot, and public-site product surfaces. - [Ghostty Terminal Integration](ghostty-terminal-integration.md) - Explain the boundary between Ghostty rendering and input and Termind transport, retained native surfaces, Find lifecycle, menu customization, upload policy, and appearance on macOS and iOS. -- [Persistent Data, Migrations, and Secrets](persistence-and-secrets.md) - How Termind owns SwiftData and GRDB durable data, evolves compatible schemas, projects records through repositories, and keeps credentials in Keychain-backed and runtime-only boundaries. +- [Persistence and Secrets](persistence-and-secrets.md) - How Termind owns SwiftData and GRDB durable data, evolves compatible schemas, projects records through repositories, and keeps credentials in Keychain-backed and runtime-only boundaries. - [Remote Services and Forwarding](remote-services-and-forwarding.md) - Ownership boundaries among terminal, metrics, Agent SSH/SFTP tools, and TCP forwarding; explains when connections can be reused and which interactions are allowed. - [Shell Integration: Bootstrap, OSC Events, and Terminal State](shell-integration.md) - How SSH PTY shell hooks bootstrap and decode command and cwd events, how command completion notifications observe shell hooks or OSC 133, and how charset handling differs from Telnet. - [SSH Multiplexing, Channel Admission, and I/O Scheduling](ssh-multiplexing-and-scheduling.md) - The concrete ownership and scheduling layers behind Termind SSH: per-connection session leases, serial libssh2 workers, exec state machines, multiplexed SFTP retries, readiness, backpressure, and feature-specific connection reuse. - [SSH Security Key Authentication](ssh-security-key-authentication.md) - How Termind registers an RP-bound FIDO2/WebAuthn Security Key, stores its public reference, and turns a local authenticator assertion into an OpenSSH SSH signature across macOS and iOS. -- [UI state and presentation principles](ui-design-principles.md) - Source-backed guide to native desktop and mobile settings routes, reusable presentation, terminal appearance selection, and state continuity across layout changes. +- [UI Design Principles](ui-design-principles.md) - Source-backed guide to native desktop and mobile settings routes, reusable presentation, terminal appearance selection, and state continuity across layout changes. diff --git a/openwiki/concepts/persistence-and-secrets.md b/openwiki/concepts/persistence-and-secrets.md index a90557f9..b909ea77 100644 --- a/openwiki/concepts/persistence-and-secrets.md +++ b/openwiki/concepts/persistence-and-secrets.md @@ -1,6 +1,6 @@ --- type: persistence and security concept -title: Persistent Data, Migrations, and Secrets +title: Persistence and Secrets description: How Termind owns SwiftData and GRDB durable data, evolves compatible schemas, projects records through repositories, and keeps credentials in Keychain-backed and runtime-only boundaries. tags: [persistence, swiftdata, grdb, migrations, keychain, secrets, security] sources: @@ -12,6 +12,8 @@ sources: resource: repo://Frameworks/TermindApplication/Agent/ConversationModel.swift - id: openwiki-source-2d98a2885dc21f508f49b749 resource: repo://Frameworks/TermindApplication/Agent/MemoryIndexedAgentRepository.swift + - id: openwiki-source-a51ca858a040a90d4ab9c1c4 + resource: repo://Frameworks/TermindApplication/AppLanguage.swift - id: openwiki-source-dc7b363138921b0923d2dc2f resource: repo://Frameworks/TermindApplication/FeatureModels/ConnectionHistoryModel.swift - id: openwiki-source-4143deeb06ebc3198de62e2b @@ -42,8 +44,6 @@ sources: resource: repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift - id: openwiki-source-8a511ce5c3a76863609a6e6e resource: repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycleSafety.swift - - id: openwiki-source-8a2e7d4db4b334995f8eaf61 - resource: repo://Frameworks/TermindPersistence/AgentPersistence.swift - id: openwiki-source-ec0a64adad50721ec500ebc2 resource: repo://Frameworks/TermindPersistence/AISecretStore.swift - id: openwiki-source-33d69cea4b24d60ab000a0cc @@ -60,6 +60,8 @@ sources: resource: repo://Frameworks/TermindPersistence/HistorySyncStore.swift - id: openwiki-source-fe1dc993163bb9ff61397be7 resource: repo://Frameworks/TermindPersistence/HostPersistence.swift + - id: openwiki-source-cea80bf19da5e3f99d178655 + resource: repo://Frameworks/TermindPersistence/PersistentAgentMessageReplay.swift - id: openwiki-source-0e8ecbf3896cbf237a679d6a resource: repo://Frameworks/TermindPersistence/PersistentNotificationPreferences.swift - id: openwiki-source-303e34b5782a7a14e28cc9f8 @@ -136,10 +138,14 @@ sources: resource: repo://TFI/Features/Vault/MobileVaultView.swift - id: openwiki-source-b822b041afbf829d09a8555a resource: repo://TFITests/MobileAppTests.swift + - id: openwiki-source-c52cd56852d12b2dee153082 + resource: repo://TFM/App/AppDependencies.swift - id: openwiki-source-4dbea0ff2808163cb4ecdd8f resource: repo://TFM/App/AppModel.swift - id: openwiki-source-d5cca6ff91d9aaa5da323b40 resource: repo://TFM/App/DatabaseStartupGuard.swift + - id: openwiki-source-37c42ec0ebc182c487dd27aa + resource: repo://TFM/App/DesktopSettingsKey.swift - id: openwiki-source-6570374eaac2e0278fa84640 resource: repo://TFM/App/TermindApp.swift - id: openwiki-source-0854be84b166268626a3cb08 @@ -148,13 +154,13 @@ sources: resource: repo://TFMTests/AgentPersistenceTests.swift - id: openwiki-source-1d1efb20e239b1f2142f6cb7 resource: repo://TFMTests/SettingsSyncTests.swift -generated: { by: "pi", at: "2026-10-06T18:02:45.153Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - by: openwiki/0.7.0 - at: 2026-10-06T18:02:45.153Z + at: 2026-10-10T23:08:14.218Z --- -# Persistent Data, Migrations, and Secrets +# Persistence and Secrets Termind separates durable *metadata* from secret material and live connection state. `TermindPersistence` is the sole owner of SwiftData models, schema history, migrations, and container construction; application and platform layers select the store location and inject contexts into repositories. Credentials are represented durably by non-secret references and storage provenance, resolved only through `SecretStoring`, and converted to wipeable runtime objects only for a physical SSH connection attempt. @@ -187,6 +193,10 @@ This diagram distinguishes durable SwiftData and GRDB records from Keychain mate The desktop root opens the container during `TermindApp.init()`: normal launches use the factory's default file-backed configuration (no explicit URL), while DEBUG XCTest and screenshot demos select an in-memory container. Before normal open, `DatabaseStartupGuard` checks for another running instance of the same bundle identifier, including older versions that do **not** use the new lock; a busy store presents an alert and exits, while other open/setup errors are fatal rather than replaced with an empty database. It supplies `KeychainSecretStore` for live composition and passes the same container to the main and authentication-window scenes. A screenshot demo instead constructs demo models and returns before live repository wiring; XCTest uses an in-memory container but disables cloud coordinators (it still constructs a Keychain secret store). The mobile root creates its Application Support parent before opening `Termind.store`, or chooses an in-memory container and `InMemorySecretStore` when `inMemory` is set or hosts are injected. A supplied container overrides factory construction; otherwise mobile initialization throws on open failure rather than replacing the store. `TermindIOSApp` retains the startup result and `MobileStartupView` injects the container on success or shows an unavailable-data-store view on failure. Data availability is an explicit startup condition. +These are app-lifetime dependencies, not per-window stores: desktop retains `AppModel` alongside the container, and passes the same `KeychainSecretStore` to the Host library, AI configuration/AI secret-location service, and built-in MCP server token storage. MCP client OAuth authorization uses its separate `MCPKeychainCredentialStore` in live composition (an in-memory authorization store in XCTest). The root prepares Settings, Metrics and History persistence before constructing their feature models. `AppDependencies` carries repositories and secret ports into `AppModel`; windows consume the shared application model rather than opening another database or allocating their own secret store. + +Not all preferences belong to a cloud replica. `AppLanguage.stored()` reads the local `UserDefaults` choice at desktop launch; storing it updates the local language key and `AppleLanguages`. Desktop appearance, main-window geometry, sidebar state and built-in MCP listener preferences are defaults-backed local settings, not evidence of Settings CloudKit replication. Only explicit journal-backed preference paths described in [Settings and Cloud Sync](../workflows/settings-and-cloud-sync.md) establish replication. + ### Repository projections and atomic mutation SwiftData rows are persistence representations, not the feature API. `SwiftDataHostRepository` projects `PersistentHost` and `PersistentHostGroup` into `HostLibrarySnapshot` values and publishes revisioned `AsyncStream` changes. Host upserts update or insert the row, then use one commit path that advances a database revision, records Vault mutation information, saves, and only then publishes the mutation. On failure it rolls the `ModelContext` back. @@ -199,6 +209,8 @@ Agent conversations use a separate SwiftData repository with similarly deliberat ### Assistant history edits: one message boundary, one transaction +Replay is backing for the original turn, not a fresh reconstruction from current Host or skill configuration. `PersistentAgentMessageReplay` stores a redacted captured `contextSnapshot` and optional frozen `modelText`; attachment backing is keyed separately by message ID. Snapshot saves deduplicate messages across runs and can add missing replay backing to an already-stored user row when preparation finally freezes its context, without rewriting that historical row. Existing replay backing is not overwritten by later snapshots. Restore rejoins backing by message ID and recovers durable tool output, including its frozen model preview, before compaction projection. + Restore and truncation share a canonical ordering: runs by `startedAt` then UUID; messages by that run order, then `sequence`, `createdAt`, role rank and UUID. Timestamps alone do not identify the cutoff. `truncateConversation` checks cancellation and validates an ordinary user replacement with a fresh ID that collides with neither a stored message nor a run before mutation. It removes the selected user message and the ordered suffix, including whole later runs. When the target run still has a prefix, that run and its retained messages survive: the summary becomes neutral `.completed`, clears its previous failure and ends at the last retained non-tool message. Editing its opening removes the target run only when no prefix remains. Graph cleanup follows the same boundary. Removed messages lose their replay and context-attachment backing. Within a partially retained run, invocation source-message ownership takes priority, followed by call identity in retained/removed transcript rows; only unrecoverable ownership uses the conservative `createdAt >= target.createdAt` fallback. This covers both legacy rows and modern provider-native events persisted before their source-message snapshot. Results and reviews follow the deleted invocation IDs. Compactions are deleted only if their explicit boundary is among the removed message IDs, so retained-prefix records and nil-boundary fork bases survive. The generated title is cleared only when the opening user turn is removed. On load, raw run-local assistant calls supply missing legacy source IDs, and reconstructed deterministic tool-result messages receive run ownership before compaction projection; this lets the live model preserve the same graph through compacted history. @@ -211,9 +223,30 @@ The repository's in-memory retired-ID set includes the edited target run even wh `AssistantConversationOperations` applies the live history edit only after the repository commits; invalid replacements and rejected writes leave history unchanged. Captured missing-model/credential failure now schedules a separate asynchronous save and **only then** calls `recordFailedRun` to adopt and publish the failed run; preparation remains active in its saving-failure phase until this finishes. Preparation cancellation that records interruption also saves its terminal snapshot before adopting and publishing the interrupted state. A save error appends a visible error rather than projecting a successful failed/interrupted adoption. This commit-before-projection rule does not make every stop a durability barrier: `ConversationModel`'s queued-before-start cancellation projects `.cancelled` when it launches a best-effort `prestartPersistence` task, and `stopAndWait` joins the consumer but the save error is swallowed. See [Assistant Runtime](agent-runtime-and-assistant.md) for runtime handoff. Shared replay tests reopen failed/interrupted adoption through a fresh context and cover queued cancellation and captured configuration failure; desktop and mobile fixtures compare same-run prefix history and provenance after edits. These fixtures do not inject a real disk-full or read-only SwiftData failure. +The edited suffix and replacement are handled as a transaction, not a timestamp-based overwrite: + +```mermaid +flowchart TD + Request["Edit ordinary user turn"] --> Validate["Validate target and fresh replacement ID"] + Validate --> Cut["Find canonical message boundary"] + Cut --> Graph["Delete suffix graph and later runs"] + Graph --> Prefix["Neutralize retained target-run prefix"] + Prefix --> Stage["Insert replacement staging run and backing"] + Stage --> Commit{"SwiftData save succeeds"} + Commit -->|no| Rollback["Rollback context and retired run IDs"] + Commit -->|yes| Project["Apply live history edit"] + Project --> Snapshot["First real run snapshot"] + Snapshot --> Adopt["Adopt staged message and retire staging ID"] + Adopt --> Save["Save real run atomically"] +``` + +An edit commits its graph cut before live projection; adoption later transfers the replacement without duplicating its backing. + +**Whole-conversation deletion is a distinct path.** `deleteConversation` retires the conversation and its existing run IDs, explicitly deletes result/review/invocation rows and message replay, attachment and origin backing, then removes messages, runs, conversation ordering, model selection and compactions before saving. Late snapshots for that conversation are ignored, and events for its retired runs are ignored. Unlike truncation and snapshot save, this method has no local catch that rolls back the context and restores retired IDs on failure; do not assume all history operations have identical failure semantics. It also does not purge collaboration delivery receipts or conversation-creation relations as part of this graph helper. [AgentPersistenceTests](../../TFMTests/AgentPersistenceTests.swift) checks graph removal, equal-time suffix tools with and without transcript call ownership, same-run prefix retention and staging adoption, rejection of foreign message targets, and explicit compaction-boundary pruning. [AgentReplayPersistenceTests](../../FrameworkTests/AgentReplayPersistenceTests.swift) verifies captured replay/model previews and failed/interrupted adoption through a fresh context. + ## Schema evolution: preserve the past exactly -Historical SwiftData model shapes are deliberately frozen. A `VersionedSchema` hashes the types it names, so retroactively editing a type used by an already shipped schema can make an installed store unopenable rather than migrated. The migration plan retains endpoints V1–V28, V30, V31, and V34–V52. Its recent path is V27→V28→V30→V31→V34→V35→V36→V37→V38→V39→V40→V41→V42→V43→V44→V45→V46→V47→V48→V49→V50→V51→V52: all of these stages are lightweight except the V31→V34 custom history backfill. V29, V32, and V33 remain absent as migration endpoints. V35 adds native History journals and capability checkpoints; V36 removes the retired importer table; V37 adds scoped cloud record-presence and deletion-acknowledgement sidecars; V38 adds deletion-handoff causality and durable asset-cache cleanup work. V39 adds per-conversation assistant model selection (provider and model IDs, not credentials). V40 removes the retired per-category Vault, AI-configuration and Metrics quarantine tables while retaining scoped quarantine. V41 adds shared notification preferences; V42 adds assistant message/conversation provenance and durable collaboration delivery receipts; V43 adds public MCP OAuth configuration, V44 replaces the Host model, and V45 replaces the SSH key model to persist Security Key reference metadata, and V46 adds terminal keyword-highlight preferences, without changing historical model shapes. These are **local SwiftData schema versions**, not revisions to independently versioned CloudKit wire envelopes; the current local schema is V51. When a persisted shape changes, introduce a version-specific model and replace it only in the new schema; do not “clean up” an old model definition. +Historical SwiftData model shapes are deliberately frozen. A `VersionedSchema` hashes the types it names, so retroactively editing a type used by an already shipped schema can make an installed store unopenable rather than migrated. The migration plan retains endpoints V1–V28, V30, V31, and V34–V52. Its recent path is V27→V28→V30→V31→V34→V35→V36→V37→V38→V39→V40→V41→V42→V43→V44→V45→V46→V47→V48→V49→V50→V51→V52: all of these stages are lightweight except the V31→V34 custom history backfill. V29, V32, and V33 remain absent as migration endpoints. V35 adds native History journals and capability checkpoints; V36 removes the retired importer table; V37 adds scoped cloud record-presence and deletion-acknowledgement sidecars; V38 adds deletion-handoff causality and durable asset-cache cleanup work. V39 adds per-conversation assistant model selection (provider and model IDs, not credentials). V40 removes the retired per-category Vault, AI-configuration and Metrics quarantine tables while retaining scoped quarantine. V41 adds shared notification preferences; V42 adds assistant message/conversation provenance and durable collaboration delivery receipts; V43 adds public MCP OAuth configuration, V44 replaces the Host model, and V45 replaces the SSH key model to persist Security Key reference metadata, and V46 adds terminal keyword-highlight preferences, without changing historical model shapes. These are **local SwiftData schema versions**, not revisions to independently versioned CloudKit wire envelopes; the current local schema is V52. When a persisted shape changes, introduce a version-specific model and replace it only in the new schema; do not “clean up” an old model definition. V28 is required for existing installed stores. Removing its endpoint made staged migration reject those stores with CoreData error `134504` (unknown model version). `TermindSchemaV28HostModels` restores the historical fields, defaults and model identity from the parent of `b0d35d24`; V30 retains its existing model types and order. Recovery uses the normal container factory and preserves the original database on an opening failure, rather than clearing or rebuilding it. diff --git a/openwiki/concepts/ui-design-principles.md b/openwiki/concepts/ui-design-principles.md index f946e59b..90de9113 100644 --- a/openwiki/concepts/ui-design-principles.md +++ b/openwiki/concepts/ui-design-principles.md @@ -1,6 +1,6 @@ --- type: design guide -title: UI state and presentation principles +title: UI Design Principles description: Source-backed guide to native desktop and mobile settings routes, reusable presentation, terminal appearance selection, and state continuity across layout changes. tags: [ui, design-system, presentation, accessibility, localization, macos, ios] sources: @@ -118,6 +118,8 @@ sources: resource: repo://TFM/Features/Ports/PortForwardingView.swift - id: openwiki-source-cfe2a6b22338023c37a5b503 resource: repo://TFM/Features/Settings/SettingsPageView.swift + - id: openwiki-source-af7fbbca079c2cb6201a5151 + resource: repo://TFM/Features/Settings/TerminalCustomConfigurationEditorSheet.swift - id: openwiki-source-c661b665fc19c6bd43499b01 resource: repo://TFM/Features/Settings/TerminalThemePage.swift - id: openwiki-source-0854be84b166268626a3cb08 @@ -168,13 +170,13 @@ sources: resource: repo://TFMTests/WorkspaceLayoutDropTests.swift - id: openwiki-source-a3093a0c1d98ecc1cdbc0279 resource: repo://TFMTests/WorkspaceNavigatorDropTests.swift -generated: { by: "pi", at: "2026-10-08T13:46:25.102Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:46:25.102Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- -# UI state and presentation principles +# UI Design Principles Termind shares the *meaning* of actions, copy, form fragments and display values, not a cross-platform view tree. TFM owns window routing, persistent workspace views and pointer/keyboard controls; TFI owns scene destinations, native navigation stacks and touch-oriented sheets. See [Desktop UI](../architecture/desktop-ui.md), [Mobile UI](../architecture/mobile-ui.md), and [Commerce and Localization](commerce-localization-and-product-surfaces.md) for the surrounding ownership boundaries. diff --git a/openwiki/integrations/index.md b/openwiki/integrations/index.md index 676c9353..0eed743e 100644 --- a/openwiki/integrations/index.md +++ b/openwiki/integrations/index.md @@ -1,4 +1,4 @@ # Files -- [MCP server and bridge integration](mcp-server-and-bridge.md) - How the macOS loopback MCP server, stdio bridge, and scoped SFTP transfer tickets work, and how the mobile Assistant instead consumes remote MCP servers with device-local authorization. +- [MCP Server and Bridge](mcp-server-and-bridge.md) - How the macOS loopback MCP server, stdio bridge, and scoped SFTP transfer tickets work, and how the mobile Assistant instead consumes remote MCP servers with device-local authorization. - [Tailscale groups, embedded nodes, and SSH](tailscale.md) - Group-bound embedded Tailscale enrollment, managed peer identity, ordinary and Tailscale SSH routing, sync boundaries, profile cleanup, and focused regression tests. diff --git a/openwiki/integrations/mcp-server-and-bridge.md b/openwiki/integrations/mcp-server-and-bridge.md index 4eba92e9..eef4dd10 100644 --- a/openwiki/integrations/mcp-server-and-bridge.md +++ b/openwiki/integrations/mcp-server-and-bridge.md @@ -1,9 +1,11 @@ --- type: integration guide -title: MCP server and bridge integration +title: MCP Server and Bridge description: How the macOS loopback MCP server, stdio bridge, and scoped SFTP transfer tickets work, and how the mobile Assistant instead consumes remote MCP servers with device-local authorization. tags: [mcp, macos, ios, loopback, authentication, sftp, bridge] sources: + - id: openwiki-source-5f99e8af445e073ffe9bc257 + resource: repo://Frameworks/TermindApplication/Agent/AssistantExecutionController.swift - id: openwiki-source-3118dfa7dfade14baaccd202 resource: repo://Frameworks/TermindApplication/FeatureModels/AIConfigurationModel.swift - id: openwiki-source-130fa30d2edc07154f39ee38 @@ -32,6 +34,8 @@ sources: resource: repo://TFM/App/DesktopSettingsKey.swift - id: openwiki-source-b8fca76c89564e16eccfbb53 resource: repo://TFM/App/MCPServerController.swift + - id: openwiki-source-6570374eaac2e0278fa84640 + resource: repo://TFM/App/TermindApp.swift - id: openwiki-source-8a6e94328a445c3566ef9d57 resource: repo://TFM/Application/MCPServer/MCPDownloadCoordinator.swift - id: openwiki-source-76b1cdca4a02038756187f92 @@ -50,6 +54,8 @@ sources: resource: repo://TFM/Infrastructure/Networking/LoopbackHTTPServer.swift - id: openwiki-source-a0f4f642e37695357bc9150d resource: repo://TFM/Infrastructure/Security/MCPServerTokenStore.swift + - id: openwiki-source-73c75477db0674192248aa00 + resource: repo://TFMTests/AppTerminationTests.swift - id: openwiki-source-4cacabcabe7b6b079746d10e resource: repo://TFMTests/LoopbackHTTPServerTests.swift - id: openwiki-source-b466ff39828d4aa83b5bcd74 @@ -64,13 +70,13 @@ sources: resource: repo://TFMTests/MCPTransferTicketTests.swift - id: openwiki-source-47dc468698b2904f0004ee04 resource: repo://TFMTests/MCPUploadTests.swift -generated: { by: "pi", at: "2026-10-04T16:22:58.117Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - by: openwiki/0.7.0 - at: 2026-10-06T18:02:45.153Z + at: 2026-10-10T23:08:14.218Z --- -# MCP server and bridge integration +# MCP Server and Bridge Termind’s built-in MCP **server** is a **macOS-only, local integration boundary**. `TFM/App/AppModel.swift` constructs `MCPServerController`, which owns the listener and tool/transfer lifecycle and injects the app's external authentication coordinator and audit model; an MCP client supplies JSON-RPC requests. The listener accepts only loopback connections and authenticates normal MCP traffic with a device-local Bearer token. It is not a remote-access server. The iOS `MobileAppModel` instead composes a mobile Assistant and ordinary SFTP transfers, not this listener. iOS **does** support the other side of MCP: its Pro-gated remote MCP settings/editor can discover HTTP(S) servers with `MCPClient`, configure a manual Bearer credential or OAuth, set per-tool permissions, and make their tools available to the Assistant. Do not confuse the mobile outbound client with the desktop inbound server or its `/uploads` and `/downloads` routes. @@ -94,7 +100,33 @@ flowchart TD Assistant outbound discovery and desktop inbound serving are separate catalogs and authorization paths; the bridge is only a transport adapter to the inbound listener. -The desktop app explicitly revokes MCP tickets when a host is deleted (including host-deletion outbox handling and remotely applied vault removals), alongside cancelling ordinary SFTP work and SSH connections. On app termination, it stops MCP and waits for ordinary SFTP cancellation before closing shared agent background commands and connections. Mobile host-deletion handling instead cancels its ordinary SFTP transfers; there are no inbound MCP sessions to revoke there. +The desktop app explicitly revokes MCP tickets when a host is deleted (including host-deletion outbox handling and remotely applied vault removals), alongside cancelling ordinary SFTP work and SSH connections. Mobile host-deletion handling instead cancels its ordinary SFTP transfers; there are no inbound MCP sessions to revoke there. + +### Shutdown boundary + +The Assistant and inbound MCP server share app-owned host services, so **stopping the listener alone is not sufficient to close the shared SSH resources safely**. `AppModel.shutdownForTermination()` fences new Assistant admissions with `isTerminating`, cancels pending Assistant preparations, and calls `stop()` on every stored conversation before awaiting any of them—not just conversations visible in live windows. It creates `stopAndWait()` drains for those conversations. The preparation cancellation join also covers captured preparation-failure persistence for conversations not yet in the store. + +MCP stop and ordinary SFTP cancellation waiting run concurrently with those drains. Only after preparation cancellation, every conversation drain, and both MCP/SFTP stop tasks complete does the app close shared background commands, then shared agent connections, then Tailscale. Repeated termination calls reuse the same cleanup task. The app also synchronously cancels ordinary transfers and external authentication, closes windows, and stops forwarding and other UI/background producers. `MCPServerController.stop()` itself drains transfer coordinators, stops protocol contexts, stops HTTP, then cancels external MCP prompts; it does not close the app-wide agent connection pool. + +```mermaid +flowchart TD + Fence["Fence new Assistant admissions"] --> Cancel["Cancel pending preparations and stop all stored conversations"] + Cancel --> Prep["Join preparation cancellation and persistence"] + Cancel --> Chats["Join conversation stopAndWait drains"] + Cancel --> MCPStop["Stop MCP transfers, protocol and HTTP"] + Cancel --> SFTPStop["Wait for ordinary SFTP cancellation"] + Prep --> Joined["All drains and stops complete"] + Chats --> Joined + MCPStop --> Joined + SFTPStop --> Joined + Joined --> Background["Close shared background commands"] + Background --> Connections["Close shared agent connections"] + Connections --> Tailscale["Shut down Tailscale"] +``` + +Desktop termination joins Assistant work and both inbound MCP and ordinary SFTP cleanup before tearing down their shared host resources. + +This is the intended cleanup ordering, not an unlimited guarantee that every operation can finish before process exit: the app delegate hands the task to a bounded termination reply coordinator. See [Platform runtime](../architecture/platform-runtime.md) for the wider application lifecycle. The endpoint is normally `http://127.0.0.1:7391/mcp`, but the port is configurable. The server is off by default and requires the MCP Server Pro entitlement to enable, configure, rotate its token, or change exported tools. These inbound-server preferences are device-local rather than synced AI or remote MCP client settings. @@ -299,6 +331,8 @@ Missing token disables all generated commands; a missing bundled bridge disables * To immediately cut off an external client, disable the server or rotate its token. To reduce surface area, disable individual tools; disabling either transfer tool revokes its tickets. * Test changes at the correct seam: `MCPAuthorizationTests` checks device-local grant separation, draft rollback and saved-token refresh, endpoint rebinding and retirement, and cancellation of browser authorization callbacks without adding a transcript message. `MCPServerSessionTests` exercises initialization, version/session requirements, tool discovery, legacy SSE, and deletion cancellation without session resurrection; `MCPServerToolsTests` verifies all 18 exports, shared descriptor parity with the acting host subset, excluded tools, disabled invocation, one-shot session-bound secrets, and upload/download ticket creation with connection openers that would fail if called. Its cross-run status lookups fail, and `endSession` removes ticket access and discards secrets/cancels prompts. `MCPUploadTests` and `MCPDownloadTests` cover resume, claim, digest, cancellation, status, and wire behavior; `MCPClientSetupCommandTests` checks exact, shell-safe client registration arguments. +* `AppTerminationTests` guards the shared-resource shutdown boundary: all stored conversations are cancelled even without a live window, terminal saves finish before background commands/connections close, repeated shutdown does not duplicate cleanup, post-fence Assistant admissions and prepared edits cannot start, and an unstored captured failure is joined before resource closure. + ## Related pages * [Platform runtime](../architecture/platform-runtime.md) — desktop and mobile composition and shutdown ownership. diff --git a/openwiki/operations/build-test-and-release.md b/openwiki/operations/build-test-and-release.md index 36b5ddfd..b54bd492 100644 --- a/openwiki/operations/build-test-and-release.md +++ b/openwiki/operations/build-test-and-release.md @@ -4,6 +4,8 @@ title: Build, Test, and Release description: Reproducible Xcode builds and pinned dependency updates, opt-in UI and commerce test gates, and localization checks for Termind releases. tags: [operations, build, testing, localization, release, agentkit, screenshots] sources: + - id: openwiki-source-8c4bdfb086f2eff661e05807 + resource: repo://.github/workflows/localization.yml - id: openwiki-source-ea70eb6c045047448e446296 resource: repo://.gitignore - id: openwiki-source-ed77bc20b48d193bace3dde6 @@ -20,10 +22,6 @@ sources: resource: repo://Frameworks/TermindApplication/Terminal/TerminalFindController.swift - id: openwiki-source-e568ada11d31632da3ad26c7 resource: repo://FrameworkTests/TerminalFindControllerTests.swift - - id: openwiki-source-985b6a509f7d84d0b5153a7a - resource: repo://Scripts/check-appstrings.sh - - id: openwiki-source-7d8d2d50a9c530a1decb39f3 - resource: repo://Scripts/check-appstrings.swift - id: openwiki-source-c6328be86432b33a68051c73 resource: repo://Scripts/check-cjk-localization.py - id: openwiki-source-de562e98d49cbf6502c23c13 @@ -72,10 +70,10 @@ sources: resource: repo://TFMTests/Screenshots/MacScreenshotTests.swift - id: openwiki-source-a08489338be00125d646c77e resource: repo://TFMTests/VisibleWindowTestTrait.swift -generated: { by: "openwiki/0.7.0", at: "2026-10-09T00:05:26.272Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - by: openwiki/0.7.0 - at: 2026-10-09T00:05:26.272Z + at: 2026-10-10T23:08:14.218Z --- # Build, Test, and Release @@ -208,14 +206,43 @@ Planning and Ruby checks do not open windows. Actual Mac capture takes desktop f Define user-facing copy as shared `LocalizedStringResource` entries under `Frameworks/TermindPresentation/Strings` (`AppStrings`), even for one-platform wording; the catalog compiled into each app bundle is `Frameworks/Resources/Localizable.xcstrings`, and bundle metadata belongs in `Frameworks/Resources/InfoPlist.xcstrings`. Keep identifiers and diagnostic codes with their existing owners. `STRING_CATALOG_GENERATE_SYMBOLS = NO` because `AppStrings` owns the Swift-facing API. Any new or changed English string must ship with finished `zh-Hans`, `zh-Hant` and `ja` translations in the same commit. Preserve placeholders and Chinese punctuation conventions. -The two shared catalogs are manually maintained: keep `extractionState: "manual"`, add keys/translations explicitly, and verify use in both apps and shared code before deleting an entry. A single target's `stale` extraction flag does not establish that a shared key is unused. After a catalog edit run `Scripts/normalize-string-catalogs.py --write` for Xcode-compatible ordering and formatting; do not substitute `jq -S` or a generic JSON formatter. The normalizer leaves translations intact and does not touch AgentKit's catalog; it rejects duplicate keys or invalid JSON before writing any catalog in a batch and atomically replaces each changed file. `--check` reports drift without writing. Before committing, run both checks (the catalog check uses Python and optionally accepts catalog paths): +The two shared catalogs are manually maintained: keep `extractionState: "manual"`, add keys/translations explicitly, and verify use in both apps and shared code before deleting an entry. A single target's `stale` extraction flag does not establish that a shared key is unused. After a catalog edit normalize the files for Xcode-compatible ordering and formatting; do not substitute `jq -S` or a generic JSON formatter: + +```sh +python3 Scripts/normalize-string-catalogs.py --write +python3 Scripts/normalize-string-catalogs.py --check +``` + +The normalizer leaves translations intact and does not touch AgentKit's catalog; it rejects duplicate keys or invalid JSON before writing any catalog in a batch and atomically replaces each changed file. `--check` reports drift without writing. Before committing, run both copy and catalog checks (the catalog check uses Python and optionally accepts catalog paths): ```sh Scripts/check-appstrings.sh python3 Scripts/check-cjk-localization.py ``` -The presentation-copy check uses Xcode's Swift parser to scan `TFM` and `TFI` Swift literals at presentation API call sites and fails when app-facing copy is written directly instead of using shared resources; it distinguishes these from identifiers, URLs, comments, and interpolated expressions. The catalog check runs the normalizer in read-only `--check` mode, then validates JSON, positional format-argument compatibility and other-only CJK plural forms and, for each Chinese/Japanese locale, rejects absent localization entries, string units not marked `translated`, empty values except for the empty key, and invalid Han/Latin or punctuation spacing for translatable entries (`shouldTranslate != false`). Fix the reported catalog or copy violation rather than bypassing the gate. Run `AppLanguageTests` when language behavior changes; for resource/formatting behavior also target `PresentationCopyTests`, which exercises multilingual resources, nested native menus, count-sensitive copy and SwiftUI resource bridging. +The presentation-copy check uses Xcode's Swift parser to scan `TFM` and `TFI` Swift literals at presentation API call sites and fails when app-facing copy is written directly instead of using shared resources; it distinguishes these from identifiers, URLs, comments, and interpolated expressions. It requires Xcode's SwiftParser/SwiftSyntax host libraries. The catalog checker imports the normalizer's loader and normalization function and compares normalized bytes to the original without writing; it does not invoke the normalizer's `--check` CLI. It validates JSON, positional format-argument compatibility and other-only CJK plural forms and, for each Chinese/Japanese locale, rejects absent localization entries, string units not marked `translated`, empty values except for the empty key, and invalid locale-specific spacing for translatable entries (`shouldTranslate != false`). Reordered placeholders must retain their original argument positions and types, including literal `%%` occurrences. Fix the reported catalog or copy violation rather than bypassing the gate. + +**Use the current scripts, not the stale shell command in `AGENTS.md`.** `Scripts/check-zh-localization.sh` is no longer available; `python3 Scripts/check-cjk-localization.py` is the workflow-backed replacement and does not require `jq`. The Localization workflow runs on Ubuntu for pull requests and pushes to `main` only when the shared catalogs, normalizer, CJK checker, their Python tests, or the workflow change. Its steps are: + +```sh +python3 -m unittest discover -s Scripts/Tests -p 'test_normalize_string_catalogs.py' +python3 -m unittest discover -s Scripts/Tests -p 'test_cjk_localization.py' +python3 Scripts/check-cjk-localization.py +``` + +This CI job does not run `Scripts/check-appstrings.sh` or native Swift tests, and its path filters do not make it an every-commit check. Keep the local copy and catalog gates in the commit pipeline even for code-only changes. When modifying the validation tools, run the two Python test commands above as well as checking the real catalogs. + +### Focused language and presentation regression tests + +Use the resolved-package `AppLanguageTests` command above for language-selection or catalog behavior changes. The suite verifies canonical `en`, `ja`, `zh-Hans` and `zh-Hant` overrides; migration of legacy Simplified Chinese preferences; unknown/Traditional Chinese regional preferences continuing to follow the system without rewriting the preference; and removal of `AppleLanguages` when returning to Follow System. It also checks Traditional Chinese system-locale resolution without cross-fallback, the region/flag-dependent Traditional Chinese display name, selected-language relaunch notices, Japanese regional locales and reordered interpolation, Chinese model-token terminology, forwarding-template argument order, and English `one`/`other` versus CJK other-only counted resources. These are routine tests, not visible-window or mobile key-window tests. + +For resource/formatting behavior also target `PresentationCopyTests`, which exercises multilingual resources, nested native menus, count-sensitive copy and SwiftUI resource bridging: + +```sh +xcodebuild -project Termind.xcodeproj -scheme TFM -destination 'platform=macOS' -onlyUsePackageVersionsFromResolvedFile test -only-testing:TFMTests/PresentationCopyTests +``` + +The Python gates prove catalog structure and translation invariants; these native tests additionally exercise bundle resolution and presentation-resource use. See [Commerce, Localization, and Product Surfaces](../concepts/commerce-localization-and-product-surfaces.md) for the ownership context. AgentKit owns its separate runtime catalog at `../AgentKit/Localizations/Localizable.xcstrings`. After changing that catalog, run the package’s catalog build step because a plain Swift build copies its resources rather than compiling string catalogs: diff --git a/openwiki/operations/index.md b/openwiki/operations/index.md index 87bc34d9..da3400a1 100644 --- a/openwiki/operations/index.md +++ b/openwiki/operations/index.md @@ -5,4 +5,4 @@ - [Ghostty Dependency Updates](ghostty-fork-maintenance.md) - Update the direct upstream Ghostty Swift Package pin while preserving app-owned menu, input, surface and upload boundaries. - [Marketing Assets and App Icons](marketing-assets-and-app-icons.md) - Capture localized native screenshots and generate staged device frames with parent-owned tooling. Separate locally verified safety guarantees from external artwork, website and publication checks. - [Port Forwarding Benchmark Design, Results, and Reproduction](port-forwarding-benchmarks.md) - Historical forwarding measurements, workload semantics, retained transport decisions, comparison limits, and reproducible loopback commands. -- [Storefront commerce release checks](storefront-commerce-release.md) - Release checklist for the local StoreKit product fixture, RevenueCat entitlement and paywall configuration, cross-platform access refresh, and Sandbox validation. +- [Storefront Commerce Release](storefront-commerce-release.md) - Release checklist for the local StoreKit product fixture, RevenueCat entitlement and paywall configuration, cross-platform access refresh, and Sandbox validation. diff --git a/openwiki/operations/storefront-commerce-release.md b/openwiki/operations/storefront-commerce-release.md index a5525848..0ca957ed 100644 --- a/openwiki/operations/storefront-commerce-release.md +++ b/openwiki/operations/storefront-commerce-release.md @@ -1,6 +1,6 @@ --- type: storefront-release-operations -title: Storefront commerce release checks +title: Storefront Commerce Release description: Release checklist for the local StoreKit product fixture, RevenueCat entitlement and paywall configuration, cross-platform access refresh, and Sandbox validation. tags: [app-store, storekit, revenuecat, commerce, subscriptions, localization, release] sources: @@ -36,8 +36,6 @@ sources: resource: repo://TFI/Features/Commerce/MobileProView.swift - id: openwiki-source-4bb1006b8fba7cd74268940b resource: repo://TFI/Features/Settings/MobileMCPSettings.swift - - id: openwiki-source-492f738896225c6b3acf7d2f - resource: repo://TFI/Features/Workspaces/MobileWorkspace.swift - id: openwiki-source-4f0f9c14c0bea850182fe19b resource: repo://TFI/Info.plist - id: openwiki-source-25a2df896b93f410289e8512 @@ -56,13 +54,13 @@ sources: resource: repo://TFM/Info.plist - id: openwiki-source-f5ab2303a67ae18cbe2f59f2 resource: repo://TFM/TFM.entitlements -generated: { by: "openwiki/0.7.0", at: "2026-10-09T00:05:26.272Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - by: openwiki/0.7.0 - at: 2026-10-09T00:05:26.272Z + at: 2026-10-10T23:08:14.218Z --- -# Storefront commerce release checks +# Storefront Commerce Release **Three separate authorities:** `StoreKit/Termind.storekit` is a local catalog fixture, App Store Connect defines the live Apple products and introductory offer, and RevenueCat supplies the configured `termind_pro` entitlement and remotely configured paywall. A fixture product or signed CloudKit capability does not grant Pro. Check the RevenueCat dashboard, Apple storefront and native app together before shipping; do not treat local test prices, screenshots or UI strings as production purchase evidence. See [Commerce, localization and product surfaces](../concepts/commerce-localization-and-product-surfaces.md) for the broader gate and copy ownership map. @@ -89,6 +87,10 @@ Signing is a different check: the targets have distinct `TFI/TFI.entitlements` a `ProAccessController.live()` builds a `RevenueCatProPurchaseService` if bundled configuration is valid. The observable main-actor controller is the gate: `canUse` tests current `hasProAccess`, while a denied `require(feature)` presents the paywall. `ProFeature` covers Tailscale, Assistant, remote MCP, the Mac built-in MCP server, Vault sync, unlimited Metrics and optional SFTP/port-forwarding multi-connection acceleration. Ordinary Workspace composition and SSH terminal uploads are free when their normal connection and destination gates pass. Tests and screenshot setups can create `.unrestrictedForTesting`; production must use RevenueCat status. On Mac `.mcpServer` gates the built-in server; mobile uses `.remoteMCP` for remote connections. The enumeration alone is not a platform availability promise. +For Mac release verification, inspect the **composition root**, not the `AppModel` initializer default: `AppModel` defaults to `.unrestrictedForTesting()`, but `TermindApp` explicitly injects `.live()` for normal launches and `.unrestrictedForTesting()` when running tests. The DEBUG screenshot-demo branch also explicitly injects unrestricted access and returns early. Neither a screenshot nor a directly constructed default `AppModel` demonstrates production entitlement enforcement. + +Mac entitlement changes also reconcile runtime services: `accessDidChange` updates Tailscale, Metrics and effective SFTP/port-forwarding connection preferences without requiring a visible settings surface. `RootView` observes status changes to reconcile sync and the built-in MCP server. SFTP integration tests verify that gaining access restores the saved concurrency strategy and losing access uses the free effective strategy without rewriting saved preferences or opening a paywall. Include access gain/loss in release checks, not just initial paywall presentation. + ```mermaid flowchart TD App["Live ProAccessController"] --> Config{"Valid bundled RevenueCat config"} @@ -136,6 +138,12 @@ Purchase and restore callbacks refresh the controller's entitlement status while Both wrappers refuse purchase initiation if access already exists, mark operations processing on purchase/restore start and clear processing on completion, cancellation or failure. Completion refreshes entitlement without closing the sheet: the first RevenueCat dismissal request after purchase is suppressed so the account variant remains visible; the close button or subsequent dismissal can close it. Disappearance clears the processing flag. Mac chooses one owner window for the sheet; iOS presents it from `MobileRootView`. Confirm purchase/restore failures and recovery in real UI rather than assuming controller unit tests exercise SDK purchases. +## Language changes are presentation changes + +On Mac, the language picker persists `AppLanguage` and displays a relaunch notice when it differs from the launch language. `AppLanguage.store` updates the app setting and Apple's `AppleLanguages` override; Follow System removes that override. RevenueCat reads the stored locale when its SDK is first configured, while the native paywall formats its `date` variable with the view's locale. Relaunch before reviewing a changed language; do not assume changing the setting reconfigures an already-running RevenueCat SDK. + +This path does **not** change entitlement resolution, purchase authorization or restore callbacks, and is not a way to reset introductory-offer eligibility. Keep language/copy review separate from account eligibility and transaction validation: verify the actual remote paywall after relaunch, then use the appropriate Sandbox accounts for purchases and restores. The manual StoreKit fixture's English and Chinese strings do not prove RevenueCat dashboard locale coverage. + ## Release validation 1. Reconcile product IDs, types, group, Family Sharing, introductory eligibility and grace settings in App Store Connect **and** entitlement-to-product mapping, offering/paywall rules and localized disclosure in RevenueCat. Test new versus used-offer accounts, trial-to-paid renewal, cancellation through expiry, billing retry with and without grace, refunds, Family Sharing and lifetime restoration across Mac, iPhone and iPad. Record Sandbox results separately from the fixture and production configuration review. diff --git a/openwiki/quickstart.md b/openwiki/quickstart.md index 002d46e6..24d7f28d 100644 --- a/openwiki/quickstart.md +++ b/openwiki/quickstart.md @@ -102,6 +102,8 @@ sources: resource: repo://TFM/App/TermindApp.swift - id: openwiki-source-fe5539ed6f675410b82b41c4 resource: repo://TFM/Features/Terminal/PersistentTerminalSurfaceView.swift + - id: openwiki-source-dad03c68d6b805a4170ffc02 + resource: repo://TFM/Platform/WorkspaceCloseConfirmation.swift - id: openwiki-source-dc041600497a72c229d5eef4 resource: repo://TFM/UI/Components/ConnectionJourneyView.swift - id: openwiki-source-73c75477db0674192248aa00 @@ -116,10 +118,12 @@ sources: resource: repo://TFMTests/VisibleWindowTestTrait.swift - id: openwiki-source-402244fee941f965f41388e4 resource: repo://TFMTests/WebAuthnSSHTests.swift -generated: { by: "openwiki/0.7.0", at: "2026-10-09T00:05:26.272Z" } + - id: openwiki-source-d798805795e92534f3b13250 + resource: repo://TFMTests/WorkspaceCloseConfirmationTests.swift +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - by: openwiki/0.7.0 - at: 2026-10-09T00:05:26.272Z + at: 2026-10-10T23:08:14.218Z --- # Termind Engineering Quickstart @@ -133,7 +137,7 @@ Termind is one Xcode project with native `TFM` (macOS) and `TFI` (iPhone/iPad) a | Task | Guide and focused check | | --- | --- | | Module placement, imports, nested directories, shared resources | [Module and Product Map](architecture/module-map.md) → [System Boundaries](architecture/system-boundaries.md). For placement, use [Shared Module Ownership](architecture/shared-module-directories.md), [Native App Responsibilities](architecture/native-app-directories.md), or [Supporting Systems](architecture/supporting-system-directories.md). Run `ArchitectureBoundaryTests` for dependency changes. | -| Composition, startup failure, window/scene state, backgrounding, quit | [Platform Startup and Runtime Composition](architecture/platform-runtime.md); choose `AppTerminationTests`, `MobileAppTests`, `MobileWorkspaceSessionTests`, or `MobileForwardingLifecycleTests`. | +| Composition, startup failure, window/scene state, backgrounding, quit | [Platform Runtime and Application Lifecycle](architecture/platform-runtime.md); choose `AppTerminationTests`, `WorkspaceCloseConfirmationTests`, `MobileAppTests`, `MobileWorkspaceSessionTests`, or `MobileForwardingLifecycleTests`. | | Desktop Navigator, sidebar, workspaces, Vault, Assistant layout | [macOS UI and Workspace Ownership](architecture/desktop-ui.md); use the relevant window/navigation or layout tests. | | Desktop Workspace/Pane sorting, merging, drag-out windows, Assistant reordering | [TFM Drag and Drop](workflows/desktop-drag-and-drop.md); preserve native Workspace List source and live receiver contracts. Start with `WorkspaceNavigatorDropTests`, `WorkspaceLayoutDropTests`, `WorkspaceWindowMoveTests`, or `AssistantConversationListDropTests`, then the guide's source/cancellation/animation suites. | | Mobile tabs, scene routes, cards, editors, terminal continuity | [iOS and iPadOS UI and Scene Ownership](architecture/mobile-ui.md); use `MobileWorkspaceSessionTests` for window-owned connections/routes, `MobileAdaptiveLayoutTests` or `MobileNavigationParityTests` for UI, and `MobileMetricsJourneyTests` for metrics authentication/navigation. | @@ -189,7 +193,7 @@ These are routing reminders, not replacements for the detailed guides. ### Shutdown, sync, and authentication safety - Mobile phases aggregate across scenes: pause background services only when **all** registered scenes background. Expiration suspends workspaces, stops forwards, pauses transfers and expires Assistant work; an active scene resumes monitoring, memory, workspaces, eligible sync and metrics. Use [Platform Runtime](architecture/platform-runtime.md). -- Closing the last macOS window never terminates the Debug test host; otherwise menu-bar metrics preferences and running metrics decide. Quit rechecks unuploaded SFTP edits and permits cancellation. Confirmed termination fences new Assistant work, drains cancellation/persistence, stops transfers/MCP and closes agent resources. AppKit receives one reply after cleanup or a three-second fallback; the fallback bounds the reply, **not** asynchronous cleanup completion. +- Closing the last macOS window never terminates the Debug test host; otherwise menu-bar metrics preferences and running metrics decide. Quit first asks for application-wide confirmation if any registered, nonclosed window has workspaces, then rechecks unuploaded SFTP edits and permits cancellation at either prompt before shutdown. The workspace confirmation defaults Return to Cancel and requires the explicit destructive action; it is separate from the window-close veto. Confirmed termination fences new Assistant work, drains cancellation/persistence, stops transfers/MCP and closes agent resources. AppKit receives one reply after cleanup or a three-second fallback; the fallback starts only after confirmation and bounds the reply, **not** asynchronous cleanup completion. Use `WorkspaceCloseConfirmationTests` for prompt scope/defaults and window veto, and `AppTerminationTests` for drain ordering and once-only replies; follow [Platform Runtime](architecture/platform-runtime.md) and [Desktop UI](architecture/desktop-ui.md) for implementation details. - `CloudSyncLifecycle` owns a separate engine/account scope per category: persist a recovery fence before automatic syncing, fetch before authorizing uploads, and reconcile pending changes against the durable journal. Pause, account changes and non-cancellation persistence failures retire/cancel the engine outside delegate context; late completions cannot update its replacement. A build is not a sync migration check: use [CloudKit Release Readiness](operations/cloudkit-release-readiness.md) and the lifecycle/safety/coordinator tests. - Shared `ConnectionJourney` tracks route progress and authentication/host-key checkpoints; `ConnectionJourneyRouteView` draws the common route, while apps own prompts/navigation. Use shared state tests, then affected native rendering/metrics fixtures; rendering is not a live SSH test. - Security Key references remain external Vault metadata with compatibility normalization; failed metadata writes/deletes roll back, and software keys cannot use external storage. A forbidden SSH interaction policy blocks destination/jump security-key credentials even with optional user presence. Keep silent auxiliary connections from triggering hardware prompts. diff --git a/openwiki/testing/behavioral-boundaries-and-fixtures.md b/openwiki/testing/behavioral-boundaries-and-fixtures.md index ce196396..b77d6f43 100644 --- a/openwiki/testing/behavioral-boundaries-and-fixtures.md +++ b/openwiki/testing/behavioral-boundaries-and-fixtures.md @@ -1,7 +1,7 @@ --- type: testing strategy -title: Behavioral Tests and Fixtures -description: Map observable shared and platform contracts to deterministic regression fixtures, including terminal Find and mobile search lifecycle coverage. Distinguish native presentation opt-ins, historical-store migration, network seams, and external-package validation. +title: Behavioral Boundaries and Fixtures +description: Route observable behavioral changes to focused regression suites and deterministic fixtures. Distinguish persistence and restore boundaries, context accounting, language migration, independent close and Quit vetoes, and deliberate native-presentation opt-ins. tags: [testing, regression, fixtures, architecture, persistence, commerce, navigation, ui] sources: - id: openwiki-source-ed77bc20b48d193bace3dde6 @@ -172,13 +172,13 @@ sources: resource: repo://TFMTests/VaultSyncTests.swift - id: openwiki-source-a08489338be00125d646c77e resource: repo://TFMTests/VisibleWindowTestTrait.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- -# Behavioral Tests and Fixtures +# Behavioral Boundaries and Fixtures Termind uses Swift Testing to test a behavior at the layer that owns it, rather than treating the test tree as a second implementation. `FrameworkTests/` protects portable framework contracts; `TFMTests/` exercises macOS composition, native interaction, and remote-session behavior; `TFITests/` protects iPhone/iPad composition, scene behavior, and mobile presentation. The targets use `@Suite`, `@Test`, and `#expect`. @@ -241,12 +241,12 @@ Hardware-key compatibility belongs at both the shared and mobile seams. `WebAuth A message edit inside one run needs a fixture such as `opening → assistant/tool batch → steering → assistant/tool batch`, not just one user turn per run. The regression boundary spans several owners: -- `AgentPersistenceTests.sameRunTruncationPreservesThePrefixAndAtomicallyAdoptsTheReplacement` uses equal timestamps and deliberately reversed UUID order. It restores a compacted conversation through the production store and operations, then checks the retained prefix, neutral completed summary, title, attachments, replay rows, invocations/results/reviews and compaction bases. A legacy prefix invocation has no stored source ID and a later timestamp; its known call ownership must still preserve it. An unknown native-tool-like invocation at the exact cutoff must be removed. Real running/completed snapshots must adopt staging once without changing historical owners, and late old snapshots/events must not regrow the suffix. +- `AgentPersistenceTests.sameRunTruncationPreservesThePrefixAndAtomicallyAdoptsTheReplacement` uses equal timestamps and deliberately reversed UUID order. It restores a compacted conversation through the production store and operations, then checks the retained prefix, neutral completed summary, title, attachments, replay rows, invocations/results/reviews and compaction bases. A legacy prefix invocation has no stored source ID and a later timestamp; its known call ownership must still preserve it and restore its source message ID. This fixture does not seed an unknown native-tool-like invocation at the cutoff; do not infer that coverage from the prefix case. Real running/completed snapshots must adopt staging once without changing historical owners, and late old snapshots/events must not regrow the suffix. - `editingALaterRunOpeningKeepsAnEarlierEmptyRunBeforeTheReplacement` checks an earlier retained run with no messages and a replacement clock older than the retained run. Live and restored staging order must agree. Ordinary replay fixtures give genuinely later runs a later start time instead of relying on random UUID tie order. - Shared `AgentReplayPersistenceTests` parameterizes the first real replacement snapshot as `.failed` or `.interrupted`. It checks the single surviving message/run/replay backing and reloads with a new `ModelContext` and repository over the same in-memory container to verify provenance, terminal state, failure and frozen context. This is fresh-context visibility, not a disk-relaunch test. - `ChatComposerStateTests.editingSteeringKeepsCompactedSameRunPrefixAndLegacyToolOwnership` checks full visible-prefix retention even when model history is compacted, alongside tool cards, title, plans, compaction records/marks and run-summary chronology through `runStarted`/`runFinished`. Mobile's `swiftDataRestoreAndEditPreserveASameRunPrefixOnMobile` exercises the same repository/store/operations path and compares live and restored message ownership. -Application error and timing fixtures complement those real SwiftData checks. `AssistantConversationOperationsTests.invalidReplacementNeverCallsPersistence` rejects a non-user replacement without invoking truncation; `truncationFailureLeavesInMemoryHistoryUnchanged` throws from a rejecting repository and keeps both visible messages and transcript unchanged. In `AssistantExecutionControllerTests`, `manualCompactionIsRefusedWhileRunPreparationIsActive` holds the memory snapshot barrier and verifies no compaction activity starts; `editFailurePreservesHistoryButUsesTheSynchronouslyCapturedReplacement` checks that rejected truncation preserves history while the attempted replacement retains captured host and plan context. `cancellationAfterSuccessfulEditAdoptsTheInterruptedRunProvenance` holds memory preparation after the edit commits, then checks that the interrupted real run owns the replacement rather than its staging ID. These port-level failure seams do not inject disk-full or read-only failure into a real SwiftData transaction; its rollback path must not be described as fault-injection tested. +Application error and timing fixtures complement those real SwiftData checks. `AssistantConversationOperationsTests.invalidReplacementNeverCallsPersistence` rejects a non-user replacement without invoking truncation; `truncationFailureLeavesInMemoryHistoryUnchanged` throws from a rejecting repository and keeps both visible messages and transcript unchanged. In `AssistantExecutionControllerTests`, `manualCompactionIsRefusedWhileRunPreparationIsActive` holds the memory snapshot barrier and verifies no compaction activity starts; `editFailurePreservesHistoryButUsesTheSynchronouslyCapturedReplacement` checks that rejected truncation preserves history while the attempted replacement retains captured host and plan context. `cancellationAfterSuccessfulEditAdoptsTheInterruptedRunProvenance` holds memory preparation after the edit commits, then checks that the interrupted real run owns the replacement rather than its staging ID. These port-level failure seams do not inject disk-full or read-only failure into a real SwiftData transaction; its rollback path must not be described as fault-injection tested. The repository's truncation catch does call `context.rollback()`, restores its previous `deletedRunIDs` fence and rethrows. `AgentPersistenceTests.truncationRejectsAMessageOutsideThePersistedConversation` separately checks a real persisted-target rejection leaves the original message intact; it is not a failing-save test. Use the boundary that the assertion needs as its wait condition. `beginFailedRunPersistence` constructs a failed snapshot and starts a repository-save task, but keeps preparation active and does **not** project `.failed` into the live conversation until `save` succeeds; on save failure it appends an error message instead. `missingModelFailureStaysOwnedUntilSaveAndAllCancellationPathsJoinIt` and `missingCredentialAfterMemoryPreparationProjectsOnlyAfterSaveCompletes` block the first save and verify stop, cancellation, and projection ordering. `credentialAddedAfterCaptureDoesNotReviveTheTurn` and `selectingAnotherConversationDoesNotCancelPreparation` check live `.failed` after preparation and additionally wait for the recording repository's failed snapshot. The fixture's `waitUntilSnapshot(state:)` checks existing snapshots or suspends an actor-owned continuation; `save` records the attempt before resuming matching waiters. A recorded attempt is **not** proof of a completed save: use `completedSnapshots` or the joined preparation where completion is the contract. This does not prove durability across the separate queued-runtime immediate-stop handoff. @@ -267,7 +267,7 @@ sequenceDiagram end ``` -The captured preparation-failure boundary is the repository save, not merely entry into the recording fixture's `save` method. +The captured preparation-failure boundary is the repository save, not merely entry into the recording fixture's `save` method. The real SwiftData queued-cancellation fixture additionally asserts **zero provider calls**, one `.cancelled` real run, removal of the staging summary, captured payload restoration and `.fullAccess` persistence. Both queued cancellation and captured configuration failure check that the real run starts no earlier than staging, finishes no earlier than its start, and bounds the replacement's message clock. Wait on `stopAndWait()` before constructing the fresh repository; a live cancellation flag alone does not establish completed persistence. `archiveAndDeleteWaitForCapturedFailurePersistence` additionally waits on the blocked save before mutating metadata or deleting the conversation. `capturedFailureSaveOutlivesReleasedControllerAndConversationHandles` releases both external references while the save is held, then checks completion before they deallocate. These tests protect ownership through the persistence handoff, not process-crash durability. The controller's `failedReplacementSaveCannotBeSupersededOrMutatedBeforeItCompletes` holds a failed-run save after truncation: edit/fork cannot bypass the pending preparation, and after release the failed real run takes the replacement's ownership. `failedSnapshotSaveRetiresPreparationWithoutAdoptingStaging` injects a save error at the repository port instead: the staging owner remains, no failed summary is adopted, an assistant error appears, and no save completes. When changing edit/cancel ordering, also run `cancelledPreparedEditCannotEnterHistoryMutation`, `supersededPreparedEditCannotEnterHistoryMutation`, `cancellationDuringEditJoinsHistoryWorkBeforeRecordingInterruption`, and `supersededPreparationIgnoresAnOlderCancellationCompletion` to keep stale operations from mutating the new history. @@ -394,6 +394,22 @@ The mobile Metrics suite is `.serialized` and **key-window-gated** through `.req `MobileSettingsPortTests` constructs `MobileAppModel(inMemory: true)` and tests that changing shared SFTP preferences updates transfer limits and strategy immediately—even without opening Settings—and that an applied remote snapshot updates the same coordinator. The small direct `SFTPTransferCoordinator` test verifies invalid limits clamp to usable values. The test app's active scene must not start Vault, History, Device or Settings cloud engines. For provider editing, injected `AISecretStoring` and `AIConfigurationPersisting` failures show that a failed secret write leaves the draft and avoids metadata persistence, and a metadata failure restores the previous secret. A concurrent model refresh survives editing, remote deletion cannot be resurrected by an open draft, and controlled discovery ignores a late response after cancellation or provider deletion. Run these ordinary, no-key-window tests before any settings UI rendering suite. +### Context accounting: compare an incremental projection with its reference + +Run `AgentTokenUsageTests` when changing provider usage parsing or the context gauge. Cached input is a subset of the total for OpenAI/Google, but Anthropic's input total adds cache-read and cache-creation counts. Empty-choice or absent-candidate usage chunks must still parse; streaming Chat Completions requests ask for usage while buffered requests omit `stream_options`. Cumulative usage merges by per-field maximum rather than summing chunks. + +The estimator separates system prompt, schemas, conversation, tool results and attachments. Measured input-plus-output rescales the parts to sum exactly to the displayed total; unknown context windows produce no share and overfull shares clamp to one. `incrementalTrackerMatchesTheReferenceEstimator` compares streaming/upsert, measurement and removal against the reference estimator. `conversationSnapshotTracksAttachmentsHistoryEditsAndUsage` checks the production `ConversationModel` projection after attachments, tool completion, edits, compaction, usage and fork. `composerDraftEditsNeverRebuildTheContextEstimate` protects a different contract: 100 draft revisions over 300 historical messages neither rebuild nor change the estimate. These are accounting and invalidation regressions, not tokenizer accuracy or live-provider billing validation. + +### Language migration: verify stored policy and resolved copy separately + +Use `AppLanguageTests` without a window gate. Explicit choices write canonical `en`, `ja`, `zh-Hans` or `zh-Hant` to `AppleLanguages`; Follow System removes that override. Legacy Simplified Chinese identifiers migrate both the preference and override to `zh-Hans`. Unknown values and legacy Traditional Chinese preference identifiers remain unchanged and follow system; this differs from **system locale resolution**, where Traditional Chinese regional locales resolve the Traditional catalog. The suite checks separate Chinese fallback, Japanese interpolation, selected-language relaunch notices, ordered forwarding arguments and catalog plural rules. A catalog-only check is not evidence that an already-running native view changes language without relaunch. + +### Window close and Quit: independent scopes, independent vetoes + +`WorkspaceCloseConfirmationTests` is serialized but **offscreen**: it constructs `NSAlert` and `NSWindow`, injects confirmation results and invokes delegate methods without making a key or visible window. Do not opt it out merely because it uses AppKit. It checks distinct application/window copy, Cancel as Return's default, an explicit destructive second-button response as the only approval, all-registered-window workspace aggregation, and an empty window closing without confirmation. A cancelled window close preserves the workspace and can be retried; the forwarded delegate can independently veto before confirmation, unrelated delegate callbacks forward, and detach restores the original delegate. + +Production Quit is owned separately by `TermindApp.applicationShouldTerminate`: it returns `.terminateLater`, asks the application-scoped workspace confirmation, then rechecks pending SFTP edits and may veto again before starting shutdown. A window-close delegate test does not exercise that app termination path. For shutdown ordering, `AppTerminationTests` holds terminal persistence and verifies cancellation of every stored conversation before waiting, then background-command and connection closure after drains, with repeated cleanup occurring once. `SheetApplicationTerminationTests` checks offscreen mounting/moving/removal restores each window's original modal termination policy. Only `SheetQuitInteractionTests` deliberately makes windows key, focuses sheet fields and sends native Command-Q; it is serialized and `.requiresVisibleWindow`, using a cancelling delegate so the test host does not terminate. Use that opt-in for native Quit reachability, not as proof of real production shutdown or remote paywall layout. + ## Focused additions: drafts, confirmations and resource retirement - `SnippetColorTests` checks tolerant legacy JSON, unsaved draft isolation, duplicate color, V1/V2 wire/tombstone behavior and a real V50 upgrade through V51 to the current V52 schema followed by saved-color reopen. @@ -461,7 +477,21 @@ Start with the smallest deterministic regression named in the matrix (for exampl xcodebuild -project Termind.xcodeproj -scheme TFM -destination 'platform=macOS' -onlyUsePackageVersionsFromResolvedFile test ``` -For mobile changes, build/test the `TFI` scheme against an installed iPhone or iPad simulator. Keep ordinary framework tests separate from key-window opt-ins. For example, run `-only-testing:FrameworkTests/TerminalFindControllerTests` without a UI environment flag first, then deliberately opt in to the native search suite: +For assistant edit, restore and accounting changes, begin with these ordinary suites (no native-window opt-in): + +```sh +xcodebuild -project Termind.xcodeproj -scheme TFM -destination 'platform=macOS' -onlyUsePackageVersionsFromResolvedFile test -only-testing:FrameworkTests/AgentReplayPersistenceTests -only-testing:FrameworkTests/AssistantConversationOperationsTests -only-testing:FrameworkTests/AssistantExecutionControllerTests -only-testing:TFMTests/AgentPersistenceTests -only-testing:TFMTests/ChatComposerStateTests -only-testing:TFMTests/AgentTokenUsageTests +xcodebuild -project Termind.xcodeproj -scheme TFM -destination 'platform=macOS' -onlyUsePackageVersionsFromResolvedFile test -only-testing:TFMTests/AppLanguageTests -only-testing:TFMTests/WorkspaceCloseConfirmationTests -only-testing:TFMTests/SheetApplicationTerminationTests -only-testing:TFMTests/AppTerminationTests +xcodebuild -project Termind.xcodeproj -scheme TFM -destination 'platform=macOS' -onlyUsePackageVersionsFromResolvedFile test -only-testing:TFMTests/RemoteSessionAuthenticationContinuationTests +``` + +Filter further to the named test method for the first regression iteration. For real sheet focus and native Quit reachability, deliberately enable the host's `TERMIND_WINDOW_TESTS` gate: + +```sh +TEST_RUNNER_TERMIND_WINDOW_TESTS=1 xcodebuild -project Termind.xcodeproj -scheme TFM -destination 'platform=macOS' -onlyUsePackageVersionsFromResolvedFile -parallel-testing-enabled NO test -only-testing:TFMTests/SheetQuitInteractionTests +``` + +A skipped opt-in case is not a validated native interaction; verify the test host receives the flag. For mobile changes, build/test the `TFI` scheme against an installed iPhone or iPad simulator. Keep ordinary framework tests separate from key-window opt-ins. For example, run `-only-testing:FrameworkTests/TerminalFindControllerTests` without a UI environment flag first, then deliberately opt in to the native search suite: ```sh TEST_RUNNER_TERMIND_MOBILE_UI_TESTS=1 xcodebuild -project Termind.xcodeproj -scheme TFI -destination 'platform=iOS Simulator,id=' -onlyUsePackageVersionsFromResolvedFile -parallel-testing-enabled NO test -only-testing:TFITests/MobileTerminalSearchIntegrationTests diff --git a/openwiki/testing/index.md b/openwiki/testing/index.md index 2c3a130b..a161b812 100644 --- a/openwiki/testing/index.md +++ b/openwiki/testing/index.md @@ -1,3 +1,3 @@ # Files -- [Behavioral Tests and Fixtures](behavioral-boundaries-and-fixtures.md) - Map observable shared and platform contracts to deterministic regression fixtures, including terminal Find and mobile search lifecycle coverage. Distinguish native presentation opt-ins, historical-store migration, network seams, and external-package validation. +- [Behavioral Boundaries and Fixtures](behavioral-boundaries-and-fixtures.md) - Route observable behavioral changes to focused regression suites and deterministic fixtures. Distinguish persistence and restore boundaries, context accounting, language migration, independent close and Quit vetoes, and deliberate native-presentation opt-ins. diff --git a/openwiki/workflows/desktop-drag-and-drop.md b/openwiki/workflows/desktop-drag-and-drop.md index 0b2f5c6d..248db8cc 100644 --- a/openwiki/workflows/desktop-drag-and-drop.md +++ b/openwiki/workflows/desktop-drag-and-drop.md @@ -1,6 +1,6 @@ --- type: interaction workflow -title: Desktop workspace drag and drop +title: Desktop Drag and Drop description: Native desktop workspace and pane layout dragging, including drop routing, cancellation, and deferred ownership transfer to a new window. Distinguishes these moves from pane file drops and Assistant conversation reordering. tags: [macos, tfm, drag-and-drop, workspaces, panes, assistant, appkit, lifecycle, testing] sources: @@ -12,12 +12,8 @@ sources: resource: repo://FrameworkTests/AssistantConversationOperationsTests.swift - id: openwiki-source-4dbea0ff2808163cb4ecdd8f resource: repo://TFM/App/AppModel.swift - - id: openwiki-source-873894299539cce9a737a860 - resource: repo://TFM/App/RootView.swift - id: openwiki-source-6570374eaac2e0278fa84640 resource: repo://TFM/App/TermindApp.swift - - id: openwiki-source-6a1a95f7d01cc8e15a397f6b - resource: repo://TFM/App/TermindCommands.swift - id: openwiki-source-d35453512ef488d0cacdc06e resource: repo://TFM/Features/Assistant/AssistantView.swift - id: openwiki-source-564c1a0cc523e70840b9fc66 @@ -30,6 +26,8 @@ sources: resource: repo://TFM/Features/Workspaces/WorkspaceNavigatorView.swift - id: openwiki-source-22e92f96394f292a731fc6fb resource: repo://TFM/Platform/MainWindowCoordinator.swift + - id: openwiki-source-dad03c68d6b805a4170ffc02 + resource: repo://TFM/Platform/WorkspaceCloseConfirmation.swift - id: openwiki-source-6bb48fd98bdef02c39562aed resource: repo://TFM/UI/Components/WorkspaceDragCancellationTracker.swift - id: openwiki-source-29722107a3a9d938ca19ebb4 @@ -54,13 +52,13 @@ sources: resource: repo://TFMTests/WorkspaceNavigatorDropTests.swift - id: openwiki-source-a48980e81bab99fcd643df54 resource: repo://TFMTests/WorkspaceWindowMoveTests.swift -generated: { by: "pi", at: "2026-10-06T18:02:45.153Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - by: openwiki/0.7.0 - at: 2026-10-06T18:02:45.153Z + at: 2026-10-10T23:08:14.218Z --- -# Desktop workspace drag and drop +# Desktop Drag and Drop TFM drag behavior spans SwiftUI row interaction, AppKit's native tracking loop, temporary layout previews and live session ownership. Changing one of these layers can break another without changing the final model operation. In particular, **Workspace rows retain the native SwiftUI List drag source**; the separate Pane source is not a replacement for it. @@ -185,6 +183,7 @@ sequenceDiagram participant Scene as Main WindowGroup participant Source as Source WindowModel participant Bridge as MainWindowRegistration + participant Native as Destination NSWindow participant Target as Target WindowModel Drag->>Coordinator: moveWorkspace or movePane with release point Coordinator->>Scene: createScene with request UUID @@ -192,6 +191,9 @@ sequenceDiagram Scene->>Target: Construct fresh model Scene->>Coordinator: configure model with requested layout Scene->>Bridge: Attach native window to RootView bridge + Bridge->>Native: Attach forwarding close delegate + Bridge->>Native: Observe and clear edited decoration + Bridge->>Native: Establish full-size content coordinates Bridge->>Coordinator: register native window and request UUID Coordinator->>Source: takeWorkspaceForMove or takePaneForMove Source-->>Coordinator: Existing workspace or pane wrapped in workspace @@ -200,12 +202,16 @@ sequenceDiagram Coordinator->>Target: Bump sidebar layout generation for move ``` -Scene creation and native registration are distinct phases; the diagram's transfer happens at registration, not at the drop or initial SwiftUI layout. +Scene creation and native registration are distinct phases; the diagram's transfer happens at registration, after native close-delegate attachment and content-coordinate setup, not at the drop or initial SwiftUI layout. Request-to-registration transfer keeps the live session in its source until the destination has a native window. `TermindCommands` installs the `openWindow(id: "main", value: id)` scene factory for the UUID-valued main `WindowGroup`. On the normal workspace scene path (after the diagnosis-consent gate), `TermindApp.makeWindow` creates a fresh `WindowModel` and applies the request's sidebar presentation and preferred width before constructing `RootView`. Its `MainWindowRegistration` bridge calls `register` only after AppKit attaches the native window; the registration view establishes full-size content coordinates before sizing. This keeps scene creation separate from live ownership transfer. +On native attachment, `MainWindowRegistrationView` installs its retained `WorkspaceWindowCloseDelegate` and observes `NSWindow.isDocumentEdited` with initial and new-value delivery, clearing it whenever it becomes true. Workspaces are not documents: suppressing edited decoration does **not** bypass close confirmation. Each registration attempt reattaches the proxy if needed before calling the coordinator. The proxy forwards other delegate callbacks to the existing delegate, honors that delegate's close veto first, and asks for window-scoped confirmation only when the model is still open and owns workspaces. Detaching restores the forwarded delegate only if the proxy is still installed. This native dismissal guard does not change window-local drag ownership, stable Workspace/Pane identity, or move lifecycle semantics; an empty source after its last transfer needs no workspace confirmation. + +Evidence: [native attachment and edited decoration](../../TFM/Platform/MainWindowCoordinator.swift#L589-L628), [forwarding close delegate](../../TFM/Platform/WorkspaceCloseConfirmation.swift#L33-L77), [close veto and forwarding tests](../../TFMTests/WorkspaceCloseConfirmationTests.swift#L56-L109). + Live layout ownership changes only when the requested destination window registers. The request stores a weak source, the move identity, a layout snapshot (ordinary content size, sidebar width and presentation, fullscreen state and source-screen center), and an optional release point. Repeated requests for the same source and move reuse the pending request ID; different pane requests may register in reverse order. A closed source, missing scene factory, nonexistent workspace, nonexistent or noncombinable pane cannot initiate a move. If the source or pane disappears before registration, the pending request cannot recreate it. `register` rejects a closed destination and duplicate native/model pair, then removes the pending request when a new destination consumes its ID, **even if extraction finds no live layout**. A late scene may therefore register empty; registering the same destination again cannot transfer a second copy. @@ -218,7 +224,7 @@ Closing the source window removes its pending requests. A pane closed before reg New windows use the source's last valid ordinary AppKit **content** size, not its fullscreen frame; if no source size was recorded, the shared default applies. During scene construction `configure` applies sidebar presentation and preferred width; registration reapplies them (and invalidates initial sidebar layout if they differ), sizes the native window, then bumps `sidebar.initialLayoutGeneration` for a move to rebuild the split view at the transferred width. For a release point, `applySize` selects the screen containing that point, centers the frame horizontally on it and places its top at the point's Y coordinate, then `fittingFrame` clamps its size and origin inside that screen's visible frame. The minimum content dimensions are enforced before fitting, so a release near a screen edge may shift the resulting window rather than leave it exactly centered on the cursor. Destination registration disables native opening animation for requests with a release point; the tests' `nativeWindow` fixture already has `.none` set and checks placement and layout rather than verifying that assignment separately. If the source was fullscreen, the destination also defers fullscreen activation until it becomes key. A restored scene without a pending request is left at its system-restored frame. -Evidence: [request and layout snapshot](../../TFM/Platform/MainWindowCoordinator.swift#L214-L265), [request consumption and registration](../../TFM/Platform/MainWindowCoordinator.swift#L291-L350), [placement, fullscreen and close](../../TFM/Platform/MainWindowCoordinator.swift#L352-L436), [frame fit and registration bridge](../../TFM/Platform/MainWindowCoordinator.swift#L517-L576), [scene factory](../../TFM/App/TermindCommands.swift#L29-L32), [scene construction and consent gate](../../TFM/App/TermindApp.swift#L221-L249), [RootView bridge](../../TFM/App/RootView.swift#L75-L89), [model extraction and close](../../TFM/Features/Workspaces/WindowModel.swift#L1363-L1409), [move regressions](../../TFMTests/WorkspaceWindowMoveTests.swift#L11-L249). +Evidence: [request and layout snapshot](../../TFM/Platform/MainWindowCoordinator.swift#L257-L299), [request consumption and registration](../../TFM/Platform/MainWindowCoordinator.swift#L329-L390), [placement, fullscreen and close](../../TFM/Platform/MainWindowCoordinator.swift#L392-L470), [frame fit and registration bridge](../../TFM/Platform/MainWindowCoordinator.swift#L565-L628), [scene factory](../../TFM/App/TermindCommands.swift#L31-L34), [scene construction and consent gate](../../TFM/App/TermindApp.swift#L224-L252), [RootView bridge](../../TFM/App/RootView.swift#L75-L85), [model extraction and close](../../TFM/Features/Workspaces/WindowModel.swift#L1385-L1430), [move regressions](../../TFMTests/WorkspaceWindowMoveTests.swift#L11-L254). ## Assistant conversation reordering @@ -243,6 +249,7 @@ Evidence: [active list routing](../../TFM/Features/Assistant/AssistantView.swift | Return animation or data-source forwarding/replacement | `WorkspaceNavigatorDragAnimationTests`; native split-workspace tear-off also asserts the animation flag at session start. | | Whole split Workspace fails to tear off | `WorkspaceNavigatorDropTests.draggingASplitWorkspaceOutsideItsSourceMovesTheWholeLayout()`: release over another window, one request, original Workspace and pane IDs. | | Ownership loss, reconnect, changed history or late request | `WorkspaceWindowMoveTests`: destination delay, duplicate requests, lone-pane identity, closed sources, reversed arrival order and window placement. | +| Native close veto or lost delegate callbacks after registration | `WorkspaceCloseConfirmationTests`: veto preserves the same workspace, retry, forwarded veto/callbacks, delegate restoration and empty-window dismissal. These tests exercise the proxy directly; the move tests register through the coordinator, not the registration view's edited-state observation. | | Assistant hidden receiver or active-row eligibility | `AssistantConversationListDropTests`; `AssistantConversationOperationsTests` covers persistence failure and preserved activity; `WindowStateTests` covers stable manual order. | Run the offscreen policy, geometry and lifecycle cases without visible-window opt-in first, keeping dependency resolution pinned. The suite selection below also includes three `WorkspaceNavigatorDropTests` mouse/session cases that will be skipped by default; an `NSWindow` fixture alone does not require the gate if it stays offscreen. diff --git a/openwiki/workflows/index.md b/openwiki/workflows/index.md index abf1f240..afd92921 100644 --- a/openwiki/workflows/index.md +++ b/openwiki/workflows/index.md @@ -1,8 +1,8 @@ # Files -- [Desktop workspace drag and drop](desktop-drag-and-drop.md) - Native desktop workspace and pane layout dragging, including drop routing, cancellation, and deferred ownership transfer to a new window. Distinguishes these moves from pane file drops and Assistant conversation reordering. +- [Desktop Drag and Drop](desktop-drag-and-drop.md) - Native desktop workspace and pane layout dragging, including drop routing, cancellation, and deferred ownership transfer to a new window. Distinguishes these moves from pane file drops and Assistant conversation reordering. - [File Drag and Drop](file-drag-and-drop.md) - How desktop file URLs and private drag payloads are validated and dispatched to terminal path insertion, SSH/SFTP uploads, or remote copies, alongside mobile and other file-drop destinations. -- [Port Forwarding: Multi-Connection Pool and Flow Dispatch](port-forwarding-pool-and-dispatch.md) - Trace forwarding rules through Host-scoped intent, primary and auxiliary SSH workers, local/SOCKS socket dispatch, remote-listener ownership, bounded flow pumps, silent recovery, and ordered teardown. -- [Settings, Vault, Metrics, History, Device, and Cloud Sync](settings-and-cloud-sync.md) - Per-category CloudKit replication, durable journals, compatibility fences, fetch-before-send authorization, account and environment transitions, and recovery for Termind configuration and activity data. +- [Port Forwarding Pool and Dispatch](port-forwarding-pool-and-dispatch.md) - Trace app-owned forwarding requests through Host-scoped intent, feature-owned authentication, pooled SSH flow dispatch, silent recovery, and lifecycle cleanup. +- [Settings and Cloud Sync](settings-and-cloud-sync.md) - Settings mutation and persistence, device-local desktop language selection, and app-wide ownership of CloudKit synchronization, journals, recovery, and secret boundaries. - [SFTP Transfer, Scheduling, and External Edit Workflow](sftp-transfer-and-external-edit.md) - How browser SFTP jobs are scheduled and resumed, how Agent SFTP leases reuse authenticated SSH connections, and how MCP uploads and external edits remain separate workflows. - [SSH session lifecycle](ssh-session-lifecycle.md) - Traces host selection, one-shot SSH preparation, routed trust and authentication, transport ownership, and desktop and mobile workspace teardown. Distinguishes window sessions from app-owned Agent connections and mobile restoration intent. diff --git a/openwiki/workflows/port-forwarding-pool-and-dispatch.md b/openwiki/workflows/port-forwarding-pool-and-dispatch.md index 8aa46ddb..0f6103ac 100644 --- a/openwiki/workflows/port-forwarding-pool-and-dispatch.md +++ b/openwiki/workflows/port-forwarding-pool-and-dispatch.md @@ -1,8 +1,11 @@ --- type: implementation workflow -title: "Port Forwarding: Multi-Connection Pool and Flow Dispatch" -description: "Trace forwarding rules through Host-scoped intent, primary and auxiliary SSH workers, local/SOCKS socket dispatch, remote-listener ownership, bounded flow pumps, silent recovery, and ordered teardown." +title: Port Forwarding Pool and Dispatch +description: "Trace app-owned forwarding requests through Host-scoped intent, feature-owned authentication, pooled SSH flow dispatch, silent recovery, and lifecycle cleanup." tags: [ssh, port-forwarding, connection-pool, scheduling, socks5, backpressure, recovery] +verified: + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z sources: - id: openwiki-source-eeeeca9ded05fd2db5c3240d resource: repo://Frameworks/TermindDomain/Transfers/MultiConnectionPolicy.swift @@ -18,17 +21,18 @@ sources: resource: repo://FrameworkTests/SSHInteractionPolicyTests.swift - id: openwiki-source-4dbea0ff2808163cb4ecdd8f resource: repo://TFM/App/AppModel.swift + - id: openwiki-source-f1d91230931f6a1921af6d03 + resource: repo://TFM/Features/Ports/PortForwardingView.swift + - id: openwiki-source-e22355480144bf1b83a6cbde + resource: repo://TFMTests/PortForwardingJourneyTests.swift - id: openwiki-source-7468ad597251e97aa63a1c55 resource: repo://TFMTests/PortForwardingReconnectTests.swift - id: openwiki-source-c70098228d301514a5a44981 resource: repo://TFMTests/SSHForwardingWireTests.swift -generated: { by: "pi", at: "2026-10-04T14:45:13.763Z" } -verified: - - by: openwiki/0.7.0 - at: 2026-10-06T18:02:45.153Z +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } --- -# Port Forwarding: Multi-Connection Pool and Flow Dispatch +# Port Forwarding Pool and Dispatch ## Two scheduling decisions, three kinds of rule @@ -42,12 +46,20 @@ Forwarding first decides **which physical SSH connection owns a newly accepted T The pool is **not** several sockets jointly carrying one TCP stream. Every flow keeps one worker/channel until closure; existing flows are not migrated or replayed if that worker fails. Remote forwarding does not become multi-worker just because a Host's maximum is greater than one. +## Feature dispatch and app-owned lifetime + +On macOS, `AppModel.forwarding` owns the runtime controller; `AppModel.forwardings` is the saved-rule library. The Ports UI opens the editor and calls `app.forwarding.start(rule.id)`. Editors in different windows resolve the same controller context, so navigating away or closing editor work does not cancel another window's authentication or stop the transport. Explicit Journey Cancel is different: it calls `stop` and then closes the editor. This separation keeps forwarding lifetime independent of a terminal pane or editor's visibility. + +The forwarding controller owns its authentication continuation and reconnect policy. The embedded Journey submits credentials, host-key decisions, and Retry to that controller with captured prompt/request IDs. It does not route forwarding authentication through app-global `ExternalAuthentication` or read app settings itself; architecture boundary tests guard that separation. Interactive Start/Retry and forbidden-interaction recovery remain distinct boundaries, as described below. + ## Intent, configuration, and runtime ownership `PortForwardingController.start` validates a saved rule and SSH-enabled Host. It creates or joins a `PortForwardingConnection` context keyed by Host ID; the context contains the route snapshot, shared Journey/authentication surface, connection attempt, active rule presentations, and an effective maximum computed when the context starts. App-supplied limit changes can update that maximum on a live context without rewriting the saved Host setting. Later rules on the same connected context call `startRule` instead of authenticating again. Stop removes that rule's generation; the last active rule retires the context. Route changes, including jumps, invalidate affected contexts. Saved rules themselves are not live listeners. macOS `AppModel` selects `.hostConfigured`; Host values normalize to `1...64`, default `1`, and the controller caps that saved value by an app-supplied connection limit. Without Pro multi-connection access the effective limit is one; entitlement changes update the limit on existing contexts without changing the saved Host preference. The `.hostConfigured` factory uses `SSHForwardingConnectionPool` even when its current target is one. Mobile composition uses the controller's default `.singleConnection` policy, so a synchronized Host setting can remain stored on mobile without producing the desktop multi-connection topology. +Lowering a live limit does not evict streams already using excess published auxiliaries. The pool marks excess auxiliaries draining, excludes them from new assignments, and disconnects them only after the worker reports no active flows and no assignment is in flight. Pending auxiliary opens/installations can be cancelled immediately. Increasing the limit can retain a draining slot again before it becomes idle. + Three runtime numbers must be read separately: - `activeConnections`: registered flows for one rule, aggregated across slots; these can include handshake/open/connect phases, not only streams already forwarding bytes. @@ -78,6 +90,8 @@ sequenceDiagram Pool->>Primary: or auxiliary acceptShared ``` +The sequence shows primary authentication, shared-listener setup, and auxiliary publication before flow dispatch. + `connect` first disconnects any previous pool and opens only the primary. Configuring the additional-request resolver calls `fillPool`, but without a local/dynamic registration there is no auxiliary demand. Starting a local/dynamic rule registers it on the current slots, creates **one** listener at the pool level, then fills missing slots. A remote-only rule calls `primary.connection.start` and needs no auxiliaries. `fillPool` computes: @@ -96,7 +110,7 @@ Stopping the last non-remote rule removes auxiliary demand, cancels the retry ti The pool-local listener accepts up to 32 descriptors in one callback. `assign` then follows this algorithm: 1. Match the rule's current registration generation; close the descriptor if obsolete. -2. Select published slots whose installed-rule set contains that rule ID. +2. Select published slots whose installed-rule set contains that rule ID, excluding draining slots. 3. Find the minimum `counts[slotID][ruleID]`, treating absent counts as zero. 4. Sort equal-minimum candidates by UUID string; choose `roundRobin % candidateCount`, then increment the pool-wide counter. 5. Call that worker's `acceptShared`. The worker rechecks connection, rule, generation, non-stopped state and non-remote kind before registering the flow. If it rejects, the pool closes the socket; it does not retry assignment across every slot. @@ -154,8 +168,39 @@ There are several different tokens, none interchangeable: This is not stream failover: recovery restores listeners and future connection opportunities, not application protocol state or bytes already sent on failed TCP streams. +### Application termination is not a forwarding drain barrier + +`AppModel.shutdownForTermination()` stores an asynchronous cleanup task, then performs synchronous feature shutdown, including `forwarding.stopAll()`, before returning that task. Forwarding is stopped in the calling method, **not** after awaiting MCP or agent cleanup inside the task. `stopAll` immediately removes runtime intent and invalidates generations; rule closure and Host transport disconnect run in controller-owned tasks. The returned termination task does not explicitly join those forwarding cleanup tasks. + +```mermaid +sequenceDiagram + participant Caller + participant App as AppModel + participant Forwarding as Forwarding controller + participant Cleanup as Termination task + Caller->>App: shutdownForTermination + App->>App: cancel pending runs and stop conversations + App->>Cleanup: create and store cleanup task + App->>App: close windows and cancel transfers + App->>Forwarding: stopAll + Forwarding->>Forwarding: retire intent and schedule transport cleanup + App-->>Caller: return termination task + Cleanup->>Cleanup: stop MCP and await SFTP cancellation concurrently + Cleanup->>Cleanup: await preparation and conversation drains + Cleanup->>Cleanup: join MCP and SFTP cleanup + Cleanup->>Cleanup: close agent background commands + Cleanup->>Cleanup: close agent connections + Cleanup->>Cleanup: shut down Tailscale +``` +The diagram separates synchronous forwarding retirement from the asynchronous agent/MCP cleanup chain; it is not a forwarding-disconnect completion guarantee. + +The cleanup task waits for preparation cancellation and conversation drains, joins MCP/SFTP cleanup, closes agent background commands before agent connections, and finally shuts down Tailscale. Repeated termination calls reuse the stored task. See [Platform Runtime](../architecture/platform-runtime.md) for the broader application lifetime boundary. + ## Focused tests, limits, and troubleshooting +- `PortForwardingJourneyTests` checks shared authentication across windows, editor navigation without cancellation, jump-host deletion, explicit-only Start, peer-preserving Stop, and rejection of retired forms and callbacks. +- `ArchitectureBoundaryTests.forwardingAndMobileAuthenticationRemainFeatureOwned` guards against controller dependencies on settings/global external authentication and checks forbidden-interaction reconnect wiring. +- `AppTerminationTests` checks conversation drains before agent resource closure, background-command closure before connection closure, and reuse of termination cleanup. Forwarding retirement placement is established by `AppModel` and controller source, not by a forwarding-drain assertion in that suite. - `SSHForwardingPumpTests` is the deterministic low-level seam: short writes, stable retry bytes, ready-queue order, watermarks, half-close, zero-window wakeups, encrypted-buffer allowance and source cancellation. - `PortForwardingReconnectTests` uses controlled connections/timers to check shared context recovery, silent authentication, remembered versus once-only trust, rule generations, late results, and cleanup-before-rebind ordering. - `SSHForwardingWireTests` is **opt-in** via `TERMIND_SSH_FIXTURE`, a path to the fixture JSON. It covers local/remote/dynamic streams with 1/16/64 concurrency, one/two jump bridges, slow consumers, listener restart, and local pool distribution. Source presence or a default suite pass does not mean these wire tests ran. diff --git a/openwiki/workflows/settings-and-cloud-sync.md b/openwiki/workflows/settings-and-cloud-sync.md index 3e22b9da..2cc9078a 100644 --- a/openwiki/workflows/settings-and-cloud-sync.md +++ b/openwiki/workflows/settings-and-cloud-sync.md @@ -1,9 +1,11 @@ --- type: synchronization workflow -title: Settings, Vault, Metrics, History, Device, and Cloud Sync -description: Per-category CloudKit replication, durable journals, compatibility fences, fetch-before-send authorization, account and environment transitions, and recovery for Termind configuration and activity data. +title: Settings and Cloud Sync +description: Settings mutation and persistence, device-local desktop language selection, and app-wide ownership of CloudKit synchronization, journals, recovery, and secret boundaries. tags: [cloudkit, synchronization, settings, vault, metrics, history, devices, secrets, swiftdata] sources: + - id: openwiki-source-a51ca858a040a90d4ab9c1c4 + resource: repo://Frameworks/TermindApplication/AppLanguage.swift - id: openwiki-source-162732a78d991992cfb9458d resource: repo://Frameworks/TermindApplication/FeatureModels/DiagnosisController.swift - id: openwiki-source-4143deeb06ebc3198de62e2b @@ -116,28 +118,73 @@ sources: resource: repo://TFM/Features/Workspaces/WindowModel.swift - id: openwiki-source-44cb7a6172854edbfc9b5ccb resource: repo://TFMTests/SettingsNavigationTests.swift -generated: { by: "pi", at: "2026-10-08T13:28:49.393Z" } +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:28:49.393Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- -# Settings, Vault, Metrics, History, Device, and Cloud Sync +# Settings and Cloud Sync -Termind replicates a SwiftData-backed journal into the private `iCloud.com.akinokaede.termind` CloudKit container; this is **not** SwiftData CloudKit mirroring (`TermindPersistence.makeContainer` uses `cloudKitDatabase: .none`). Five writable owners—Vault, Settings, Metrics, Connection History, and Device—each own an engine, subscription, binding, queue, and lifecycle. History and Device retain the **same deployed zone**, `TermindConnectionHistoryV1`, but use separate subscriptions, kind-filtered journals and state rows, and separate safety scopes (Device's scope has consumer `device`; History keeps the original empty consumer). Neither should consume, reset or acknowledge the other's records. `HistoryRecordSyncCoordinator` shares the record-handling implementation, **not** their engine or account state. The former legacy importer is retired: Vault ignores obsolete History/Device copies in its own zone; it neither imports nor currently schedules physical cleanup of those copies. [CloudSyncJournal](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncJournal.swift), [HistoryRecordSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/HistoryRecordSyncCoordinator.swift), [VaultSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/VaultSyncCoordinator.swift). +Termind replicates a SwiftData-backed journal into the private `iCloud.com.akinokaede.termind` CloudKit container; this is **not** SwiftData CloudKit mirroring (`TermindPersistence.makeContainer` uses `cloudKitDatabase: .none`). Five writable owners—Vault, Settings, Metrics, Connection History, and Device—each own an engine, subscription, binding, queue, and lifecycle. History and Device retain the **same deployed zone**, `TermindConnectionHistoryV1`, but use separate subscriptions, kind-filtered journals and state rows, and separate safety scopes (Device's scope has consumer `device`; History keeps the original empty consumer). Neither should consume, reset or acknowledge the other's records. `HistoryRecordSyncCoordinator` shares the record-handling implementation, **not** their engine or account state. The former legacy importer is retired: Vault ignores obsolete History/Device copies in its own zone; it neither imports nor currently schedules physical cleanup of those copies. [CloudSyncJournal](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncJournal.swift), [HistoryRecordSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/HistoryRecordSyncCoordinator.swift), [VaultSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/VaultSyncCoordinator.swift). For embedded node enrollment, stable peer IDs, group preferences and device-local profile retirement, see [Tailscale groups, embedded nodes, and SSH](../integrations/tailscale.md). ## Settings routes and ownership -On macOS, `WindowModel` owns a per-window `settingsRoute` separate from app-wide preference models. `SettingsPageView` dispatches index → category → detail: App (including workspace preferences and diagnosis consent), Notifications, Terminal, Connections (SSH/SFTP), AI (providers, models, MCP servers, skills), and Sync. The index opens Pro as a paywall rather than a Settings category; a debug-enabled Logging route hangs directly off the index, and Developer is compiled only in DEBUG builds. `SettingsRoute.parent` drives toolbar back navigation: terminal configuration → scheme themes → Terminal, provider models → provider → AI, and other detail pages → their category; back from index closes Settings and restores the previous destination, falling back to Vault if its workspace vanished. [SettingsPageView](repo://TFM/Features/Settings/SettingsPageView.swift#L10-L207), [WindowModel](repo://TFM/Features/Workspaces/WindowModel.swift#L596-L632), [SettingsNavigationTests](repo://TFMTests/SettingsNavigationTests.swift#L13-L39), [SettingsNavigationTests](repo://TFMTests/SettingsNavigationTests.swift#L76-L155). +On macOS, `WindowModel` owns a per-window `settingsRoute` separate from app-wide preference models. `SettingsPageView` dispatches index → category → detail: App (including workspace preferences and diagnosis consent), Notifications, Terminal, Connections (SSH/SFTP), AI (providers, models, MCP servers, skills), and Sync. The index opens Pro as a paywall rather than a Settings category; a debug-enabled Logging route hangs directly off the index, and Developer is compiled only in DEBUG builds. `SettingsRoute.parent` drives toolbar back navigation: terminal configuration → scheme themes → Terminal, provider models → provider → AI, and other detail pages → their category; back from index closes Settings and restores the previous destination, falling back to Vault if its workspace vanished. [SettingsPageView](../../TFM/Features/Settings/SettingsPageView.swift#L10-L207), [WindowModel](../../TFM/Features/Workspaces/WindowModel.swift#L596-L632), [SettingsNavigationTests](../../TFMTests/SettingsNavigationTests.swift#L13-L39), [SettingsNavigationTests](../../TFMTests/SettingsNavigationTests.swift#L76-L155). -Leaving a provider draft discards it only if unnamed and unconfigured; a new MCP draft with no valid name/namespace is deleted, whereas a skill with written content but no title is kept as a draft. Navigating between provider and its models does not prematurely clean it up. These route transitions act on shared app models rather than starting CloudKit directly. [SettingsPageView](repo://TFM/Features/Settings/SettingsPageView.swift#L107-L113), [SettingsPageView](repo://TFM/Features/Settings/SettingsPageView.swift#L213-L245), [SettingsPageView](repo://TFM/Features/Settings/SettingsPageView.swift#L343-L365). +Leaving a provider draft discards it only if unnamed and unconfigured; a new MCP draft with no valid name/namespace is deleted, whereas a skill with written content but no title is kept as a draft. Navigating between provider and its models does not prematurely clean it up. These route transitions act on shared app models rather than starting CloudKit directly. [SettingsPageView](../../TFM/Features/Settings/SettingsPageView.swift#L107-L113), [SettingsPageView](../../TFM/Features/Settings/SettingsPageView.swift#L213-L245), [SettingsPageView](../../TFM/Features/Settings/SettingsPageView.swift#L343-L365). -`SettingsKey` contains local `UserDefaults` keys for both UI/selection and mirrors of synced preferences; being a key in this enum does **not** imply it is a cloud record. `SettingsSyncSnapshot.recordPayloads` determines what Settings actually journals: AI providers, MCP servers, roles, skills, web search, terminal, SSH, SFTP, workspace and notification preferences. Retired terminal highlighting no longer participates in that snapshot or active sync. In contrast diagnostic consent (`diagnostics.crashReportingConsent`, retained to avoid resetting earlier choices), debug visibility/logging, review-invitation history and terminal font family/size/follow-theme are device-local. Consent is an optional per-installation choice, not a synced notification preference or a cloud toggle: absence requires a choice and never enables reporting; `DiagnosisController` persists an explicit choice before applying it to the reporter. Terminal theme and integration preferences *do* enter the Settings snapshot even though they are mirrored to defaults. [SettingsKey](repo://Frameworks/TermindApplication/SettingsKey.swift#L4-L78), [SettingsSyncStore](repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L7-L55), [DiagnosisController](repo://Frameworks/TermindApplication/FeatureModels/DiagnosisController.swift#L9-L32), [DiagnosisTests](repo://FrameworkTests/DiagnosisTests.swift#L9-L50). +`SettingsKey` contains local `UserDefaults` keys for both UI/selection and mirrors of synced preferences; being a key in this enum does **not** imply it is a cloud record. `SettingsSyncSnapshot.recordPayloads` determines what Settings actually journals: AI providers, MCP servers, roles, skills, web search, terminal, SSH, SFTP, workspace and notification preferences. Retired terminal highlighting no longer participates in that snapshot or active sync. In contrast diagnostic consent (`diagnostics.crashReportingConsent`, retained to avoid resetting earlier choices), debug visibility/logging, review-invitation history and terminal font family/size/follow-theme are device-local. Consent is an optional per-installation choice, not a synced notification preference or a cloud toggle: absence requires a choice and never enables reporting; `DiagnosisController` persists an explicit choice before applying it to the reporter. Terminal theme and integration preferences *do* enter the Settings snapshot even though they are mirrored to defaults. [SettingsKey](../../Frameworks/TermindApplication/SettingsKey.swift#L4-L78), [SettingsSyncStore](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L7-L55), [DiagnosisController](../../Frameworks/TermindApplication/FeatureModels/DiagnosisController.swift#L9-L32), [DiagnosisTests](../../FrameworkTests/DiagnosisTests.swift#L9-L50). The desktop Show Status Bar toggle is a separate app-wide, device-local `DesktopSettingsKey.showStatusBar` default, enabled initially and shared by Settings and the View menu. It does not add a Settings sync record or per-window durable preference. Hiding it removes the root overlay and its reserved clearance; expanded/collapsed state remains separate. +## Local mutation, persistence errors, and composition + +Settings views bind to app-wide preference models, not CloudKit records. `TerminalSettingsModel`, `SSHSettingsModel` and `WorkspaceSettingsModel` build a candidate value and persist it before replacing the live preferences. A failed save returns `false`, retains the old live value and sets `storageError`; a successful or remote application clears that error. SSH candidates and remote snapshots are normalized, and changed SSH preferences notify the connection coordinator through `didChange`. Workspace automatic-renaming Settings displays its model's storage error in orange. These are **local persistence failures**, distinct from a sync owner's account, fetch or upload status. [TerminalSettingsModel](../../Frameworks/TermindApplication/FeatureModels/TerminalSettingsModel.swift#L11-L59), [SSHSettingsModel](../../Frameworks/TermindApplication/FeatureModels/SSHSettingsModel.swift#L12-L41), [WorkspaceSettingsModel](../../Frameworks/TermindApplication/FeatureModels/WorkspaceSettingsModel.swift#L10-L35), [SettingsPageView](../../TFM/Features/Settings/SettingsPageView.swift#L459-L469), [AppModel](../../TFM/App/AppModel.swift#L422-L448). + +Desktop `TermindApp` prepares `SettingsSyncPersistence` before constructing the live models. AI configuration and skills use synced adapters; terminal, notifications, SSH, SFTP and workspace models share the same settings persistence instance. Coordinators are injected through `AppDependencies` and omitted during XCTest, while settings persistence still exists. Store-startup failure is separate again: an in-use database presents the startup guard and exits, while other startup errors fail initialization. App composition does not put secret bytes into the settings projection; the Keychain and sync secret policy below remain separate boundaries. [TermindApp](../../TFM/App/TermindApp.swift#L81-L99), [TermindApp](../../TFM/App/TermindApp.swift#L151-L213). + +```mermaid +flowchart TD + Edit["Settings edit"] --> Candidate["Build candidate preferences"] + Candidate --> Save["Save projection and journal intent"] + Save --> Result{"Commit succeeds?"} + Result -->|No| Error["Rollback and retain live preferences with storageError"] + Result -->|Yes| Live["Update app-wide live preferences"] + Result -->|Yes| Journal["Notify sync owner of durable changes"] + Journal --> Gate["Cloud lifecycle fetch and upload gates"] + Remote["Accepted remote settings"] --> Merge["Transactional journal merge and projection rebuild"] + Merge --> Snapshot["Publish committed snapshot to AppModel"] + Snapshot --> Live +``` +Local edits persist before updating live models; remote snapshots reach them after commit without replaying a local edit. The cloud lifecycle gate is independent of local save success. [SettingsSyncPersistence](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L148-L211), [ModelContext.commit](../../Frameworks/TermindPersistence/ModelContext+Commit.swift#L9-L17), [AppModel](../../TFM/App/AppModel.swift#L440-L448). + +## Device-local desktop language selection + +The App language picker stores a **device-local** preference, not a Settings journal envelope. Its binding writes `SettingsKey.appLanguage` and calls `AppLanguage.store`. Explicit English, Japanese, Simplified Chinese and Traditional Chinese choices store canonical `en`, `ja`, `zh-Hans` and `zh-Hant` values plus a single-element `AppleLanguages` override. Follow System stores `system` and **removes** `AppleLanguages`, restoring normal Bundle negotiation rather than writing a guessed system language. Missing or unknown preferences resolve to Follow System. Legacy identifiers that Foundation resolves as Simplified Chinese migrate through `store` to canonical `zh-Hans`; unknown and noncanonical Traditional Chinese identifiers are not rewritten. [AppLanguage](../../Frameworks/TermindApplication/AppLanguage.swift#L41-L104), [SettingsPageView](../../TFM/Features/Settings/SettingsPageView.swift#L403-L416), [SettingsSyncSnapshot](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L7-L55), [AppLanguageTests](../../TFMTests/AppLanguageTests.swift#L11-L64). + +`TermindApp` captures `launchLanguage` during initialization (a DEBUG screenshot demo can override it) and injects its locale into main and external-authentication windows. Selecting a new language does not replace that launch-time choice or automatically relaunch the app. `AppSettings` separately captures `AppLanguage.stored()` in its own initial state, also named `launchLanguage`; when its selected value differs, it shows “The new language will be used after Termind relaunches.” The notice resolves through `AppLocalization` with **the selected language's locale**, not the existing window locale. This local view baseline is not the app's immutable launch capture. [TermindApp](../../TFM/App/TermindApp.swift#L40-L52), [TermindApp](../../TFM/App/TermindApp.swift#L238-L271), [SettingsPageView](../../TFM/Features/Settings/SettingsPageView.swift#L403-L456). + +```mermaid +flowchart TD + Launch["App initialization"] --> Read["AppLanguage.stored"] + Read --> Capture["Capture launchLanguage"] + Capture --> Windows["Inject launch locale into windows"] + Picker["Desktop language picker"] --> Store["AppLanguage.store"] + Store --> Defaults["Write canonical appLanguage"] + Store --> Choice{"Follow System?"} + Choice -->|Yes| Remove["Remove AppleLanguages"] + Choice -->|No| Override["Write single canonical AppleLanguages tag"] + Picker --> Notice["Show selected-language notice when view baseline differs"] + Defaults --> Next["Next app launch reads stored choice"] + Next --> Read +``` +Language storage affects the next launch; the current picker only stores the choice and presents a notice, with no automatic relaunch step. + +`AppLocalization` chooses the `en`, `ja`, `zh-Hans` or `zh-Hant` localization bundle from the supplied locale, falling back to the main bundle if unavailable. Chinese script selection keeps Traditional and Simplified catalogs separate. The Traditional Chinese picker label is `正體中文` only when the device region is `TW` and `DeviceCensorship.canDisplayTaiwanFlag` is true; otherwise it is `繁體中文`. This label policy does not change the `zh-Hant` stored tag. [AppLanguage](../../Frameworks/TermindApplication/AppLanguage.swift#L3-L64), [AppLanguageTests](../../TFMTests/AppLanguageTests.swift#L66-L105). For wider localization/product boundaries, see [Commerce, Localization, and Product Surfaces](../concepts/commerce-localization-and-product-surfaces.md). + ## Selection and app ownership | Owner | Zone and records | Selection | @@ -146,15 +193,15 @@ The desktop Show Status Bar toggle is a separate app-wide, device-local `Desktop | Connection History | `TermindConnectionHistoryV1` / `ConnectionHistoryItem` | Master and optional History | | Device | `TermindConnectionHistoryV1` / `DeviceItem` | Master, independently of History and Vault | | Metrics | Its own Metrics zone and journal | Master and optional Metrics | -| Settings | `TermindAIConfigurationV1` / `AIConfigurationItem`, `SSHPreferencesItem`, `SFTPPreferencesItem`, `WorkspacePreferencesItem`, `NotificationPreferencesItem`, `TerminalHighlightPreferencesItem` | Master and optional Settings | +| Settings | `TermindAIConfigurationV1` / `AIConfigurationItem`, `SSHPreferencesItem`, `SFTPPreferencesItem`, `WorkspacePreferencesItem`, `NotificationPreferencesItem` (legacy highlighting is ignored, not active) | Master and optional Settings | -The migration-only `vault.sync.enabled` participates in deriving the master selection if that switch has not been set. **History does not inherit it**: an unset History preference defaults to enabled, and `migrateSyncPreferences` does not copy the old Vault selection or environment into History. `CloudSyncSettingsContent` supplies the same UI to both platforms: a headerless iCloud toggle and aggregate status/actions, then “Optional Sync” toggles in History → Metrics → Settings order. There is no separate Vault or Device toggle. Enabling master sync is Pro-gated and attempts custom Host password migration to iCloud Keychain. [SettingsKey](repo://Frameworks/TermindApplication/SettingsKey.swift), [CloudSyncSettingsContent](repo://Frameworks/TermindPresentation/CloudSyncSettingsContent.swift). +The migration-only `vault.sync.enabled` participates in deriving the master selection if that switch has not been set. **History does not inherit it**: an unset History preference defaults to enabled, and `migrateSyncPreferences` does not copy the old Vault selection or environment into History. `CloudSyncSettingsContent` supplies the same UI to both platforms: a headerless iCloud toggle and aggregate status/actions, then “Optional Sync” toggles in History → Metrics → Settings order. There is no separate Vault or Device toggle. Enabling master sync is Pro-gated and attempts custom Host password migration to iCloud Keychain. [SettingsKey](../../Frameworks/TermindApplication/SettingsKey.swift), [CloudSyncSettingsContent](../../Frameworks/TermindPresentation/CloudSyncSettingsContent.swift). -Desktop `AppModel` starts **five** owners on setup when Pro permits, and starts or pauses all five when Pro access is reconciled. Mobile constructs all five coordinators only for a persistent store, wires History and Device repository change callbacks to their respective owners, and starts them from `reconcileSync()` only with an active scene and Pro access. Scene activation, preference changes, and scene removal call reconciliation; backgrounding explicitly pauses all five. Both composition roots relay accepted remote changes into app-wide models, rather than making engines window-owned. Settings snapshots update AI, skills, terminal, SSH, SFTP, workspace, notification and terminal keyword-highlight preferences; Metrics updates its live selections, and History/Device reload their models. For a remote Vault Host route change or removal, the app plans runtime teardown before replacing the persisted snapshot: it closes affected workspaces and cancels transfers or forwarding rules, then reconciles forwarding configuration; desktop also revokes host tools, while mobile updates live activities. Local persistence continues to record changes when an engine is paused. [AppModel](repo://TFM/App/AppModel.swift#L332-L420), [MobileAppModel](repo://TFI/App/MobileAppModel.swift#L182-L205), [MobileAppModel](repo://TFI/App/MobileAppModel.swift#L291-L319), [MobileAppModel](repo://TFI/App/MobileAppModel.swift#L454-L551). +Desktop `AppModel` starts **five** owners on setup when Pro permits, and starts or pauses all five when Pro access is reconciled. Mobile constructs all five coordinators only for a persistent store, wires History and Device repository change callbacks to their respective owners, and starts them from `reconcileSync()` only with an active scene and Pro access. Scene activation, preference changes, and scene removal call reconciliation; backgrounding explicitly pauses all five. Both composition roots relay accepted remote changes into app-wide models, rather than making engines window-owned. Settings snapshots update AI, skills, terminal, SSH, SFTP, workspace and notification preferences; Metrics updates its live selections, and History/Device reload their models. Remote Vault changes are planned before replacing the Host projection. Removed Hosts close workspaces and clear related activity; on desktop they also cancel transfers, authentication and host tools. Route changes invalidate desktop forwarding/agent connections through `willDeleteHost`; mobile disconnects retained workspaces and cancels transfers. Both stop affected forwarding rules before snapshot replacement and reconcile forwarding afterward; mobile also updates live activities. Local persistence continues to record changes when an engine is paused. [AppModel](../../TFM/App/AppModel.swift#L332-L420), [MobileAppModel](../../TFI/App/MobileAppModel.swift#L182-L205), [MobileAppModel](../../TFI/App/MobileAppModel.swift#L291-L319), [MobileAppModel](../../TFI/App/MobileAppModel.swift#L454-L551). ## Lifecycle and recovery -Each coordinator adapts its category journal to `CloudSyncLifecycleStore` and delegates CloudKit events to `CloudSyncLifecycle`. On start, it finishes pending asset cleanup, checks account availability and identity, prepares scoped safety and replica state, resets the journal on environment change, and discards a cursor if safety requires refetch, replica reconciliation is incomplete, or serialization is absent/invalid. It **commits the recovery fence before checking/creating the zone or constructing the automatically syncing engine**; a missing zone resets that owner's replica and journal. It reconciles eligible mutations with the engine queue, then fetches before sending. Explicit and automatic fetches may overlap: *all* current fetches must succeed before `isReadyToSend` and Settings secret-upload authorization become true. A fresh inventory compares observed IDs to previously present scoped records, reconciles missing IDs, and checkpoints the replica and decoder capability only after successful fetch/merge; journaled edits survive the fresh cursor. [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L314-L425), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L464-L513), [CloudSyncLifecycleSafety](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycleSafety.swift#L16-L61), [CloudSyncSafetyPersistenceTests](repo://FrameworkTests/CloudSyncSafetyPersistenceTests.swift#L141-L186). +Each coordinator adapts its category journal to `CloudSyncLifecycleStore` and delegates CloudKit events to `CloudSyncLifecycle`. On start, it finishes pending asset cleanup, checks account availability and identity, prepares scoped safety and replica state, resets the journal on environment change, and discards a cursor if safety requires refetch, replica reconciliation is incomplete, or serialization is absent/invalid. It **commits the recovery fence before checking/creating the zone or constructing the automatically syncing engine**; a missing zone resets that owner's replica and journal. It reconciles eligible mutations with the engine queue, then fetches before sending. Explicit and automatic fetches may overlap: *all* current fetches must succeed before `isReadyToSend` and Settings secret-upload authorization become true. A fresh inventory compares observed IDs to previously present scoped records, reconciles missing IDs, and checkpoints the replica and decoder capability only after successful fetch/merge; journaled edits survive the fresh cursor. [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L314-L425), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L464-L513), [CloudSyncLifecycleSafety](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycleSafety.swift#L16-L61), [CloudSyncSafetyPersistenceTests](../../FrameworkTests/CloudSyncSafetyPersistenceTests.swift#L141-L186). ```mermaid flowchart TD @@ -178,9 +225,9 @@ flowchart TD Batch -->|Eligible| Send["Persist delete handoffs then send and acknowledge"] Send -->|Later sync| Fetch ``` -The diagram shows one owner's fetch-before-send and scoped recovery gate; History and Device each run it over their shared zone with distinct consumer scopes. [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift), [HistoryRecordSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/HistoryRecordSyncCoordinator.swift). +The diagram shows one owner's fetch-before-send and scoped recovery gate; History and Device each run it over their shared zone with distinct consumer scopes. [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift), [HistoryRecordSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/HistoryRecordSyncCoordinator.swift). -A disabled master/category preference makes `start` pause the owner; disabled `retry` and account-merge confirmation do nothing. `pause` (also used by app Pro/background reconciliation), account sign-out or switch, and fatal delegate persistence failure retire an active engine: `retireEngine` changes the operation ID, cancels the operation task, clears readiness, Settings secret-upload authorization, scope and in-flight state, removes the engine synchronously, then schedules its `cancel()` in a **detached MainActor task**. This matters inside a CloudKit delegate callback: cancellation must not inherit its task-local callback context. Starting, retrying and scheduling local-change sends likewise use detached MainActor tasks. An old operation's late completion cannot change replacement status or authorize sends, and delegate events or batches for the old engine fail the identity check. A fatal delegate error rolls back, retires and reports failure; a normal cancellation (`CancellationError` or CloudKit `.operationCancelled`) is not logged or made a red failure. [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L218-L314), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L621-L689), [CloudSyncEventHandling](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncEventHandling.swift#L85-L130), [CloudSyncLifecycleTests](repo://FrameworkTests/CloudSyncLifecycleTests.swift#L11-L74), [CloudSyncLifecycleTests](repo://FrameworkTests/CloudSyncLifecycleTests.swift#L383-L435). +A disabled master/category preference makes `start` pause the owner; disabled `retry` and account-merge confirmation do nothing. `pause` (also used by app Pro/background reconciliation), account sign-out or switch, and fatal delegate persistence failure retire an active engine: `retireEngine` changes the operation ID, cancels the operation task, clears readiness, Settings secret-upload authorization, scope and in-flight state, removes the engine synchronously, then schedules its `cancel()` in a **detached MainActor task**. This matters inside a CloudKit delegate callback: cancellation must not inherit its task-local callback context. Starting, retrying and scheduling local-change sends likewise use detached MainActor tasks. An old operation's late completion cannot change replacement status or authorize sends, and delegate events or batches for the old engine fail the identity check. A fatal delegate error rolls back, retires and reports failure; a normal cancellation (`CancellationError` or CloudKit `.operationCancelled`) is not logged or made a red failure. [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L218-L314), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L621-L689), [CloudSyncEventHandling](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncEventHandling.swift#L85-L130), [CloudSyncLifecycleTests](../../FrameworkTests/CloudSyncLifecycleTests.swift#L11-L74), [CloudSyncLifecycleTests](../../FrameworkTests/CloudSyncLifecycleTests.swift#L383-L435). ```mermaid flowchart TD @@ -195,19 +242,19 @@ flowchart TD Confirm -->|"Confirm while enabled"| Prepare Failed -->|"Retry while enabled"| Prepare ``` -Retirement changes engine identity immediately; asynchronous cancellation and late completions cannot reopen the retired upload gate. A retryable fetch failure, unlike retirement, keeps the same engine for retry. [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L238-L251), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L390-L403), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L645-L689). +Retirement changes engine identity immediately; asynchronous cancellation and late completions cannot reopen the retired upload gate. A retryable fetch failure, unlike retirement, keeps the same engine for retry. [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L238-L251), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L390-L403), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L645-L689). -A retryable zone-fetch failure keeps the engine but does **not** reopen uploading or clear the recovery fence; the pending journal remains authoritative and a later successful fetch reauthorizes sending. Network/rate/zone-busy errors report syncing for retry, `notAuthenticated` requests sign-in, and terminal errors report failure. Recovery stays required until outstanding fetch/send work completes safely. The status can separately report uploadable and blocked counts, with an update hint only for unsupported format—not corruption. Logging emits `cloud.sync.failed` with zone, stage and NSError domain/code, not raw records, account IDs or localized descriptions; expected cancellation is not an error. [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L467-L515), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L598-L669), [CloudSyncEventHandling](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncEventHandling.swift). +A retryable zone-fetch failure keeps the engine but does **not** reopen uploading or clear the recovery fence; the pending journal remains authoritative and a later successful fetch reauthorizes sending. Network/rate/zone-busy errors report syncing for retry, `notAuthenticated` requests sign-in, and terminal errors report failure. Recovery stays required until outstanding fetch/send work completes safely. The status can separately report uploadable and blocked counts, with an update hint only for unsupported format—not corruption. Logging emits `cloud.sync.failed` with zone, stage and NSError domain/code, not raw records, account IDs or localized descriptions; expected cancellation is not an error. [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L467-L515), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L598-L669), [CloudSyncEventHandling](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncEventHandling.swift). ### Changing replicas and CloudKit deletions -An account mismatch records a pending user and requires explicit merge confirmation **before** any new-account engine exists. Confirmation resets the *owner's* journal before binding the new user, drops old-account tombstones and system fields, and requeues surviving local records. Old-scoped safety rows remain scoped to the old account; no legacy-quarantine preparation runs on confirmation. A changed `TermindCloudKitEnvironment` resets that owner's engine state and system fields and requeues envelopes without moving old-scoped restrictions. A missing zone or ordinary zone deletion recreates and requeues the corresponding replica; Device and History must not reset one another despite sharing a zone. By contrast CloudKit `.purged` or `.encryptedDataReset` retires the owner and purges that scope's local cloud cache and safety/replica state **without recreating the zone or uploading old local data**. Category asset-cache cleanup is tracked durably and finished on restart. [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L284-L386), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L695-L720), [HistorySyncStore](repo://Frameworks/TermindPersistence/HistorySyncStore.swift#L35-L64). +An account mismatch records a pending user and requires explicit merge confirmation **before** any new-account engine exists. Confirmation resets the *owner's* journal before binding the new user, drops old-account tombstones and system fields, and requeues surviving local records. Old-scoped safety rows remain scoped to the old account; no legacy-quarantine preparation runs on confirmation. A changed `TermindCloudKitEnvironment` resets that owner's engine state and system fields and requeues envelopes without moving old-scoped restrictions. A missing zone or ordinary zone deletion recreates and requeues the corresponding replica; Device and History must not reset one another despite sharing a zone. By contrast CloudKit `.purged` or `.encryptedDataReset` retires the owner and purges that scope's local cloud cache and safety/replica state **without recreating the zone or uploading old local data**. Category asset-cache cleanup is tracked durably and finished on restart. [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L284-L386), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L695-L720), [HistorySyncStore](../../Frameworks/TermindPersistence/HistorySyncStore.swift#L35-L64). ## Durable journals and wire contracts -Settings app models persist through `SettingsSyncPersistence`: configuration, skills and preference changes are compared as per-record snapshot payloads, and the SwiftData `ModelContext.commit` saves the local projection **and** `SettingsSyncJournal` upload intent together, rolling back on failure. `prepare()` migrates legacy role keys and bootstraps the current projection, including subsequently added terminal, SSH, SFTP, workspace and notification singletons, after discarding inactive highlighting journal entries. Its `didRecordChanges` callback notifies `SettingsSyncCoordinator`; a paused or missing engine does not prevent the journal write. Remote batches instead use `applyRemote` to merge envelopes, rebuild the complete projection (including out-of-order roles and providers), and commit journal, system-field and safety updates without echoing an accepted remote value as a local edit. Legacy provider Vertex fields and local skill packages are preserved when a remote envelope lacks them; resulting local differences are recorded as pending changes. [SettingsSyncPersistence](repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L76-L102), [SettingsSyncPersistence](repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L161-L234), [ModelContext.commit](repo://Frameworks/TermindPersistence/ModelContext+Commit.swift#L9-L17), [SettingsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L212-L275). +Settings app models persist through `SettingsSyncPersistence`: configuration, skills and preference changes are compared as per-record snapshot payloads, and the SwiftData `ModelContext.commit` saves the local projection **and** `SettingsSyncJournal` upload intent together, rolling back on failure. `prepare()` migrates legacy role keys and bootstraps the current projection, including subsequently added terminal, SSH, SFTP, workspace and notification singletons, after discarding inactive highlighting journal entries. Its `didRecordChanges` callback notifies `SettingsSyncCoordinator`; a paused or missing engine does not prevent the journal write. Remote batches instead use `applyRemote` to merge envelopes, rebuild the complete projection (including out-of-order roles and providers), and commit journal, system-field and safety updates without echoing an accepted remote value as a local edit. Legacy provider Vertex fields and local skill packages are preserved when a remote envelope lacks them; resulting local differences are recorded as pending changes. [SettingsSyncPersistence](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L76-L102), [SettingsSyncPersistence](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L161-L234), [ModelContext.commit](../../Frameworks/TermindPersistence/ModelContext+Commit.swift#L9-L17), [SettingsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L212-L275). -The Settings write and receive paths below share a journal but not an app-model edit entrypoint. The same lifecycle gate and scoped safety checks also apply to the other four owners; each supplies its own journal, record decoder and projection. [SettingsSyncPersistence](repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L161-L234), [SettingsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L181-L275), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L387-L417). +The Settings write and receive paths below share a journal but not an app-model edit entrypoint. The same lifecycle gate and scoped safety checks also apply to the other four owners; each supplies its own journal, record decoder and projection. [SettingsSyncPersistence](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L161-L234), [SettingsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L181-L275), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L387-L417). ```mermaid sequenceDiagram @@ -237,56 +284,57 @@ sequenceDiagram Engine-->>Coordinator: Sent changes or conflicts Coordinator->>Journal: Acknowledge matching version or merge conflict ``` -Settings' committed local mutation survives an unavailable engine; the fetch and quarantine fence prevents that mutation from being handed to CloudKit until safe. Received supported records update the live model only after the transactional merge. [SettingsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L73-L89), [SettingsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L188-L301), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L254-L268), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L464-L588). +Settings' committed local mutation survives an unavailable engine; the fetch and quarantine fence prevents that mutation from being handed to CloudKit until safe. Received supported records update the live model only after the transactional merge. [SettingsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L73-L89), [SettingsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L188-L301), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L254-L268), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L464-L588). -Bootstrap writes baseline envelopes from local projections; subsequent real edits get ordered `VaultVersion` values and durable pending intent, including logical tombstones. Merge chooses the newer version, breaking equal-version payload ties deterministically; a local winner is requeued. Fetched values do not echo merely because they were read. Journal-held system fields preserve CloudKit optimistic-concurrency metadata; successful acknowledgements clear pending only for the **sent version**, never a later edit or a quarantined ID. Outgoing eligibility derives from journal and scoped replica sidecars, not a restored CloudKit queue alone. [CloudSyncJournal](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncJournal.swift#L41-L96), [HistorySyncStore](repo://Frameworks/TermindPersistence/HistorySyncStore.swift#L222-L320), [CloudSyncRecordStateStore](repo://Frameworks/TermindPersistence/CloudSyncRecordStateStore.swift#L112-L148). +Bootstrap writes baseline envelopes from local projections; subsequent real edits get ordered `VaultVersion` values and durable pending intent, including logical tombstones. Merge chooses the newer version, breaking equal-version payload ties deterministically; a local winner is requeued. Fetched values do not echo merely because they were read. Journal-held system fields preserve CloudKit optimistic-concurrency metadata; successful acknowledgements clear pending only for the **sent version**, never a later edit or a quarantined ID. Outgoing eligibility derives from journal and scoped replica sidecars, not a restored CloudKit queue alone. [CloudSyncJournal](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncJournal.swift#L41-L96), [HistorySyncStore](../../Frameworks/TermindPersistence/HistorySyncStore.swift#L222-L320), [CloudSyncRecordStateStore](../../Frameworks/TermindPersistence/CloudSyncRecordStateStore.swift#L112-L148). -Vault, Metrics and Settings carry version/kind/entity/clock/deletion envelope metadata and validated payload hashes, storing small payloads inline and larger ones in `CKAsset`. Settings uses separate active SSH, SFTP, workspace and notification preference record types inside its existing zone so old clients filtering on type can skip them before decoding. Its record-type check also validates that the decoded preference kind agrees with the type. Vault normalizes device-local group/package expansion rather than syncing it; the V33 Identity shape has no `lastUsedAt`. Debug/logging, review history and terminal font family/size/follow-theme settings likewise stay device-local. [CloudSyncRecordCoding](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncRecordCoding.swift), [SettingsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L309-L355), [SettingsKey](repo://Frameworks/TermindApplication/SettingsKey.swift). +Vault, Metrics and Settings carry version/kind/entity/clock/deletion envelope metadata and validated payload hashes, storing small payloads inline and larger ones in `CKAsset`. Settings uses separate active SSH, SFTP, workspace and notification preference record types inside its existing zone so old clients filtering on type can skip them before decoding. Its record-type check also validates that the decoded preference kind agrees with the type. Vault normalizes device-local group/package expansion rather than syncing it; the V33 Identity shape has no `lastUsedAt`. Debug/logging, review history and terminal font family/size/follow-theme settings likewise stay device-local. [CloudSyncRecordCoding](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncRecordCoding.swift), [SettingsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L309-L355), [SettingsKey](../../Frameworks/TermindApplication/SettingsKey.swift). -Native History/Device writes use **typed CloudKit fields**, not JSON/asset payloads. `HistoryCloudRecordV1` validates `formatVersion`, `kind`, canonical `entityID` and record name, `modifiedAt`, nonnegative `counter`, `writerID`, and `isDeleted`; live events also require `hostID`, `connectionKind` (`ssh`, `telnet`, `sftp`) and `connectedAt`, with optional `endedAt` and `sourceDeviceID`; devices require `displayName`. Writes explicitly clear optional fields; tombstones need only metadata. IDs remain `v1/connectionHistory/` and `v1/device/`, independent of later format versions. An event can arrive before its Host and is persisted without a CloudKit reference. [HistoryCloudRecordV1](repo://Frameworks/TermindInfrastructure/Cloud/HistoryCloudRecordV1.swift), [HistorySyncStore](repo://Frameworks/TermindPersistence/HistorySyncStore.swift#L116-L149). +Native History/Device writes use **typed CloudKit fields**, not JSON/asset payloads. `HistoryCloudRecordV1` validates `formatVersion`, `kind`, canonical `entityID` and record name, `modifiedAt`, nonnegative `counter`, `writerID`, and `isDeleted`; live events also require `hostID`, `connectionKind` (`ssh`, `telnet`, `sftp`) and `connectedAt`, with optional `endedAt` and `sourceDeviceID`; devices require `displayName`. Writes explicitly clear optional fields; tombstones need only metadata. IDs remain `v1/connectionHistory/` and `v1/device/`, independent of later format versions. An event can arrive before its Host and is persisted without a CloudKit reference. [HistoryCloudRecordV1](../../Frameworks/TermindInfrastructure/Cloud/HistoryCloudRecordV1.swift), [HistorySyncStore](../../Frameworks/TermindPersistence/HistorySyncStore.swift#L116-L149). -History retains the 250 newest events and journals tombstones for overflow and clear operations while leaving device records intact. Event UUID deletion is terminal even against a delayed update with a later clock; Device, unlike events, can have a later explicit incarnation. Per-scope sidecars remember cloud presence, confirmed deletions and the exact **durable delete handoff before network submission**. Full-inventory reconciliation treats previously present but unseen records as physical deletions; only a newer pending explicit incarnation after a causal deletion handoff is preserved for recreation (not terminal History events). An ambiguous delete retains the handoff for retry; an acknowledgement applies only to the handed-off version, not a newer local edit. This avoids falsely acknowledging a newer device recreation after a crash, while reobserved records and blocked IDs still obey the outgoing fence. Ordinary physical CloudKit deletion is reconciled against the journal rather than treated as a new user edit: surviving local records can be recreated with cleared system fields, subject to upload restrictions. [HistorySyncStore](repo://Frameworks/TermindPersistence/HistorySyncStore.swift#L66-L113), [CloudSyncRecordStateStore](repo://Frameworks/TermindPersistence/CloudSyncRecordStateStore.swift#L123-L170), [CloudSyncRecordStateStore](repo://Frameworks/TermindPersistence/CloudSyncRecordStateStore.swift#L177-L256), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L480-L512), [CloudSyncSafetyPersistenceTests](repo://FrameworkTests/CloudSyncSafetyPersistenceTests.swift#L189-L248). +History retains the 250 newest events and journals tombstones for overflow and clear operations while leaving device records intact. Event UUID deletion is terminal even against a delayed update with a later clock; Device, unlike events, can have a later explicit incarnation. Per-scope sidecars remember cloud presence, confirmed deletions and the exact **durable delete handoff before network submission**. Full-inventory reconciliation treats previously present but unseen records as physical deletions; only a newer pending explicit incarnation after a causal deletion handoff is preserved for recreation (not terminal History events). An ambiguous delete retains the handoff for retry; an acknowledgement applies only to the handed-off version, not a newer local edit. This avoids falsely acknowledging a newer device recreation after a crash, while reobserved records and blocked IDs still obey the outgoing fence. Ordinary physical CloudKit deletion is reconciled against the journal rather than treated as a new user edit: surviving local records can be recreated with cleared system fields, subject to upload restrictions. [HistorySyncStore](../../Frameworks/TermindPersistence/HistorySyncStore.swift#L66-L113), [CloudSyncRecordStateStore](../../Frameworks/TermindPersistence/CloudSyncRecordStateStore.swift#L123-L170), [CloudSyncRecordStateStore](../../Frameworks/TermindPersistence/CloudSyncRecordStateStore.swift#L177-L256), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L480-L512), [CloudSyncSafetyPersistenceTests](../../FrameworkTests/CloudSyncSafetyPersistenceTests.swift#L189-L248). -**Retired Vault History is not imported or physically cleaned by the current owner.** New native History/Device records are written only to `TermindConnectionHistoryV1`. The Vault coordinator filters obsolete History/Device IDs out of decoded records and journal eligibility. It neither imports those payloads nor applies their deletion to local activity, but **does not queue physical deletes of old-zone copies**; do not assume that disabling the importer removes already uploaded legacy records. Old importer state is removed in migrated stores; native History bootstrap starts from current local events/devices, not the old Vault journal's clocks or system fields. Fresh installs read previously uploaded native records in the shared zone. [VaultSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/VaultSyncCoordinator.swift#L105-L111), [VaultSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/VaultSyncCoordinator.swift#L171-L237), [SwiftDataVaultJournal](repo://Frameworks/TermindPersistence/SwiftDataVaultJournal.swift#L215-L239), [HistorySyncTests](repo://FrameworkTests/HistorySyncTests.swift#L144-L167), [PersistenceCompatibilityTests](repo://FrameworkTests/PersistenceCompatibilityTests.swift#L11-L101). +**Retired Vault History is not imported or physically cleaned by the current owner.** New native History/Device records are written only to `TermindConnectionHistoryV1`. The Vault coordinator filters obsolete History/Device IDs out of decoded records and journal eligibility. It neither imports those payloads nor applies their deletion to local activity, but **does not queue physical deletes of old-zone copies**; do not assume that disabling the importer removes already uploaded legacy records. Old importer state is removed in migrated stores; native History bootstrap starts from current local events/devices, not the old Vault journal's clocks or system fields. Fresh installs read previously uploaded native records in the shared zone. [VaultSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/VaultSyncCoordinator.swift#L105-L111), [VaultSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/VaultSyncCoordinator.swift#L171-L237), [SwiftDataVaultJournal](../../Frameworks/TermindPersistence/SwiftDataVaultJournal.swift#L215-L239), [HistorySyncTests](../../FrameworkTests/HistorySyncTests.swift#L144-L167), [PersistenceCompatibilityTests](../../FrameworkTests/PersistenceCompatibilityTests.swift#L11-L101). ## Compatibility and upload fence -`SyncRecordFormats` versions records per family and kind; `VaultVersion` is a modification clock, **not** a wire version. Before decoding a payload or choosing historical defaults, readers check supported kind/version and field types. Frozen `VaultWireV1` DTOs preserve shipped Host SSH keys/defaults. Vault V2 writes Host agent-forwarding settings, SSH Key hardware references and Snippet color through separate adapters, while retaining V1 readers. The V2 Host decoder requires `agentForwardingEnabled` rather than silently defaulting a missing V2 field; V1 Host decoding still supplies historical defaults. V2 SSH Key encoding preserves the frozen wire storage vocabulary: a domain `.external` hardware key is written as `thisDevice` and is restored as `.external` by its security-key algorithm. A software key marked `.external` fails encoding instead of being sent with misleading storage. Current HostGroup and SSHKey writers use V3 with V1/V2/V3 readers; Snippet reads V1/V2 and writes V2; other ordinary kinds and native History/Device remain V1; a test-only Host V3 exercises further independent evolution. Record names keep their `v1/` identity prefix despite format upgrades. New fields an older writer would silently drop need a breaking per-kind version and adapter. Local V28 Host → V30 SSH/Telnet migration is separate from cloud format: the V28 reopen fixture verifies local SSH settings, default-disabled Telnet, associated state and workspace defaults, **not** CloudKit replica compatibility. [SyncRecordFormats](repo://Frameworks/TermindDomain/Vault/SyncRecordFormats.swift#L45-L55), [VaultWireV1](repo://Frameworks/TermindPersistence/VaultWireV1.swift), [VaultWireV2](repo://Frameworks/TermindPersistence/VaultWireV2.swift#L4-L30), [VaultWireV2](repo://Frameworks/TermindPersistence/VaultWireV2.swift#L127-L205), [VaultSyncCodec](repo://Frameworks/TermindPersistence/VaultSyncPersistence.swift#L56-L87), [WebAuthnVaultMetadataTests](repo://FrameworkTests/WebAuthnVaultMetadataTests.swift#L96-L119), [PersistenceCompatibilityTests](repo://FrameworkTests/PersistenceCompatibilityTests.swift#L152-L237). +`SyncRecordFormats` versions records per family and kind; `VaultVersion` is a modification clock, **not** a wire version. Before decoding a payload or choosing historical defaults, readers check supported kind/version and field types. Frozen `VaultWireV1` DTOs preserve shipped Host SSH keys/defaults. Vault V2 writes Host agent-forwarding settings, SSH Key hardware references and Snippet color through separate adapters, while retaining V1 readers. The V2 Host decoder requires `agentForwardingEnabled` rather than silently defaulting a missing V2 field; V1 Host decoding still supplies historical defaults. V2 SSH Key encoding preserves the frozen wire storage vocabulary: a domain `.external` hardware key is written as `thisDevice` and is restored as `.external` by its security-key algorithm. A software key marked `.external` fails encoding instead of being sent with misleading storage. Current HostGroup and SSHKey writers use V3 with V1/V2/V3 readers; Snippet reads V1/V2 and writes V2; other ordinary kinds and native History/Device remain V1; a test-only Host V3 exercises further independent evolution. Record names keep their `v1/` identity prefix despite format upgrades. New fields an older writer would silently drop need a breaking per-kind version and adapter. Local V28 Host → V30 SSH/Telnet migration is separate from cloud format: the V28 reopen fixture verifies local SSH settings, default-disabled Telnet, associated state and workspace defaults, **not** CloudKit replica compatibility. [SyncRecordFormats](../../Frameworks/TermindDomain/Vault/SyncRecordFormats.swift#L45-L55), [VaultWireV1](../../Frameworks/TermindPersistence/VaultWireV1.swift), [VaultWireV2](../../Frameworks/TermindPersistence/VaultWireV2.swift#L4-L30), [VaultWireV2](../../Frameworks/TermindPersistence/VaultWireV2.swift#L127-L205), [VaultSyncCodec](../../Frameworks/TermindPersistence/VaultSyncPersistence.swift#L56-L87), [WebAuthnVaultMetadataTests](../../FrameworkTests/WebAuthnVaultMetadataTests.swift#L96-L119), [PersistenceCompatibilityTests](../../FrameworkTests/PersistenceCompatibilityTests.swift#L152-L237). -Unsupported versions/kinds, malformed payloads, invalid native fields and known-ID conflicts are quarantined with reason, received bytes (full secure `CKRecord` archive for native fields) and system fields in the container/environment/account/zone/**consumer** scope. Valid remote records, including `serverRecordChanged` replies, merge in a transaction with system-field updates and quarantine removal; failed persistence rolls back the unblock. Future unknown record types in an ordinary fetch may be ignored, but an unexpected type on a conflicting known ID cannot bypass validation. Scoped restrictions persist across pause, retry, relaunch and normal account/environment/zone resets, retain local pending intent, and do not block unrelated records. History and Device restrictions cannot leak into each other. [CloudSyncLifecycleSafety](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycleSafety.swift#L49-L90), [CloudSyncSafetyStore](repo://Frameworks/TermindPersistence/CloudSyncSafetyStore.swift), [CloudSyncSafetyTests](repo://FrameworkTests/CloudSyncSafetyTests.swift). +Unsupported versions/kinds, malformed payloads, invalid native fields and known-ID conflicts are quarantined with reason, received bytes (full secure `CKRecord` archive for native fields) and system fields in the container/environment/account/zone/**consumer** scope. Valid remote records, including `serverRecordChanged` replies, merge in a transaction with system-field updates and quarantine removal; failed persistence rolls back the unblock. Future unknown record types in an ordinary fetch may be ignored, but an unexpected type on a conflicting known ID cannot bypass validation. Scoped restrictions persist across pause, retry, relaunch and normal account/environment/zone resets, retain local pending intent, and do not block unrelated records. History and Device restrictions cannot leak into each other. [CloudSyncLifecycleSafety](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycleSafety.swift#L49-L90), [CloudSyncSafetyStore](../../Frameworks/TermindPersistence/CloudSyncSafetyStore.swift), [CloudSyncSafetyTests](../../FrameworkTests/CloudSyncSafetyTests.swift). -Three checks protect outgoing changes: the replica sidecar omits blocked IDs when it enumerates journal mutations; reconciliation removes obsolete/blocked restored engine entries; and the synchronous batch checks current engine, fetch readiness, mutation operation and restriction again in each record provider. Each batch admits **at most 250 materialized changes in total (saves plus deletes)**, not 250 saves plus deletes; invalid/blocked entries do not consume that limit. A deletion cannot leave before its causal handoff is saved, and sent-record or delete acknowledgement cannot clear a newer pending incarnation. An unreadable safety/journal state fails closed rather than being read as an empty set. [CloudSyncRecordStateStore](repo://Frameworks/TermindPersistence/CloudSyncRecordStateStore.swift#L122-L148), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L523-L600), [CloudSyncLifecycleTests](repo://FrameworkTests/CloudSyncLifecycleTests.swift#L11-L30). +Three checks protect outgoing changes: the replica sidecar omits blocked IDs when it enumerates journal mutations; reconciliation removes obsolete/blocked restored engine entries; and the synchronous batch checks current engine, fetch readiness, mutation operation and restriction again in each record provider. Each batch admits **at most 250 materialized changes in total (saves plus deletes)**, not 250 saves plus deletes; invalid/blocked entries do not consume that limit. A deletion cannot leave before its causal handoff is saved, and sent-record or delete acknowledgement cannot clear a newer pending incarnation. An unreadable safety/journal state fails closed rather than being read as an empty set. [CloudSyncRecordStateStore](../../Frameworks/TermindPersistence/CloudSyncRecordStateStore.swift#L122-L148), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L523-L600), [CloudSyncLifecycleTests](../../FrameworkTests/CloudSyncLifecycleTests.swift#L11-L30). -A changed decoder capability ID in **Vault, Settings and each native History/Device consumer** forces a scoped refetch and checkpoints capability only after a successful fetch/merge; Settings computes its ID from participating record kinds at V1, while Metrics passes its versioned decoder capability ID. No current fence can retrofit a previously shipped unsafe binary; adding silently ignored fields in an existing version without raising the breaking version remains unsafe. [CloudSyncLifecycleSafety](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycleSafety.swift#L16-L65), [VaultSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/VaultSyncCoordinator.swift#L78-L95), [HistoryRecordSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/HistoryRecordSyncCoordinator.swift#L61-L98), [SettingsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L91-L105), [MetricsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/MetricsSyncCoordinator.swift#L81-L94). +A changed decoder capability ID in **Vault, Settings and each native History/Device consumer** forces a scoped refetch and checkpoints capability only after a successful fetch/merge; Settings computes its ID from participating record kinds at V1, while Metrics passes its versioned decoder capability ID. No current fence can retrofit a previously shipped unsafe binary; adding silently ignored fields in an existing version without raising the breaking version remains unsafe. [CloudSyncLifecycleSafety](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycleSafety.swift#L16-L65), [VaultSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/VaultSyncCoordinator.swift#L78-L95), [HistoryRecordSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/HistoryRecordSyncCoordinator.swift#L61-L98), [SettingsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L91-L105), [MetricsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/MetricsSyncCoordinator.swift#L81-L94). Snippet color is an explicit per-kind wire upgrade, not a consequence of local schema V51. V2 composes color with the frozen V1 fields; V1 encoding rejects colored snippets rather than losing color. A newer remote V1 Snippet clears color to `.none`, and tombstones retain V1/V2 compatibility. `SnippetColorTests` verifies both direct codec and journal/merge behavior. Treat successful historical decoding separately from whether an older writer can round-trip a colored value. ## Notification preferences across Settings and app models -`NotificationPreferences` is a synced Settings singleton, not a device-local authorization grant. It carries enabled state, independent assistant/terminal/SFTP delivery modes (`never`, `unfocused`, `always`), terminal system-notification versus bell delivery and a nonnegative terminal minimum duration. The defaults enable notifications, use `unfocused` for all three modes, system delivery and a five-second minimum. `usesSystemNotifications` excludes bell-only terminal delivery and disabled categories; the terminal duration threshold still applies to bell delivery. The decoder defaults a missing SFTP mode to `unfocused` for older payloads, but validates the nonnegative duration. [NotificationPreferences](repo://Frameworks/TermindDomain/Settings/NotificationPreferences.swift#L3-L57), [NotificationPreferencesTests](repo://FrameworkTests/NotificationPreferencesTests.swift#L55-L75). +`NotificationPreferences` is a synced Settings singleton, not a device-local authorization grant. It carries enabled state, independent assistant/terminal/SFTP delivery modes (`never`, `unfocused`, `always`), terminal system-notification versus bell delivery and a nonnegative terminal minimum duration. The defaults enable notifications, use `unfocused` for all three modes, system delivery and a five-second minimum. `usesSystemNotifications` excludes bell-only terminal delivery and disabled categories; the terminal duration threshold still applies to bell delivery. The decoder defaults a missing SFTP mode to `unfocused` for older payloads, but validates the nonnegative duration. [NotificationPreferences](../../Frameworks/TermindDomain/Settings/NotificationPreferences.swift#L3-L57), [NotificationPreferencesTests](../../FrameworkTests/NotificationPreferencesTests.swift#L55-L75). -`SettingsSyncPersistence.prepare()` bootstraps a missing notifications envelope even for stores already bootstrapped before this preference existed. `saveNotificationPreferences` validates then commits its SwiftData singleton and changed journal envelope together; `materialize()` restores default preferences on a winning remote tombstone. Settings uses `NotificationPreferencesItem` in the existing Settings zone rather than the older `AIConfigurationItem` record type; decoding verifies the envelope kind agrees with the record type. A supported remote winner updates persistence without invoking the local-change callback, then the coordinator notifies the app after commit; both platform composition roots apply the snapshot to their shared `NotificationSettingsModel`. The V40→V41 lightweight schema step adds `PersistentNotificationPreferences`, and the migration/reopen test verifies both the saved notification row and an older workspace setting survive. [SettingsSyncPersistence](repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L76-L102), [SettingsSyncPersistence](repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L172-L235), [SettingsSyncPersistence](repo://Frameworks/TermindPersistence/SettingsSyncStore.swift#L368-L382), [SettingsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L309-L355), [NotificationPreferencesTests](repo://FrameworkTests/NotificationPreferencesTests.swift#L95-L185), [MobileAppModel](repo://TFI/App/MobileAppModel.swift#L410-L418), [AppModel](repo://TFM/App/AppModel.swift#L393-L401). +`SettingsSyncPersistence.prepare()` bootstraps a missing notifications envelope even for stores already bootstrapped before this preference existed. `saveNotificationPreferences` validates then commits its SwiftData singleton and changed journal envelope together; `materialize()` restores default preferences on a winning remote tombstone. Settings uses `NotificationPreferencesItem` in the existing Settings zone rather than the older `AIConfigurationItem` record type; decoding verifies the envelope kind agrees with the record type. A supported remote winner updates persistence without invoking the local-change callback, then the coordinator notifies the app after commit; both platform composition roots apply the snapshot to their shared `NotificationSettingsModel`. The V40→V41 lightweight schema step adds `PersistentNotificationPreferences`, and the migration/reopen test verifies both the saved notification row and an older workspace setting survive. [SettingsSyncPersistence](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L76-L102), [SettingsSyncPersistence](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L172-L235), [SettingsSyncPersistence](../../Frameworks/TermindPersistence/SettingsSyncStore.swift#L368-L382), [SettingsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L309-L355), [NotificationPreferencesTests](../../FrameworkTests/NotificationPreferencesTests.swift#L95-L185), [MobileAppModel](../../TFI/App/MobileAppModel.swift#L410-L418), [AppModel](../../TFM/App/AppModel.swift#L393-L401). Terminal keyword highlighting is **retired**, not another active Settings singleton. V52 removes its historical local model; preparation discards inactive envelopes and excludes it from active bootstrap/capability/upload state. The legacy `TerminalHighlightPreferencesItem` type and `v1/terminalHighlighting/default` key are recognized only to ignore fetched/conflict/deletion records **before** reconciliation and payload validation. Even malformed retired records cannot replace other preferences or create quarantine through that path. Historical server types and old clients require separate rollout review; do not recreate the deleted live models or editor. See [Ghostty Terminal Integration](../concepts/ghostty-terminal-integration.md) and the retirement regression fixtures. -The notification model loads persisted preferences on construction; an invalid or failed edit does not change its live value, whereas a committed remote snapshot replaces it without writing or echoing. An active scene requests initial system authorization at most once per app-level model, only when loaded settings call for system delivery and no load error occurred; disabled notifications and bell-only terminal delivery do not prompt. The shared view modifier runs on active-scene transitions, but the model's one-shot guard deduplicates concurrent scenes and reactivation. [NotificationSettingsModel](repo://Frameworks/TermindApplication/FeatureModels/NotificationSettingsModel.swift#L4-L47), [InitialNotificationAuthorization](repo://Frameworks/TermindPresentation/InitialNotificationAuthorization.swift#L4-L20), [NotificationPreferencesTests](repo://FrameworkTests/NotificationPreferencesTests.swift#L12-L53), [NotificationPreferencesTests](repo://FrameworkTests/NotificationPreferencesTests.swift#L77-L93). +The notification model loads persisted preferences on construction; an invalid or failed edit does not change its live value, whereas a committed remote snapshot replaces it without writing or echoing. An active scene requests initial system authorization at most once per app-level model, only when loaded settings call for system delivery and no load error occurred; disabled notifications and bell-only terminal delivery do not prompt. The shared view modifier runs on active-scene transitions, but the model's one-shot guard deduplicates concurrent scenes and reactivation. [NotificationSettingsModel](../../Frameworks/TermindApplication/FeatureModels/NotificationSettingsModel.swift#L4-L47), [InitialNotificationAuthorization](../../Frameworks/TermindPresentation/InitialNotificationAuthorization.swift#L4-L20), [NotificationPreferencesTests](../../FrameworkTests/NotificationPreferencesTests.swift#L12-L53), [NotificationPreferencesTests](../../FrameworkTests/NotificationPreferencesTests.swift#L77-L93). ## Hardware key metadata at the Vault boundary -Security-key SSH entries are Vault **metadata**, not Keychain private keys: `HostLibraryModel.upsert` validates their credential ID, 65-byte X9.63 public key and lack of certificate, normalizes storage to `.external` and clears `credentialRef`. The V45 persistent key row stores credential ID, public key and verification policy; on load a security-key algorithm normalizes older storage values to `.external`. V2 carries this reference between Vault replicas, so connection preparation can resolve both destination and jump credentials even when Keychain access fails. Availability for these entries comes from the persisted reference, whereas software-key availability still checks Keychain. Failed metadata saves or deletes restore the in-memory library; upgrading a V44 software key store still preserves its keys. This is distinct from syncing software-key private material. [HostLibraryModel](repo://Frameworks/TermindApplication/FeatureModels/HostLibraryModel.swift#L1454-L1481), [HostLibraryModel](repo://Frameworks/TermindApplication/FeatureModels/HostLibraryModel.swift#L1594-L1650), [TermindSchemaV45KeyModels](repo://Frameworks/TermindPersistence/TermindSchemaV45KeyModels.swift#L14-L71), [WebAuthnVaultMetadataTests](repo://FrameworkTests/WebAuthnVaultMetadataTests.swift#L19-L76), [WebAuthnVaultMetadataTests](repo://FrameworkTests/WebAuthnVaultMetadataTests.swift#L121-L161). +Security-key SSH entries are Vault **metadata**, not Keychain private keys: `HostLibraryModel.upsert` validates their credential ID, 65-byte X9.63 public key and lack of certificate, normalizes storage to `.external` and clears `credentialRef`. The V45 persistent key row stores credential ID, public key and verification policy; on load a security-key algorithm normalizes older storage values to `.external`. V2 carries this reference between Vault replicas, so connection preparation can resolve both destination and jump credentials even when Keychain access fails. Availability for these entries comes from the persisted reference, whereas software-key availability still checks Keychain. Failed metadata saves or deletes restore the in-memory library; upgrading a V44 software key store still preserves its keys. This is distinct from syncing software-key private material. [HostLibraryModel](../../Frameworks/TermindApplication/FeatureModels/HostLibraryModel.swift#L1454-L1481), [HostLibraryModel](../../Frameworks/TermindApplication/FeatureModels/HostLibraryModel.swift#L1594-L1650), [TermindSchemaV45KeyModels](../../Frameworks/TermindPersistence/TermindSchemaV45KeyModels.swift#L14-L71), [WebAuthnVaultMetadataTests](../../FrameworkTests/WebAuthnVaultMetadataTests.swift#L19-L76), [WebAuthnVaultMetadataTests](../../FrameworkTests/WebAuthnVaultMetadataTests.swift#L121-L161). ## Settings secrets and operations -Settings synchronizes credential **references**, not secret bytes. After accepting provider or MCP-server references, `SettingsSyncCoordinator` marks them iCloud-backed. It migrates provider, Vertex and MCP references before a send/batch only when the current Settings fetch has authorized uploads and master plus Settings switches remain enabled. An active/failed fetch or engine retirement revokes authorization; `AISecretStore` verifies an iCloud copy before removing the device copy, and saves pending-deletion markers before deleting Keychain values so delayed delivery cannot resurrect a removed secret. [SettingsSyncCoordinator](repo://Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L120-L155), [SwiftDataAISecretStore](repo://Frameworks/TermindPersistence/SwiftDataAISecretStore.swift), [CloudSyncLifecycle](repo://Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L472-L505). +Settings synchronizes credential **references**, not secret bytes. After accepting provider or MCP-server references, `SettingsSyncCoordinator` marks them iCloud-backed. It migrates provider, Vertex and MCP references before a send/batch only when the current Settings fetch has authorized uploads and master plus Settings switches remain enabled. An active/failed fetch or engine retirement revokes authorization; `AISecretStore` verifies an iCloud copy before removing the device copy, and saves pending-deletion markers before deleting Keychain values so delayed delivery cannot resurrect a removed secret. [SettingsSyncCoordinator](../../Frameworks/TermindInfrastructure/Cloud/SettingsSyncCoordinator.swift#L120-L155), [SwiftDataAISecretStore](../../Frameworks/TermindPersistence/SwiftDataAISecretStore.swift), [CloudSyncLifecycle](../../Frameworks/TermindInfrastructure/Cloud/CloudSyncLifecycle.swift#L472-L505). Deploy and verify category record types, zones and subscriptions in both CloudKit environments, especially active `SFTPPreferencesItem` and `NotificationPreferencesItem`; treat legacy highlighting as a separate mixed-version retirement check rather than a new-feature deployment requirement, and run signed multi-device checks for offline edits, master/optional switches, Pro expiration, account change, zone deletion, and delayed Keychain delivery. See [CloudKit Schema Deployment and Signed-Device Release Readiness](../operations/cloudkit-release-readiness.md). When adding an owner, create an independent journal/state, subscription and safety consumer scope (and normally a zone), wire its own lifecycle/delegate and platform Pro/scene gating, and preserve every outbound fence. Do not reuse a sibling's system fields or cursor even if sharing a zone. For persistence and secrets see [Persistent Data, Migrations, and Secret Boundaries](../concepts/persistence-and-secrets.md); for app composition see [Platform Runtime, App Composition, and Lifetimes](../architecture/platform-runtime.md). ## Focused verification -- `SettingsNavigationTests` verifies the Settings parent-route chain, back-to-content return (including a missing workspace), paywall presentation without a Pro route, and navigation through Assistant; `DiagnosisTests` verifies per-installation consent defaults, durable choices and revocation ordering. [SettingsNavigationTests](repo://TFMTests/SettingsNavigationTests.swift#L13-L155), [DiagnosisTests](repo://FrameworkTests/DiagnosisTests.swift#L9-L75). +- `AppLanguageTests` pins canonical defaults and `AppleLanguages`, Follow System removal, legacy Simplified Chinese migration, unknown-value preservation, region-dependent labels, catalog separation and the selected-language notice. `SettingsSyncTests` checks settings round-trips, local edits while disabled, no-op version stability, out-of-order roles and invalid-batch rollback. These tests do not require an automatic relaunch. [AppLanguageTests](../../TFMTests/AppLanguageTests.swift#L11-L142), [SettingsSyncTests](../../TFMTests/SettingsSyncTests.swift#L435-L526). +- `SettingsNavigationTests` verifies the Settings parent-route chain, back-to-content return (including a missing workspace), paywall presentation without a Pro route, and navigation through Assistant; `DiagnosisTests` verifies per-installation consent defaults, durable choices and revocation ordering. [SettingsNavigationTests](../../TFMTests/SettingsNavigationTests.swift#L13-L155), [DiagnosisTests](../../FrameworkTests/DiagnosisTests.swift#L9-L75). - `CloudSyncLifecycleTests` exercises fetch gating, recovery, overlapping events, account/environment/zone changes, destructive purge, stale-engine rejection and the combined save/delete batch limit. Its delegate-context tests verify detached fetch/send and engine cancellation; late fetch/send and fatal delegate failures cannot revive a retired engine. Disabled selection prevents start, retry and account confirmation. - `CloudSyncSafetyTests` uses five real owner adapters with fake CloudKit transport to check winner/queue reconciliation, quarantine at each upload boundary, conflict handling, malformed native fields, rollback, and healthy-peer progress; `CloudSyncSafetyPersistenceTests` covers scoped on-disk recovery and migrations. - `HistorySyncTests` checks typed native fields, independent History/Device scopes and switches, terminal event tombstones and retention, no retired-source import, and error/cancellation diagnostics. `PersistenceCompatibilityTests` verifies V28 local reopen as well as dropping retired importer state on V35 reopen. -- `CloudSyncCoordinatorLifecycleTests`, `TFMTests/SettingsSyncTests.swift`, `TFMTests/VaultSyncTests.swift` and `SyncRecordEvolutionTests` cover per-owner gating, Settings secret authorization, journal/metadata exclusions, assets and wire evolution. `WebAuthnVaultMetadataTests` covers Keychain-free connection preparation and Vault round-trip, the frozen V2 `thisDevice` representation, invalid software external storage, metadata rollback, and V44 software-key reopen. `NotificationPreferencesTests` verifies settings record round-trip without echo, invalid remote duration, V40→V41 persistence reopening, edit failure and one-shot authorization across scenes. [HistorySyncTests](repo://FrameworkTests/HistorySyncTests.swift), [CloudSyncSafetyTests](repo://FrameworkTests/CloudSyncSafetyTests.swift), [PersistenceCompatibilityTests](repo://FrameworkTests/PersistenceCompatibilityTests.swift). +- `CloudSyncCoordinatorLifecycleTests`, `TFMTests/SettingsSyncTests.swift`, `TFMTests/VaultSyncTests.swift` and `SyncRecordEvolutionTests` cover per-owner gating, Settings secret authorization, journal/metadata exclusions, assets and wire evolution. `WebAuthnVaultMetadataTests` covers Keychain-free connection preparation and Vault round-trip, the frozen V2 `thisDevice` representation, invalid software external storage, metadata rollback, and V44 software-key reopen. `NotificationPreferencesTests` verifies settings record round-trip without echo, invalid remote duration, V40→V41 persistence reopening, edit failure and one-shot authorization across scenes. [HistorySyncTests](../../FrameworkTests/HistorySyncTests.swift), [CloudSyncSafetyTests](../../FrameworkTests/CloudSyncSafetyTests.swift), [PersistenceCompatibilityTests](../../FrameworkTests/PersistenceCompatibilityTests.swift). Current Vault writers are Host/Snippet V2, HostGroup V3 and SSHKey V3, with frozen historical readers. Metrics selection writes V2 and reads V1/V2; its upgraded capability checkpoint forces refetch of formerly quarantined V1 records without losing newer pending local changes. Tailscale group configuration syncs as HostGroup data; managed peer Hosts and local profile/sign-in/cache/cleanup state are excluded. Command/SFTP browse history is memory-only; the History coordinator remains solely the connection-event stream. diff --git a/openwiki/workflows/ssh-session-lifecycle.md b/openwiki/workflows/ssh-session-lifecycle.md index 38786725..069d295e 100644 --- a/openwiki/workflows/ssh-session-lifecycle.md +++ b/openwiki/workflows/ssh-session-lifecycle.md @@ -86,12 +86,20 @@ sources: resource: repo://TFM/Features/Workspaces/Pane.swift - id: openwiki-source-22b9fca540c3029c5ace451e resource: repo://TFM/Features/Workspaces/WindowModel.swift + - id: openwiki-source-22e92f96394f292a731fc6fb + resource: repo://TFM/Platform/MainWindowCoordinator.swift + - id: openwiki-source-dad03c68d6b805a4170ffc02 + resource: repo://TFM/Platform/WorkspaceCloseConfirmation.swift - id: openwiki-source-cfbc34def649c94f760c5695 resource: repo://TFMTests/AgentHostAuthenticationTests.swift + - id: openwiki-source-73c75477db0674192248aa00 + resource: repo://TFMTests/AppTerminationTests.swift - id: openwiki-source-c15cd5bef4646aaf64df407e resource: repo://TFMTests/ConnectionHistoryTests.swift - id: openwiki-source-85c3f954351f96433cb1fb4b resource: repo://TFMTests/ConnectionJourneyTests.swift + - id: openwiki-source-c40a8f8c27fd537db3a62965 + resource: repo://TFMTests/DesktopAuthenticationSubmissionTests.swift - id: openwiki-source-d94aebde8a56adf5ef02c658 resource: repo://TFMTests/HostShellIntegrationSessionTests.swift - id: openwiki-source-da61dbcdf7f720ccca8efaad @@ -100,10 +108,12 @@ sources: resource: repo://TFMTests/RemoteSessionConnectionControllerTests.swift - id: openwiki-source-fc9dd746ee8dc0791104d397 resource: repo://TFMTests/SSHPTYChannelLifecycleWireTests.swift -generated: { by: "pi", at: "2026-10-08T13:46:25.102Z" } + - id: openwiki-source-d798805795e92534f3b13250 + resource: repo://TFMTests/WorkspaceCloseConfirmationTests.swift +generated: { by: "openwiki/0.7.0", at: "2026-10-10T23:08:14.218Z" } verified: - - by: openwiki/0.7.1 - at: 2026-10-08T13:46:25.102Z + - by: openwiki/0.7.0 + at: 2026-10-10T23:08:14.218Z --- # SSH session lifecycle @@ -214,7 +224,13 @@ Credentials may be a password, software private key and optional passphrase, Sec Keyboard-interactive is available to routed hops and the target. It preserves host, port, username, connection ID, and round number in the challenge; answer count must exactly equal prompt count. Time spent waiting for the user extends the libssh2 callback budget, while answers themselves are transient and never a cache. -Desktop terminal and SFTP models now share `RemoteSessionAuthenticationContinuing`: it re-resolves the prompt host through route → library → temporary destination, validates a nonempty trimmed username, stages one-use proxy or SSH credentials, and invokes the model’s synchronous save policy before connecting. After staging, continuation clears the prompt and sets the journey to connecting before the save callback. A failed save does not connect; the model presents the error. A successful save policy then starts an explicit connection. Proxy submissions use the entered username, not an old proxy username. Save and error presentation remain model-owned, and these callbacks are never retained. +Desktop terminal and SFTP models share `RemoteSessionConnectionController.continueAuthentication`. It re-resolves the prompt host through route → library → temporary destination and rejects an empty trimmed username without changing the prompt. The continuation has distinct branches: + +- **Identity:** the selected identity supplies username and key selection when present; otherwise entered values are used. Stage the one-use SSH submission and model-specific username/route state, clear the prompt and resume the journey, then run the synchronous model-owned save policy if requested. Only a successful policy (or no requested save) invokes explicit connect. A failed credential save does **not** initiate another connection attempt: the model restores an error-bearing authentication prompt. +- **Proxy:** use the entered username rather than the selected identity's username. Save proxy authentication first if requested; only afterward stage the separate proxy submission, clear the prompt, resume and connect. A save failure leaves the original prompt and stages no proxy submission. +- **Protected-key passphrase:** merge through `passphraseSubmission` with the existing SSH submission, optionally saving the key passphrase. Success clears the prompt and connects; failure updates the existing prompt's failure message without connecting. + +These synchronous callbacks are never retained. Host-key trust is a separate live-handshake continuation, not an identity submission or a new connection attempt. `RemoteSessionConnectionController` centralizes attempt state: begin resets the journey and starts a pending route, finish accepts only the current token, and clear invalidates that token and route. Its live progress callback ignores replaced and cleared attempts. Transport recovery runs teardown before resetting the journey and invoking the model’s scheduled-state closure; the reconnect task does not own the transport or reconstruct secrets. @@ -297,12 +313,18 @@ The platform rules intentionally differ: | --- | --- | --- | | Window or app restoration | Native scene/frame restoration restores window presentation, not live Workspace transports. Dynamic Workspaces are window-owned and are not reconnected simply because a window is restored. | `mobile.workspaces.v1` stores device-local ordered descriptors (workspace ID, Host ID, kind and title) by native scene-session ID. Cold launch loads and prunes them but creates no live workspace, new history event, or connection; the first bound scene atomically consolidates legacy unassigned and previously owned descriptors into one window. Commands, SFTP paths, credentials and transport state are not restored. | | Reopen/select and scene ownership | Mounting a preserved desktop workspace does not repeat authentication; explicit retry or reopen may. Moving a workspace preserves its surfaces, transport, splits, and state. | Opening a dormant descriptor creates one live workspace in its owning scene; selecting it again shares the same initial attempt. Links from another window route to the owning native scene rather than create a duplicate. UIKit scene disconnect alone preserves ownership while its native session exists. Only after reconciling against `UIApplication.shared.openSessions` can a missing session's live workspaces and descriptors move to an active window without reconnecting; persistence must succeed before the handoff. An explicit native discard closes that session's workspaces, including ones handed off earlier. | -| Background/foreground | Closing a window tears down its workspace sessions, not app-wide Agent connections, transfers, or services. Quit checks pending external edits, then initiates shared shutdown (with a bounded termination reply) and closes every live window. | Only when all tracked scenes are backgrounded does a background task begin: sync, metrics and outbox are paused and external-edit monitoring is disabled. Its expiration requests workspace suspension, stops forwarding, pauses transfers and cancels Assistant runs; Assistant subsequently closes Agent background commands and pooled connections after conversation drains. An active scene ends the task, resumes only workspaces that recorded foreground-resume intent, rechecks edited files, reconciles sync and restarts metrics/outbox. Returning before expiration leaves a healthy connection intact. | +| Background/foreground | Native window close can be vetoed before any teardown; accepted closure retires only that window's workspace sessions, not app-wide Agent connections, transfers, or services. Quit checks workspaces across windows and pending external edits before shared shutdown and a bounded native termination reply. | Only when all tracked scenes are backgrounded does a background task begin: sync, metrics and outbox are paused and external-edit monitoring is disabled. Its expiration requests workspace suspension, stops forwarding, pauses transfers and cancels Assistant runs; Assistant subsequently closes Agent background commands and pooled connections after conversation drains. An active scene ends the task, resumes only workspaces that recorded foreground-resume intent, rechecks edited files, reconciles sync and restarts metrics/outbox. Returning before expiration leaves a healthy connection intact. | | Explicit close | The owning window closes its own sessions; closing a pane removes empty workspaces without transferring ownership. | Explicit close removes that workspace's live and recent entry and never retries it; deleting a saved Host also removes its live and unopened records across scenes. | The mobile store accepts legacy arrays and version-one snapshots but does not rewrite them on read: the first native scene binding commits migration, pruning and ownership together. If that save fails, binding is fenced for this launch and exposes a restoration error rather than claiming the same records in another window. An unreadable or invalid snapshot disables subsequent writes, preserving its original bytes even if users open or close workspaces. Scene recovery likewise saves the destination snapshot before transferring any live objects; if saving fails the source retains ownership. A native disconnect is not an explicit discard, and when no active replacement scene exists recovery waits instead of silently closing transports. -Desktop termination is idempotent: `AppModel.shutdownForTermination()` marks the app terminating, cancels pending Assistant preparation and stops conversations, disconnects window workspaces and stops metrics, forwarding, transfers and other app services; it waits for Assistant/MCP and SFTP drains before closing background commands and the Agent pool. `AppDelegate` rechecks pending edited-file uploads and can cancel quit **before** starting cleanup; if quit proceeds, it saves the main-window size and replies to macOS when shutdown completes or after the three-second fallback. `applicationWillTerminate` invokes the same shutdown path. Closing the last window does not necessarily quit: the metrics menu setting or running metrics can keep the app and its app-owned Agent services alive. No live workspace transport is reconstructed by native scene restoration. +### Native window close versus application Quit + +Window dismissal is vetoed by `WorkspaceWindowCloseDelegate.windowShouldClose`, attached through the native registration view—not by a SwiftUI dismissal dialog that also participates in Quit. The proxy first respects the existing native delegate's veto, forwards its other delegate methods, and guards reentrant confirmation. An empty or already-closed window needs no workspace warning; a populated window requires explicit destructive confirmation, with Cancel as the Return-key default. Cancelling preserves its model and transports and allows a later close request. Only actual native closure reaches `MainWindowCoordinator`'s `willClose` handling and `WindowModel.closeForWindow()`, which idempotently retires that window's workspace connections. + +Quit has application-wide scope. `AppDelegate.applicationShouldTerminate` returns `.terminateLater` and prevents duplicate admission while it checks `AppModel.hasOpenWorkspaces` across all registered, nonclosed windows. If any contains workspaces, it first asks the application-specific destructive confirmation; then it rechecks external edits and may ask a second pending-upload warning. Cancelling either check replies false and resets the pending-termination flag **before** cleanup begins. + +After approval, `AppModel.shutdownForTermination()` marks the app terminating and returns the same cleanup task on repeated calls. It cancels pending Assistant preparation and stops all stored conversations, disconnects window workspaces and stops metrics, forwarding, transfers and other app services; it waits for preparation/conversation, MCP and SFTP drains before closing Agent background commands, then the Agent pool and Tailscale. Assistant admission is fenced once termination starts. The delegate saves the main-window size and replies to macOS exactly once when cleanup completes or after the three-second fallback; the deadline bounds the reply, not a guarantee that every drain finished. `applicationWillTerminate` invokes the same shutdown path. Closing the last window does not necessarily quit: the metrics menu setting or running metrics can keep the app and its app-owned Agent services alive. No live workspace transport is reconstructed by native scene restoration. ```mermaid sequenceDiagram @@ -312,19 +334,25 @@ sequenceDiagram participant Windows as WindowModel participant Services as Assistant MCP SFTP and Agent OS->>Delegate: request quit - Delegate->>App: recheck external edits - alt user cancels pending upload warning - Delegate-->>OS: refuse termination - else quit accepted - Delegate->>App: shutdownForTermination - App->>Windows: close workspaces and transports - App->>Services: stop runs MCP and SFTP work - Services-->>App: drains complete - App->>Services: close background commands and pool - alt cleanup finishes first - Delegate-->>OS: reply yes - else three second deadline - Delegate-->>OS: reply yes once + Delegate-->>OS: terminateLater + Delegate->>App: check workspaces across registered windows + alt user cancels workspace warning + Delegate-->>OS: reply no and allow later request + else workspace confirmation accepted or none needed + Delegate->>App: recheck external edits + alt user cancels pending upload warning + Delegate-->>OS: reply no and allow later request + else quit accepted + Delegate->>App: shutdownForTermination + App->>Windows: close workspaces and transports + App->>Services: cancel preparation and stop runs MCP and SFTP + Services-->>App: drains complete + App->>Services: close background commands then pool and Tailscale + alt cleanup finishes first + Delegate-->>OS: reply yes once + else three second deadline + Delegate-->>OS: reply yes once + end end end ``` @@ -371,6 +399,8 @@ When changing connection code, preserve these boundaries: - Have the owner that opened a client retire it on every failed connect and on explicit close; do not transfer a half-open client to a workspace. Do not tie the Agent pool's life to a workspace, window, or Assistant turn. - When changing Agent authentication, preserve the separation between temporary per-opening secrets and reusable route-fingerprinted transports; cancel the final prompt waiter, close late opens, and never replay a command after its channel started. +`WorkspaceCloseConfirmationTests` distinguishes window and Quit scopes, verifies Cancel is the default, checks workspace detection across registered windows, and exercises native veto, retry, delegate forwarding and empty-window bypass. `AppTerminationTests` checks repeated shutdown drains resources once, blocks Assistant admission after shutdown, orders background-command and pool closure after drains, and races cleanup against the fallback without duplicate replies. `RemoteSessionAuthenticationContinuationTests` checks identity stage/save/connect ordering and proxy save-before-stage behavior, including no connect on save failure. `DesktopAuthenticationSubmissionTests` runs SSH/SFTP entrypoints against failed saves, blank usernames, selected versus custom credentials, protected-key passphrase continuation and temporary-host saving. + `SSHInteractionPolicyTests` checks silent Security Key blocking on both target and jumps, rejection of trust and prompted keyboard-interactive UI, current-identity additional-connection resolution and one-use secret invalidation. `WebAuthnVaultMetadataTests` proves hardware credentials for both target and jump resolve with a secret store that throws on Keychain access, and checks Vault metadata persistence and sync. `SSHConnectionWorkerLifecycleTests` releases the worker on and off its serial connection queue to catch teardown deadlocks. `ConnectionHistoryTests` checks attempt-time ordering, 250-entry retention, SwiftData persistence and clearing, source-device identity, end-on-pane-close, saved SSH/SFTP opening and deduplicated recents; it also verifies that clearing a host's histories leaves its Host and another host's events intact. `MobileWorkspaceSessionTests` covers independent window ownership, legacy migration and first-binding consolidation without network activity, non-reconnecting live handoff versus explicit discard, and native-session-aware reconciliation; `MobileAppTests` checks cold-launch descriptors without command replay, at-most-once selection and history, explicit retry after first failure, pruning, unreadable snapshot protection, normal SSH exit, and background expiration/foreground resume. `MobileCardTests` checks saved SSH/SFTP history starts on open and ends on close. The highest-value regression coverage is concentrated in `SSHConnectionPreparationTests` and `SSHHostKeyAuthorizationTests`: proxy attribution and one-shot proxy submission, missing proxy-password ownership, username-only and passphrase ordering, secret-read accounting, constraint-versus-Known-Hosts precedence, temporary versus persistent trust, and cancellation of pending trust. `ConnectionJourneyTests` validates endpoint progression, proxy attribution, pending-route behavior, retries, temporary host keys, multi-key trust, and the fact that editing a Host does not relocate Known Hosts. `MobileWorkspace`'s generation guards cover the platform-specific late-callback and foreground-resume boundary. `AgentHostAuthenticationTests` exercise proxy username retention with a missing password identity, password-plus-protected-key passphrase merging, shared exec/SFTP opens, prompt-waiter cancellation, host-key decisions, stale generation failures, post-success secret consumption, and non-replayed SFTP failures. For mobile expiration, inspect `MobileBackgroundController`'s lease behavior as well: expiration calls cancellation synchronously, retires a valid UIKit task exactly once, and treats a failed or synchronous `beginBackgroundTask` as expiration rather than leaving work assumed protected. ## Related pages