From 00b29b7b0989e8f17e4ca786683896734e8495ed Mon Sep 17 00:00:00 2001 From: 0xLeif Date: Sat, 18 Jul 2026 16:34:45 -0600 Subject: [PATCH] Error contract: exit-code taxonomy and JSON error envelope (Closes #31) - sysexits-aligned exit codes: 64 usage, 65 corrupt state, 69 unavailable, 70 internal, 73 write did not persist (66 reserved) - single CLIOutput.fail path: human stderr line + optional {error:{code,message,hint}} envelope (--json/--jsonl or APS_ERROR_JSON=1) - StateStore.ensureReadable: corrupt note.json/profile.json exits 65 instead of silently returning the initial value - disk readers split missing (persistenceFailed) from corrupt (decodingFailed) - tests + smoke assertions for the whole contract; specs/README in lockstep - SpecSync CHG-0016 accepted; evidence rebound for CHG-0003/0004/0007/0011/ 0012/0013/0014/0015 (audited reopens, actor 0xLeif) --- .specsync/change-sequence.json | 4 +- .../approvals.json | 126 +++++++ .../state.json | 2 +- .../verification-attempts.json | 15 + .../verification.json | 12 +- .../approvals.json | 325 ++++++++++++++++++ .../state.json | 2 +- .../verification-attempts.json | 24 ++ .../verification.json | 48 +-- .../approvals.json | 66 ++++ .../state.json | 2 +- .../verification-attempts.json | 15 + .../verification.json | 12 +- .../approvals.json | 239 +++++++++++++ .../state.json | 2 +- .../verification-attempts.json | 18 + .../verification.json | 48 +-- .../approvals.json | 250 ++++++++++++++ .../state.json | 2 +- .../verification-attempts.json | 19 + .../verification.json | 52 +-- .../approvals.json | 219 ++++++++++++ .../state.json | 2 +- .../verification-attempts.json | 18 + .../verification.json | 48 +-- .../approvals.json | 79 ++++- .../state.json | 2 +- .../verification-attempts.json | 15 + .../verification.json | 12 +- .../approvals.json | 202 ++++++++++- .../state.json | 2 +- .../verification-attempts.json | 18 + .../verification.json | 36 +- .../approvals.json | 33 ++ .../change.md | 25 ++ .../context.md | 22 ++ .../deltas/aps-cli.md | 88 +++++ .../deltas/state-store.md | 56 +++ .../design.md | 20 ++ .../docs.md | 10 + .../plan.md | 16 + .../requirements.md | 13 + .../state.json | 44 +++ .../tasks.md | 12 + .../testing.md | 18 + .../verification-attempts.json | 41 +++ .../verification.json | 264 ++++++++++++++ README.md | 23 ++ Scripts/smoke.sh | 26 ++ Sources/aps/Aps.swift | 152 ++++---- Sources/aps/CLIOutput.swift | 47 ++- Sources/aps/DemoKey.swift | 39 +++ Sources/aps/StateStore.swift | 66 +++- Tests/apsTests/APSTests.swift | 76 ++++ specs/aps-cli/aps-cli.spec.md | 41 ++- specs/aps-cli/requirements.md | 5 +- specs/state-store/requirements.md | 9 + specs/state-store/state-store.spec.md | 14 +- 58 files changed, 2862 insertions(+), 234 deletions(-) create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/approvals.json create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/change.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/context.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/deltas/aps-cli.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/deltas/state-store.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/design.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/docs.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/plan.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/requirements.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/state.json create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/tasks.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/testing.md create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/verification-attempts.json create mode 100644 .specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/verification.json diff --git a/.specsync/change-sequence.json b/.specsync/change-sequence.json index e7a6c1d..5b08b9b 100644 --- a/.specsync/change-sequence.json +++ b/.specsync/change-sequence.json @@ -1,6 +1,6 @@ { "schema_version": 1, - "sequence": 15, - "id": "CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15", + "sequence": 16, + "id": "CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31", "acknowledged_collisions": [] } diff --git a/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/approvals.json b/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/approvals.json index 27e3a34..89c9149 100644 --- a/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/approvals.json +++ b/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/approvals.json @@ -55,6 +55,13 @@ "timestamp": 1784409679, "digest": "5b7c34e74db23333cf83749a90c58bdc59cc248b10bacebcb25cafdb02dc33e1", "note": "Re-accept after docs CHG-0014." + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784413896, + "digest": "51c16bcf2eff595662297c595d0dff968d302c01566d9efee4181e80cfb7b3e0", + "note": null } ], "reopenings": [ @@ -652,6 +659,125 @@ }, "stale_acceptance_input_digest": "f414f0e4372c60a3587a85509fadade7aebc4bc3e600ae90f5c4193e7b2139c6", "current_acceptance_input_digest": "de0e45eb60ba4b5c26dc311e175865d272a52fe55e2efb7aac914b44f1bffe2f" + }, + { + "schema_version": 1, + "change_id": "CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal", + "actor": "0xLeif", + "reason": "README.md changed by the issue-31 error contract PR after acceptance; rebinding evidence to current delivery inputs", + "timestamp": 1784413874, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784409679, + "digest": "5b7c34e74db23333cf83749a90c58bdc59cc248b10bacebcb25cafdb02dc33e1", + "note": "Re-accept after docs CHG-0014." + }, + "prior_verification": { + "timestamp": 1784409670, + "commit": "e67ebd6084aa85beaf7f1ce219be5dde9cc30e0d", + "contract_digest": "641111b8f92bdfff2ba1f3bcda40f5299d9dc8a65449cd2a4a49333fac21100f", + "workspace_digest": "73b23f69f06702a7824f2aaa65bae3404b8f3763ad1c01e25bafca978ca51615", + "acceptance_input_digest": "de0e45eb60ba4b5c26dc311e175865d272a52fe55e2efb7aac914b44f1bffe2f", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/.gitignore", + "kind": "file", + "mode": 33188, + "payload_digest": "a3129d6f93250ade8dbacfc73bc7eeae0cc47c2f7032142ca47cb4f1ffb2483e", + "entry_digest": "9badbf40b83d05f58bafda0fc2cafccfe726e4da7e6e67ef513e822989e13f04", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "ddf1f4cbb0fd983c8356a34b9b4f4ef77ca22764ac2ec36b223431402c043328", + "entry_digest": "7fd95f31d540d3d3e7128fbd03cb3207a3da428c98a81515a76b7dfc9b34d582", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/config.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "54a83f6a4a252649bac390d2e936aa0815711fba86b6bfc8ea8fff2fffdfd29b", + "entry_digest": "e60f979233a7db75ce63f4ef42d6f1f7676ad1655a438b9baf78558aba60ec3e", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/registry.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "c423be1a5cf87318f85dc252fe0acd1c1486c5224ddc2d8e19bea1ac9c69c820", + "entry_digest": "7e1f068900ced849b8d1028ca40165fbdc5280ae7f53452f0e57e2710d895848", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/sdd.json", + "kind": "file", + "mode": 33188, + "payload_digest": "2d5fb3058b1ee4ce584c3cda9410c193c4893e272ed459205e58bfde06afc7b0", + "entry_digest": "8c6b11fc5787eb04e7ee651eb15fbf98fc3ea22db37e65423e3d75f99c09df19", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/version", + "kind": "file", + "mode": 33188, + "payload_digest": "f9893302cd3158f3b5aea394dcd2a91574869e9e6ff69e9235b10a3bf8c983fb", + "entry_digest": "f980ba79f6c953bed30303c6b87d8e868ad6a7f4114bf0e745c9099255e665b0", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "GOAL.md", + "kind": "file", + "mode": 33188, + "payload_digest": "20146bfa50165791ec6059f6245716170c43b23106e5123ec590a306a590a966", + "entry_digest": "68fbffe2a6cce1f402c5dcebbd733d2b1f3d5e3d259a0ee91dfc23bbc034fc56", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "README.md", + "kind": "file", + "mode": 33188, + "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", + "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "owners": [ + "@exact:delivery" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] + }, + "stale_acceptance_input_digest": "de0e45eb60ba4b5c26dc311e175865d272a52fe55e2efb7aac914b44f1bffe2f", + "current_acceptance_input_digest": "edc7307199d248cc31dfa1e76ccc47e8695b4b0bd06fd3a4ce9a9678cd5738f2" } ] } diff --git a/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/state.json b/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/state.json index b0e37d8..0e0beba 100644 --- a/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/state.json +++ b/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/state.json @@ -9,7 +9,7 @@ "canonical_applied": true, "base_commit": "c02a551afc126ca5394c62baa557ceaf30fd5bc5", "created_at": 1784398063, - "updated_at": 1784409679, + "updated_at": 1784413896, "affected_specs": [], "affected_paths": [ ".specsync/", diff --git a/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/verification-attempts.json b/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/verification-attempts.json index accc415..1a0dc2d 100644 --- a/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/verification-attempts.json +++ b/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/verification-attempts.json @@ -90,6 +90,21 @@ } ], "requirement_ids": [] + }, + { + "timestamp": 1784413886, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "641111b8f92bdfff2ba1f3bcda40f5299d9dc8a65449cd2a4a49333fac21100f", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] } ] } diff --git a/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/verification.json b/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/verification.json index 0a91777..6606f84 100644 --- a/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/verification.json +++ b/.specsync/changes/CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal/verification.json @@ -1,9 +1,9 @@ { - "timestamp": 1784409670, - "commit": "e67ebd6084aa85beaf7f1ce219be5dde9cc30e0d", + "timestamp": 1784413886, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", "contract_digest": "641111b8f92bdfff2ba1f3bcda40f5299d9dc8a65449cd2a4a49333fac21100f", - "workspace_digest": "73b23f69f06702a7824f2aaa65bae3404b8f3763ad1c01e25bafca978ca51615", - "acceptance_input_digest": "de0e45eb60ba4b5c26dc311e175865d272a52fe55e2efb7aac914b44f1bffe2f", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "acceptance_input_digest": "edc7307199d248cc31dfa1e76ccc47e8695b4b0bd06fd3a4ce9a9678cd5738f2", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -81,8 +81,8 @@ "path": "README.md", "kind": "file", "mode": 33188, - "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", - "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "payload_digest": "3b917fc037efd07213569e55a7ae617b8a0223afcf7b90f2ac4d4555ad7af681", + "entry_digest": "585a51a96139d484ce531b9de06cb6ca65b53f8c58d505d255441aac11d42211", "owners": [ "@exact:delivery" ] diff --git a/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/approvals.json b/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/approvals.json index 191027a..5ac23ec 100644 --- a/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/approvals.json +++ b/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/approvals.json @@ -125,6 +125,13 @@ "timestamp": 1784410584, "digest": "f458e966d8d0f123840a24fd68480ab0e32963fbb4b824ac4c2eb56c1c0c249d", "note": "Re-accept after CHG-0015." + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784413750, + "digest": "c0b70da582b3719ec99e1da4769e1de47186f886c2154043dd601c28c38e179d", + "note": null } ], "reopenings": [ @@ -3943,6 +3950,324 @@ }, "stale_acceptance_input_digest": "290ece89e050196f93b4eeaf1c322a3ffd9ce9b0881e8d6a3d586b32501cdf13", "current_acceptance_input_digest": "b99d095fa1301f69ade90cc5adf6a24a52fb1805dcf50da0688a6e6cc6638ebb" + }, + { + "schema_version": 1, + "change_id": "CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate", + "actor": "0xLeif", + "reason": "smoke.sh changed by the issue-31 error contract PR after acceptance; rebinding evidence to current delivery inputs", + "timestamp": 1784413731, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784410584, + "digest": "f458e966d8d0f123840a24fd68480ab0e32963fbb4b824ac4c2eb56c1c0c249d", + "note": "Re-accept after CHG-0015." + }, + "prior_verification": { + "timestamp": 1784410573, + "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "contract_digest": "73c000607b3050c54981b4c29532280dd08bf00b7a15c8ec1801e045fa5496c9", + "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", + "acceptance_input_digest": "20892dbe0c658b378986c08dc573b2b3fa948085d301ce4cc05a9f8b8033be15", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".github/workflows/ci.yml", + "kind": "file", + "mode": 33188, + "payload_digest": "a902f7c6785bb487278bfb39c95d593a45a8695f5245a9e5b9909d810410e6a8", + "entry_digest": "00a1986a1f0e06e1cbd4d3c3e933b240c5cb457a554e86515ba3db8241f7a263", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".github/workflows/linux-smoke.yml", + "kind": "file", + "mode": 33188, + "payload_digest": "7a7f76bbc068b14e9945a3add8c1f1d812362d810db6af6c4c9b60959d54f5c3", + "entry_digest": "db8fe6d1745786e008c9b0f6d2631cbf6a9e094763f305cfe07bfbc4444b0535", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".github/workflows/trust.yml", + "kind": "file", + "mode": 33188, + "payload_digest": "e165929994150d856d05ad048d8e1032e09df989c78ea3f1390d4e885b50af56", + "entry_digest": "e405b6225fdb502cfae9b22a9c14e21be8d1715bbaf8d56cdfd178bb8e173cb9", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/.gitignore", + "kind": "file", + "mode": 33188, + "payload_digest": "a3129d6f93250ade8dbacfc73bc7eeae0cc47c2f7032142ca47cb4f1ffb2483e", + "entry_digest": "9badbf40b83d05f58bafda0fc2cafccfe726e4da7e6e67ef513e822989e13f04", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "3b200adf8860f60a6ebbb7bd22278d65d36c9fb8d9dd7529cf7eba7635f9bb59", + "entry_digest": "62a4ea5935aca0821d1aeeb2b5113dda9561feaf0385be503f4cef23bbf99c7b", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/config.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "54a83f6a4a252649bac390d2e936aa0815711fba86b6bfc8ea8fff2fffdfd29b", + "entry_digest": "e60f979233a7db75ce63f4ef42d6f1f7676ad1655a438b9baf78558aba60ec3e", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/registry.toml", + "kind": "file", + "mode": 33188, + "payload_digest": "c423be1a5cf87318f85dc252fe0acd1c1486c5224ddc2d8e19bea1ac9c69c820", + "entry_digest": "7e1f068900ced849b8d1028ca40165fbdc5280ae7f53452f0e57e2710d895848", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/sdd.json", + "kind": "file", + "mode": 33188, + "payload_digest": "2d5fb3058b1ee4ce584c3cda9410c193c4893e272ed459205e58bfde06afc7b0", + "entry_digest": "8c6b11fc5787eb04e7ee651eb15fbf98fc3ea22db37e65423e3d75f99c09df19", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": ".specsync/version", + "kind": "file", + "mode": 33188, + "payload_digest": "f9893302cd3158f3b5aea394dcd2a91574869e9e6ff69e9235b10a3bf8c983fb", + "entry_digest": "f980ba79f6c953bed30303c6b87d8e868ad6a7f4114bf0e745c9099255e665b0", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Scripts/smoke.sh", + "kind": "file", + "mode": 33261, + "payload_digest": "e56bf09bae732fa3557d50b6c9b811bae7d2010dc74a01cdccd32bcd793c7a85", + "entry_digest": "be5fbb70152c18606ae7f26546919ab5cd61aa0b79de1fecb72d2cab092c2688", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps/Aps.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "d41387167cb27edb9e448160642c67e3e9819ddcc8038b3b25768e40a5f14add", + "entry_digest": "b08d0765337a24358f50b9a61ac5faa4ba39b9da193b4845f7edb71af98fd721", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/CLIOutput.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "281d1cb75cf70f49afb03a272cefdb7786a7a04f057f6aa51df33a9b672c999b", + "entry_digest": "5373f2d9892d77adc64d10ac041d291256396d280dce6792d1ae79d6f37f752c", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoKey.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "25c58ea18945c28d0c0b1dcebfa2229470d137c6434c58c8edc21f06948e59f7", + "entry_digest": "baff1088e1c04eeb481be267b76094b189baba0093526105e37a9a98676712cc", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoState.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "62cd24efb557b5cef77e7aae804f933f4b53caf47195968764f05cc5a0a0d9b8", + "entry_digest": "fb710f893d343d67c1fd7a5a5cc50c5c49caf0aa182e5af17ee7c97ff98f8122", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/Dependencies.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "14fced21000b935ed106a6a039bf574ed6cb9d8ba1598dc82001fbcd418a57a4", + "entry_digest": "0218077403628c671d6e778f6465e46e958a2c8f589d707f42a775f7da5cc799", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/StateStore.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", + "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "owners": [ + "state-store" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", + "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "owners": [ + "@exact:test" + ] + }, + { + "path": "specs/aps-cli/aps-cli.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", + "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7304a6233da2051242aeac63a51197aaf6f92d936692fe8655801eff842a2fd0", + "entry_digest": "e78ad0996c0135c37d41afa37c5fda13901bb63e14f00c3208896f7b104a72e8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", + "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ca6cafd22fb3c803eed3dc94cb980d8f535d51e0fd289b46bae82b7f255377de", + "entry_digest": "c349b8f073cefe7e387f8fabdd4dada3830be59b4b6d22a4ce79b2c29399c70e", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "472cbde38a2667652b54cda5c3ffa70ee30f91cd7de670804c0621459449bd84", + "entry_digest": "a016c672541231d3c30f08b033016d3f55b163084da74b5ec55e2ccc28c6b1be", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/state-store/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "12d848cab584c8a64f7da8bee796b581ec3b1b5efc72448299330dd493f39aa7", + "entry_digest": "76b734c278c05e3e74ff8f41dbb07aac3dcc7b1fbda64adcf32691e31365317c", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", + "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/state-store.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", + "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "0f02b782887467b358568bf22c2d68f39c40a00d56143bd6c64abdf8198cf433", + "entry_digest": "8f828f3625d4f24c3c71852bd28f76aac97cd31ace2a18d1a901a3d34642de8d", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "81ffb2b4e461759c297a2fcada8996026bfc8e18179c82365c987175829e6f05", + "entry_digest": "ca1f3e6e603b22125e5ad2ce203761b7f928029183cfbac6b5c292558197cf7b", + "owners": [ + "state-store" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-001", + "REQ-aps-cli-010", + "REQ-aps-cli-011", + "REQ-aps-cli-012", + "REQ-aps-cli-013", + "REQ-state-store-001", + "REQ-state-store-010", + "REQ-state-store-011" + ] + }, + "stale_acceptance_input_digest": "20892dbe0c658b378986c08dc573b2b3fa948085d301ce4cc05a9f8b8033be15", + "current_acceptance_input_digest": "f14bf1aef8e5dbc2a92477294a01f723d021cb441f30fd86d40c9393213f56b0" } ] } diff --git a/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/state.json b/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/state.json index ae29460..e3c9eb1 100644 --- a/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/state.json +++ b/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/state.json @@ -9,7 +9,7 @@ "canonical_applied": true, "base_commit": "6f18ccb3015b4fc59021e690fefbf5947ade5c73", "created_at": 1784399179, - "updated_at": 1784410584, + "updated_at": 1784413750, "affected_specs": [ "aps-cli", "state-store" diff --git a/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/verification-attempts.json b/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/verification-attempts.json index abb19fd..5fe3117 100644 --- a/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/verification-attempts.json +++ b/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/verification-attempts.json @@ -384,6 +384,30 @@ "REQ-state-store-010", "REQ-state-store-011" ] + }, + { + "timestamp": 1784413741, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "73c000607b3050c54981b4c29532280dd08bf00b7a15c8ec1801e045fa5496c9", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-001", + "REQ-aps-cli-010", + "REQ-aps-cli-011", + "REQ-aps-cli-012", + "REQ-aps-cli-013", + "REQ-state-store-001", + "REQ-state-store-010", + "REQ-state-store-011" + ] } ] } diff --git a/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/verification.json b/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/verification.json index 28579e8..21bc526 100644 --- a/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/verification.json +++ b/.specsync/changes/CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate/verification.json @@ -1,9 +1,9 @@ { - "timestamp": 1784410573, - "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "timestamp": 1784413741, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", "contract_digest": "73c000607b3050c54981b4c29532280dd08bf00b7a15c8ec1801e045fa5496c9", - "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", - "acceptance_input_digest": "20892dbe0c658b378986c08dc573b2b3fa948085d301ce4cc05a9f8b8033be15", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "acceptance_input_digest": "f14bf1aef8e5dbc2a92477294a01f723d021cb441f30fd86d40c9393213f56b0", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -101,8 +101,8 @@ "path": "Scripts/smoke.sh", "kind": "file", "mode": 33261, - "payload_digest": "e56bf09bae732fa3557d50b6c9b811bae7d2010dc74a01cdccd32bcd793c7a85", - "entry_digest": "be5fbb70152c18606ae7f26546919ab5cd61aa0b79de1fecb72d2cab092c2688", + "payload_digest": "ffeb56eb5676f0460aa4fac09df71bf6f8c6e010e833159ff1e76130c5b6185a", + "entry_digest": "76a215ac9afa32180fb2643cbcec67035a05ace4162cb7ba74c9df4c82350e6b", "owners": [ "@exact:delivery" ] @@ -111,8 +111,8 @@ "path": "Sources/aps/Aps.swift", "kind": "file", "mode": 33188, - "payload_digest": "d41387167cb27edb9e448160642c67e3e9819ddcc8038b3b25768e40a5f14add", - "entry_digest": "b08d0765337a24358f50b9a61ac5faa4ba39b9da193b4845f7edb71af98fd721", + "payload_digest": "b3dd9bb253ca4bca7e08b95dcb7da1b6a452130f32da0999ec9d85f1d5b7a491", + "entry_digest": "6326fa3f63954f3ad4614be6916ae3c2acb7c3f76b57ad1ac7ceb7ee215991c2", "owners": [ "aps-cli" ] @@ -121,8 +121,8 @@ "path": "Sources/aps/CLIOutput.swift", "kind": "file", "mode": 33188, - "payload_digest": "281d1cb75cf70f49afb03a272cefdb7786a7a04f057f6aa51df33a9b672c999b", - "entry_digest": "5373f2d9892d77adc64d10ac041d291256396d280dce6792d1ae79d6f37f752c", + "payload_digest": "28e5fb414d8272eb414a58c7c1e68e0e9d4bcaa0de64ba4c66b1888bb3dfc990", + "entry_digest": "4026319f59c9cbcb5b737efc1da34f4beaefb8502826a64affa9dc568864863b", "owners": [ "aps-cli" ] @@ -131,8 +131,8 @@ "path": "Sources/aps/DemoKey.swift", "kind": "file", "mode": 33188, - "payload_digest": "25c58ea18945c28d0c0b1dcebfa2229470d137c6434c58c8edc21f06948e59f7", - "entry_digest": "baff1088e1c04eeb481be267b76094b189baba0093526105e37a9a98676712cc", + "payload_digest": "a47ea6fbcd4538ead5097cdcaaa4aed28113289ad114bcbe9fafe7aafefe92cf", + "entry_digest": "c824f129c8ec4cfc23ded9895dd4110ae4f70a875ddc1ce81225e6d3f2e1b580", "owners": [ "aps-cli" ] @@ -161,8 +161,8 @@ "path": "Sources/aps/StateStore.swift", "kind": "file", "mode": 33188, - "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", - "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "payload_digest": "28d020ce5565b3c35cdfcd919a8191f7e2da45ff4c1ff35cef0348c44f2aa055", + "entry_digest": "a605fb78c95d63b1c1cdbf68b25896ab39dbe1cb78f2240162b17a7441dc4b5c", "owners": [ "state-store" ] @@ -171,8 +171,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", - "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "payload_digest": "10c11462b4b179680f6d8c468cef8c27dcd40e8792184743d26b8866059caf9e", + "entry_digest": "91593bbb8684eba55adb4ecb82d1f25d81a79443ea61729b21e9b35b365ab32d", "owners": [ "@exact:test" ] @@ -181,8 +181,8 @@ "path": "specs/aps-cli/aps-cli.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", - "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "payload_digest": "03f599b24a66c41336981a2058a5d26e7f75d8456a6f556f3f2096b4dedc9c4d", + "entry_digest": "8613eccbd83510588336bea997426747580c0ac512b8035613d5dcea7c203f53", "owners": [ "aps-cli" ] @@ -201,8 +201,8 @@ "path": "specs/aps-cli/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", - "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "payload_digest": "1ad52a139221d8bfabced8d08993e399140980a813852575769b37d4c075c901", + "entry_digest": "5668ffc635161c6fbf0f2d87569d3dd61d64bcc6d3c8f1437eb9dbdfbfe164b4", "owners": [ "aps-cli" ] @@ -241,8 +241,8 @@ "path": "specs/state-store/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", - "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "payload_digest": "2a29e8042e64f52435fd3e2762d91f6fd4d29862d5d7174ccfca26961bccf3e1", + "entry_digest": "7c39d633244caaecbf188f8567e635e88505a231758ae27a0e23b10eb1776960", "owners": [ "state-store" ] @@ -251,8 +251,8 @@ "path": "specs/state-store/state-store.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", - "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "payload_digest": "5e56d7201bcee99bc37e4efebd90c3aa1c126cffe4f1f07419270f5389922fd4", + "entry_digest": "1a4514d3e45b231b3d7f864f6f8366b75245f3dc891553bf5096c3d6be081739", "owners": [ "state-store" ] diff --git a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/approvals.json b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/approvals.json index 4d0dd40..3e45fe8 100644 --- a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/approvals.json +++ b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/approvals.json @@ -55,6 +55,13 @@ "timestamp": 1784410609, "digest": "3c196e938e9de2d80dc0ea00f7d16879651089f89f3cfee057c9fe6adc20737f", "note": "Re-accept after CHG-0015." + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784413622, + "digest": "81d914074d3d617ce101801e1bb498297a75944503dfe4436fb7a504fcb8797e", + "note": null } ], "reopenings": [ @@ -411,6 +418,65 @@ }, "stale_acceptance_input_digest": "05d1010c3397ca7918a6e9384904c43343de5678399b04f763e02e29fb76afe5", "current_acceptance_input_digest": "38f404546e865f843ca5b0dd40b03b8af49a173570c019786cd041dfe1ea79a8" + }, + { + "schema_version": 1, + "change_id": "CHG-0007-hermetic-userdefaults-in-tests", + "actor": "0xLeif", + "reason": "APSTests.swift changed by the issue-31 error contract PR after acceptance; rebinding evidence to current delivery inputs", + "timestamp": 1784413603, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784410609, + "digest": "3c196e938e9de2d80dc0ea00f7d16879651089f89f3cfee057c9fe6adc20737f", + "note": "Re-accept after CHG-0015." + }, + "prior_verification": { + "timestamp": 1784410599, + "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "contract_digest": "0c72a7f1bfaa780b21b7b2ac7904ab5352e8186a8293caf6f91354fddff40610", + "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", + "acceptance_input_digest": "38f404546e865f843ca5b0dd40b03b8af49a173570c019786cd041dfe1ea79a8", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "892737ad12d482e123f7e036d8af1cb0fd3aabb465f8c9ac561ed79af04a33bc", + "entry_digest": "c84485e5707378b3f73e17a2c56611d32c600a7ab1da4b428e69d3c0c5eea9a7", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", + "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "owners": [ + "@exact:test" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] + }, + "stale_acceptance_input_digest": "38f404546e865f843ca5b0dd40b03b8af49a173570c019786cd041dfe1ea79a8", + "current_acceptance_input_digest": "8ae17c44b9a5c5cdc79c53aceb0d6bfe55a64e531833c8ca3d13446ddc1a6370" } ] } diff --git a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/state.json b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/state.json index f7fedb6..5ddb880 100644 --- a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/state.json +++ b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/state.json @@ -9,7 +9,7 @@ "canonical_applied": true, "base_commit": "50eddbbb5c6bf829995523f0a9cd8f75f82f7cc6", "created_at": 1784403987, - "updated_at": 1784410609, + "updated_at": 1784413622, "affected_specs": [], "affected_paths": [ "Tests/apsTests/APSTests.swift", diff --git a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/verification-attempts.json b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/verification-attempts.json index d70b99d..0df3558 100644 --- a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/verification-attempts.json +++ b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/verification-attempts.json @@ -105,6 +105,21 @@ } ], "requirement_ids": [] + }, + { + "timestamp": 1784413613, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "0c72a7f1bfaa780b21b7b2ac7904ab5352e8186a8293caf6f91354fddff40610", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] } ] } diff --git a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/verification.json b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/verification.json index d68a26c..59223c4 100644 --- a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/verification.json +++ b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/verification.json @@ -1,9 +1,9 @@ { - "timestamp": 1784410599, - "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "timestamp": 1784413613, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", "contract_digest": "0c72a7f1bfaa780b21b7b2ac7904ab5352e8186a8293caf6f91354fddff40610", - "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", - "acceptance_input_digest": "38f404546e865f843ca5b0dd40b03b8af49a173570c019786cd041dfe1ea79a8", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "acceptance_input_digest": "8ae17c44b9a5c5cdc79c53aceb0d6bfe55a64e531833c8ca3d13446ddc1a6370", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -21,8 +21,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", - "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "payload_digest": "10c11462b4b179680f6d8c468cef8c27dcd40e8792184743d26b8866059caf9e", + "entry_digest": "91593bbb8684eba55adb4ecb82d1f25d81a79443ea61729b21e9b35b365ab32d", "owners": [ "@exact:test" ] diff --git a/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/approvals.json b/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/approvals.json index a1d9f00..38865f9 100644 --- a/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/approvals.json +++ b/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/approvals.json @@ -69,6 +69,13 @@ "timestamp": 1784410633, "digest": "ca1d07a8eed2b5e6b7016f68f748fba1d6e1ef032123d10bc4730f6b9dac2ac6", "note": "Re-accept after CHG-0015." + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784413465, + "digest": "e66819d42d37360ae813d1328c2bc0c72db7badee7c2e3bd2a9aca8880b221b5", + "note": null } ], "reopenings": [ @@ -1695,6 +1702,238 @@ }, "stale_acceptance_input_digest": "d321a3314dd3fac1597cae9f6146c8a54c839d482e02e0ef29d1e11ae89976c6", "current_acceptance_input_digest": "c0ec88ec1f72165d452d40bd241e1312ff403d42a6fb5a9ccb8aaceea487f33c" + }, + { + "schema_version": 1, + "change_id": "CHG-0011-dogfood-observeddependency-demostats-for-issue-18", + "actor": "0xLeif", + "reason": "README.md changed by the issue-31 error contract PR after acceptance; rebinding evidence to current delivery inputs", + "timestamp": 1784413437, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784410633, + "digest": "ca1d07a8eed2b5e6b7016f68f748fba1d6e1ef032123d10bc4730f6b9dac2ac6", + "note": "Re-accept after CHG-0015." + }, + "prior_verification": { + "timestamp": 1784410624, + "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "contract_digest": "1defd9f7ba999c7d4156cd0b80b236b4a173d5ba0985832e9f89b8b2ed5155ca", + "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", + "acceptance_input_digest": "66ac0ef1a20f2f67019d50297a433a2913b3b33ace41b5af84477f794bc31fa6", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "7379aee61297176f794273cd89e1456ff8cb7fa65acddd80bf6319aef651c0f5", + "entry_digest": "f10df0f2651b9a2bd8f53e246887e85705bf80dcd3879d88881867bff72e316a", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "README.md", + "kind": "file", + "mode": 33188, + "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", + "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Scripts/smoke.sh", + "kind": "file", + "mode": 33261, + "payload_digest": "e56bf09bae732fa3557d50b6c9b811bae7d2010dc74a01cdccd32bcd793c7a85", + "entry_digest": "be5fbb70152c18606ae7f26546919ab5cd61aa0b79de1fecb72d2cab092c2688", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps/Aps.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "d41387167cb27edb9e448160642c67e3e9819ddcc8038b3b25768e40a5f14add", + "entry_digest": "b08d0765337a24358f50b9a61ac5faa4ba39b9da193b4845f7edb71af98fd721", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/CLIOutput.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "281d1cb75cf70f49afb03a272cefdb7786a7a04f057f6aa51df33a9b672c999b", + "entry_digest": "5373f2d9892d77adc64d10ac041d291256396d280dce6792d1ae79d6f37f752c", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoState.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "62cd24efb557b5cef77e7aae804f933f4b53caf47195968764f05cc5a0a0d9b8", + "entry_digest": "fb710f893d343d67c1fd7a5a5cc50c5c49caf0aa182e5af17ee7c97ff98f8122", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/Dependencies.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "14fced21000b935ed106a6a039bf574ed6cb9d8ba1598dc82001fbcd418a57a4", + "entry_digest": "0218077403628c671d6e778f6465e46e958a2c8f589d707f42a775f7da5cc799", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/StateStore.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", + "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "owners": [ + "state-store" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", + "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "owners": [ + "@exact:test" + ] + }, + { + "path": "specs/aps-cli/aps-cli.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", + "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7304a6233da2051242aeac63a51197aaf6f92d936692fe8655801eff842a2fd0", + "entry_digest": "e78ad0996c0135c37d41afa37c5fda13901bb63e14f00c3208896f7b104a72e8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", + "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ca6cafd22fb3c803eed3dc94cb980d8f535d51e0fd289b46bae82b7f255377de", + "entry_digest": "c349b8f073cefe7e387f8fabdd4dada3830be59b4b6d22a4ce79b2c29399c70e", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "472cbde38a2667652b54cda5c3ffa70ee30f91cd7de670804c0621459449bd84", + "entry_digest": "a016c672541231d3c30f08b033016d3f55b163084da74b5ec55e2ccc28c6b1be", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/state-store/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "12d848cab584c8a64f7da8bee796b581ec3b1b5efc72448299330dd493f39aa7", + "entry_digest": "76b734c278c05e3e74ff8f41dbb07aac3dcc7b1fbda64adcf32691e31365317c", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", + "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/state-store.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", + "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "0f02b782887467b358568bf22c2d68f39c40a00d56143bd6c64abdf8198cf433", + "entry_digest": "8f828f3625d4f24c3c71852bd28f76aac97cd31ace2a18d1a901a3d34642de8d", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "81ffb2b4e461759c297a2fcada8996026bfc8e18179c82365c987175829e6f05", + "entry_digest": "ca1f3e6e603b22125e5ad2ce203761b7f928029183cfbac6b5c292558197cf7b", + "owners": [ + "state-store" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-014", + "REQ-state-store-012" + ] + }, + "stale_acceptance_input_digest": "66ac0ef1a20f2f67019d50297a433a2913b3b33ace41b5af84477f794bc31fa6", + "current_acceptance_input_digest": "37943cc22598eecf6441cee84da8d7cb4a005ea61f87d112a281d2de6c3c24b9" } ] } diff --git a/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/state.json b/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/state.json index cae3b20..3e85166 100644 --- a/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/state.json +++ b/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/state.json @@ -9,7 +9,7 @@ "canonical_applied": true, "base_commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", "created_at": 1784406105, - "updated_at": 1784410633, + "updated_at": 1784413465, "affected_specs": [ "aps-cli", "state-store" diff --git a/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/verification-attempts.json b/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/verification-attempts.json index e8c7afc..8e80eff 100644 --- a/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/verification-attempts.json +++ b/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/verification-attempts.json @@ -180,6 +180,24 @@ "REQ-aps-cli-014", "REQ-state-store-012" ] + }, + { + "timestamp": 1784413447, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "1defd9f7ba999c7d4156cd0b80b236b4a173d5ba0985832e9f89b8b2ed5155ca", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-014", + "REQ-state-store-012" + ] } ] } diff --git a/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/verification.json b/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/verification.json index 7809226..663544e 100644 --- a/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/verification.json +++ b/.specsync/changes/CHG-0011-dogfood-observeddependency-demostats-for-issue-18/verification.json @@ -1,9 +1,9 @@ { - "timestamp": 1784410624, - "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "timestamp": 1784413447, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", "contract_digest": "1defd9f7ba999c7d4156cd0b80b236b4a173d5ba0985832e9f89b8b2ed5155ca", - "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", - "acceptance_input_digest": "66ac0ef1a20f2f67019d50297a433a2913b3b33ace41b5af84477f794bc31fa6", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "acceptance_input_digest": "37943cc22598eecf6441cee84da8d7cb4a005ea61f87d112a281d2de6c3c24b9", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -21,8 +21,8 @@ "path": "README.md", "kind": "file", "mode": 33188, - "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", - "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "payload_digest": "3b917fc037efd07213569e55a7ae617b8a0223afcf7b90f2ac4d4555ad7af681", + "entry_digest": "585a51a96139d484ce531b9de06cb6ca65b53f8c58d505d255441aac11d42211", "owners": [ "@exact:delivery" ] @@ -31,8 +31,8 @@ "path": "Scripts/smoke.sh", "kind": "file", "mode": 33261, - "payload_digest": "e56bf09bae732fa3557d50b6c9b811bae7d2010dc74a01cdccd32bcd793c7a85", - "entry_digest": "be5fbb70152c18606ae7f26546919ab5cd61aa0b79de1fecb72d2cab092c2688", + "payload_digest": "ffeb56eb5676f0460aa4fac09df71bf6f8c6e010e833159ff1e76130c5b6185a", + "entry_digest": "76a215ac9afa32180fb2643cbcec67035a05ace4162cb7ba74c9df4c82350e6b", "owners": [ "@exact:delivery" ] @@ -41,8 +41,8 @@ "path": "Sources/aps/Aps.swift", "kind": "file", "mode": 33188, - "payload_digest": "d41387167cb27edb9e448160642c67e3e9819ddcc8038b3b25768e40a5f14add", - "entry_digest": "b08d0765337a24358f50b9a61ac5faa4ba39b9da193b4845f7edb71af98fd721", + "payload_digest": "b3dd9bb253ca4bca7e08b95dcb7da1b6a452130f32da0999ec9d85f1d5b7a491", + "entry_digest": "6326fa3f63954f3ad4614be6916ae3c2acb7c3f76b57ad1ac7ceb7ee215991c2", "owners": [ "aps-cli" ] @@ -51,8 +51,8 @@ "path": "Sources/aps/CLIOutput.swift", "kind": "file", "mode": 33188, - "payload_digest": "281d1cb75cf70f49afb03a272cefdb7786a7a04f057f6aa51df33a9b672c999b", - "entry_digest": "5373f2d9892d77adc64d10ac041d291256396d280dce6792d1ae79d6f37f752c", + "payload_digest": "28e5fb414d8272eb414a58c7c1e68e0e9d4bcaa0de64ba4c66b1888bb3dfc990", + "entry_digest": "4026319f59c9cbcb5b737efc1da34f4beaefb8502826a64affa9dc568864863b", "owners": [ "aps-cli" ] @@ -81,8 +81,8 @@ "path": "Sources/aps/StateStore.swift", "kind": "file", "mode": 33188, - "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", - "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "payload_digest": "28d020ce5565b3c35cdfcd919a8191f7e2da45ff4c1ff35cef0348c44f2aa055", + "entry_digest": "a605fb78c95d63b1c1cdbf68b25896ab39dbe1cb78f2240162b17a7441dc4b5c", "owners": [ "state-store" ] @@ -91,8 +91,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", - "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "payload_digest": "10c11462b4b179680f6d8c468cef8c27dcd40e8792184743d26b8866059caf9e", + "entry_digest": "91593bbb8684eba55adb4ecb82d1f25d81a79443ea61729b21e9b35b365ab32d", "owners": [ "@exact:test" ] @@ -101,8 +101,8 @@ "path": "specs/aps-cli/aps-cli.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", - "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "payload_digest": "03f599b24a66c41336981a2058a5d26e7f75d8456a6f556f3f2096b4dedc9c4d", + "entry_digest": "8613eccbd83510588336bea997426747580c0ac512b8035613d5dcea7c203f53", "owners": [ "aps-cli" ] @@ -121,8 +121,8 @@ "path": "specs/aps-cli/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", - "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "payload_digest": "1ad52a139221d8bfabced8d08993e399140980a813852575769b37d4c075c901", + "entry_digest": "5668ffc635161c6fbf0f2d87569d3dd61d64bcc6d3c8f1437eb9dbdfbfe164b4", "owners": [ "aps-cli" ] @@ -161,8 +161,8 @@ "path": "specs/state-store/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", - "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "payload_digest": "2a29e8042e64f52435fd3e2762d91f6fd4d29862d5d7174ccfca26961bccf3e1", + "entry_digest": "7c39d633244caaecbf188f8567e635e88505a231758ae27a0e23b10eb1776960", "owners": [ "state-store" ] @@ -171,8 +171,8 @@ "path": "specs/state-store/state-store.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", - "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "payload_digest": "5e56d7201bcee99bc37e4efebd90c3aa1c126cffe4f1f07419270f5389922fd4", + "entry_digest": "1a4514d3e45b231b3d7f864f6f8366b75245f3dc891553bf5096c3d6be081739", "owners": [ "state-store" ] diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/approvals.json b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/approvals.json index 22d88d3..587a42b 100644 --- a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/approvals.json +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/approvals.json @@ -69,6 +69,13 @@ "timestamp": 1784410657, "digest": "5caffc5239025757c9268ac47f33c3696d5efbc9b6af214961df5cccb6cb1013", "note": "Re-accept after CHG-0015." + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784413244, + "digest": "1ca46f4b971e917989c2346770bb783a6ef461847919cd9f4ad95e3d3e562f0c", + "note": null } ], "reopenings": [ @@ -1286,6 +1293,249 @@ }, "stale_acceptance_input_digest": "547a016db502ab7401868c4fd7110bcf54d3bd5236e0615b3530a01fcf929177", "current_acceptance_input_digest": "e3fe883352ee8ed1e6fdd989f5a1082ad5aafb3c8dd1cd36c9e33428d209be62" + }, + { + "schema_version": 1, + "change_id": "CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16", + "actor": "0xLeif", + "reason": "README.md changed by the issue-31 error contract PR after acceptance; rebinding evidence to current delivery inputs", + "timestamp": 1784413191, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784410657, + "digest": "5caffc5239025757c9268ac47f33c3696d5efbc9b6af214961df5cccb6cb1013", + "note": "Re-accept after CHG-0015." + }, + "prior_verification": { + "timestamp": 1784410647, + "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "contract_digest": "914c204b1d836d6b3de14e4908e2813862014a023e5d4244f1758d9486b3cd80", + "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", + "acceptance_input_digest": "6fcf82381bbaf4ec74fe362d145f1c7aa30e912da7ab64c0f0a644b48023b72b", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "634090de65a9412ba78ab19c40159c18c5de80df8165f8aef4bc259cf12e0402", + "entry_digest": "a29e2c49a47fbea8f3b4609e37567ba815bd772b5f718565894f9e3d585a547c", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "GOAL.md", + "kind": "file", + "mode": 33188, + "payload_digest": "20146bfa50165791ec6059f6245716170c43b23106e5123ec590a306a590a966", + "entry_digest": "68fbffe2a6cce1f402c5dcebbd733d2b1f3d5e3d259a0ee91dfc23bbc034fc56", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "README.md", + "kind": "file", + "mode": 33188, + "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", + "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Scripts/smoke.sh", + "kind": "file", + "mode": 33261, + "payload_digest": "e56bf09bae732fa3557d50b6c9b811bae7d2010dc74a01cdccd32bcd793c7a85", + "entry_digest": "be5fbb70152c18606ae7f26546919ab5cd61aa0b79de1fecb72d2cab092c2688", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps/Aps.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "d41387167cb27edb9e448160642c67e3e9819ddcc8038b3b25768e40a5f14add", + "entry_digest": "b08d0765337a24358f50b9a61ac5faa4ba39b9da193b4845f7edb71af98fd721", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/CLIOutput.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "281d1cb75cf70f49afb03a272cefdb7786a7a04f057f6aa51df33a9b672c999b", + "entry_digest": "5373f2d9892d77adc64d10ac041d291256396d280dce6792d1ae79d6f37f752c", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoKey.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "25c58ea18945c28d0c0b1dcebfa2229470d137c6434c58c8edc21f06948e59f7", + "entry_digest": "baff1088e1c04eeb481be267b76094b189baba0093526105e37a9a98676712cc", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoState.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "62cd24efb557b5cef77e7aae804f933f4b53caf47195968764f05cc5a0a0d9b8", + "entry_digest": "fb710f893d343d67c1fd7a5a5cc50c5c49caf0aa182e5af17ee7c97ff98f8122", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/StateStore.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", + "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "owners": [ + "state-store" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", + "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "owners": [ + "@exact:test" + ] + }, + { + "path": "specs/aps-cli/aps-cli.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", + "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7304a6233da2051242aeac63a51197aaf6f92d936692fe8655801eff842a2fd0", + "entry_digest": "e78ad0996c0135c37d41afa37c5fda13901bb63e14f00c3208896f7b104a72e8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", + "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ca6cafd22fb3c803eed3dc94cb980d8f535d51e0fd289b46bae82b7f255377de", + "entry_digest": "c349b8f073cefe7e387f8fabdd4dada3830be59b4b6d22a4ce79b2c29399c70e", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "472cbde38a2667652b54cda5c3ffa70ee30f91cd7de670804c0621459449bd84", + "entry_digest": "a016c672541231d3c30f08b033016d3f55b163084da74b5ec55e2ccc28c6b1be", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/state-store/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "12d848cab584c8a64f7da8bee796b581ec3b1b5efc72448299330dd493f39aa7", + "entry_digest": "76b734c278c05e3e74ff8f41dbb07aac3dcc7b1fbda64adcf32691e31365317c", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", + "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/state-store.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", + "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "0f02b782887467b358568bf22c2d68f39c40a00d56143bd6c64abdf8198cf433", + "entry_digest": "8f828f3625d4f24c3c71852bd28f76aac97cd31ace2a18d1a901a3d34642de8d", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "81ffb2b4e461759c297a2fcada8996026bfc8e18179c82365c987175829e6f05", + "entry_digest": "ca1f3e6e603b22125e5ad2ce203761b7f928029183cfbac6b5c292558197cf7b", + "owners": [ + "state-store" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-001", + "REQ-aps-cli-015", + "REQ-state-store-013" + ] + }, + "stale_acceptance_input_digest": "6fcf82381bbaf4ec74fe362d145f1c7aa30e912da7ab64c0f0a644b48023b72b", + "current_acceptance_input_digest": "5e484bd4242eb88a32f699b29774cacaa77b6a9c360cffa41a78cd9e4d0fd6ef" } ] } diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/state.json b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/state.json index 1c460ef..4ba930c 100644 --- a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/state.json +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/state.json @@ -9,7 +9,7 @@ "canonical_applied": true, "base_commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", "created_at": 1784406691, - "updated_at": 1784410657, + "updated_at": 1784413244, "affected_specs": [ "aps-cli", "state-store" diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification-attempts.json b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification-attempts.json index 558845e..d7049ac 100644 --- a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification-attempts.json +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification-attempts.json @@ -171,6 +171,25 @@ "REQ-aps-cli-015", "REQ-state-store-013" ] + }, + { + "timestamp": 1784413218, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "914c204b1d836d6b3de14e4908e2813862014a023e5d4244f1758d9486b3cd80", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-001", + "REQ-aps-cli-015", + "REQ-state-store-013" + ] } ] } diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification.json b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification.json index 5758e60..03d22ed 100644 --- a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification.json +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification.json @@ -1,9 +1,9 @@ { - "timestamp": 1784410647, - "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "timestamp": 1784413218, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", "contract_digest": "914c204b1d836d6b3de14e4908e2813862014a023e5d4244f1758d9486b3cd80", - "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", - "acceptance_input_digest": "6fcf82381bbaf4ec74fe362d145f1c7aa30e912da7ab64c0f0a644b48023b72b", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "acceptance_input_digest": "5e484bd4242eb88a32f699b29774cacaa77b6a9c360cffa41a78cd9e4d0fd6ef", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -31,8 +31,8 @@ "path": "README.md", "kind": "file", "mode": 33188, - "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", - "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "payload_digest": "3b917fc037efd07213569e55a7ae617b8a0223afcf7b90f2ac4d4555ad7af681", + "entry_digest": "585a51a96139d484ce531b9de06cb6ca65b53f8c58d505d255441aac11d42211", "owners": [ "@exact:delivery" ] @@ -41,8 +41,8 @@ "path": "Scripts/smoke.sh", "kind": "file", "mode": 33261, - "payload_digest": "e56bf09bae732fa3557d50b6c9b811bae7d2010dc74a01cdccd32bcd793c7a85", - "entry_digest": "be5fbb70152c18606ae7f26546919ab5cd61aa0b79de1fecb72d2cab092c2688", + "payload_digest": "ffeb56eb5676f0460aa4fac09df71bf6f8c6e010e833159ff1e76130c5b6185a", + "entry_digest": "76a215ac9afa32180fb2643cbcec67035a05ace4162cb7ba74c9df4c82350e6b", "owners": [ "@exact:delivery" ] @@ -51,8 +51,8 @@ "path": "Sources/aps/Aps.swift", "kind": "file", "mode": 33188, - "payload_digest": "d41387167cb27edb9e448160642c67e3e9819ddcc8038b3b25768e40a5f14add", - "entry_digest": "b08d0765337a24358f50b9a61ac5faa4ba39b9da193b4845f7edb71af98fd721", + "payload_digest": "b3dd9bb253ca4bca7e08b95dcb7da1b6a452130f32da0999ec9d85f1d5b7a491", + "entry_digest": "6326fa3f63954f3ad4614be6916ae3c2acb7c3f76b57ad1ac7ceb7ee215991c2", "owners": [ "aps-cli" ] @@ -61,8 +61,8 @@ "path": "Sources/aps/CLIOutput.swift", "kind": "file", "mode": 33188, - "payload_digest": "281d1cb75cf70f49afb03a272cefdb7786a7a04f057f6aa51df33a9b672c999b", - "entry_digest": "5373f2d9892d77adc64d10ac041d291256396d280dce6792d1ae79d6f37f752c", + "payload_digest": "28e5fb414d8272eb414a58c7c1e68e0e9d4bcaa0de64ba4c66b1888bb3dfc990", + "entry_digest": "4026319f59c9cbcb5b737efc1da34f4beaefb8502826a64affa9dc568864863b", "owners": [ "aps-cli" ] @@ -71,8 +71,8 @@ "path": "Sources/aps/DemoKey.swift", "kind": "file", "mode": 33188, - "payload_digest": "25c58ea18945c28d0c0b1dcebfa2229470d137c6434c58c8edc21f06948e59f7", - "entry_digest": "baff1088e1c04eeb481be267b76094b189baba0093526105e37a9a98676712cc", + "payload_digest": "a47ea6fbcd4538ead5097cdcaaa4aed28113289ad114bcbe9fafe7aafefe92cf", + "entry_digest": "c824f129c8ec4cfc23ded9895dd4110ae4f70a875ddc1ce81225e6d3f2e1b580", "owners": [ "aps-cli" ] @@ -91,8 +91,8 @@ "path": "Sources/aps/StateStore.swift", "kind": "file", "mode": 33188, - "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", - "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "payload_digest": "28d020ce5565b3c35cdfcd919a8191f7e2da45ff4c1ff35cef0348c44f2aa055", + "entry_digest": "a605fb78c95d63b1c1cdbf68b25896ab39dbe1cb78f2240162b17a7441dc4b5c", "owners": [ "state-store" ] @@ -101,8 +101,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", - "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "payload_digest": "10c11462b4b179680f6d8c468cef8c27dcd40e8792184743d26b8866059caf9e", + "entry_digest": "91593bbb8684eba55adb4ecb82d1f25d81a79443ea61729b21e9b35b365ab32d", "owners": [ "@exact:test" ] @@ -111,8 +111,8 @@ "path": "specs/aps-cli/aps-cli.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", - "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "payload_digest": "03f599b24a66c41336981a2058a5d26e7f75d8456a6f556f3f2096b4dedc9c4d", + "entry_digest": "8613eccbd83510588336bea997426747580c0ac512b8035613d5dcea7c203f53", "owners": [ "aps-cli" ] @@ -131,8 +131,8 @@ "path": "specs/aps-cli/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", - "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "payload_digest": "1ad52a139221d8bfabced8d08993e399140980a813852575769b37d4c075c901", + "entry_digest": "5668ffc635161c6fbf0f2d87569d3dd61d64bcc6d3c8f1437eb9dbdfbfe164b4", "owners": [ "aps-cli" ] @@ -171,8 +171,8 @@ "path": "specs/state-store/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", - "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "payload_digest": "2a29e8042e64f52435fd3e2762d91f6fd4d29862d5d7174ccfca26961bccf3e1", + "entry_digest": "7c39d633244caaecbf188f8567e635e88505a231758ae27a0e23b10eb1776960", "owners": [ "state-store" ] @@ -181,8 +181,8 @@ "path": "specs/state-store/state-store.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", - "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "payload_digest": "5e56d7201bcee99bc37e4efebd90c3aa1c126cffe4f1f07419270f5389922fd4", + "entry_digest": "1a4514d3e45b231b3d7f864f6f8366b75245f3dc891553bf5096c3d6be081739", "owners": [ "state-store" ] diff --git a/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/approvals.json b/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/approvals.json index 88f6894..432e69e 100644 --- a/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/approvals.json +++ b/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/approvals.json @@ -55,6 +55,13 @@ "timestamp": 1784410680, "digest": "7e2ed4c4d2aa668fdaaf4a205859a131720adf365fb7560b6f6d391d3f80e9bb", "note": "Re-accept after CHG-0015." + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784413307, + "digest": "a32a24e9383045b3b3683f82d62041accc32ca8513b0f533a5d132f7818b2856", + "note": null } ], "reopenings": [ @@ -905,6 +912,218 @@ }, "stale_acceptance_input_digest": "8e20e6d53b87e1e5d4749233d4e75815cb0a866f0f84f1ad9de5f8bf98afef5b", "current_acceptance_input_digest": "0835c20e53c79875e04a3e35c90dec1bc2e6073abf44c6fb935a6018446255f8" + }, + { + "schema_version": 1, + "change_id": "CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17", + "actor": "0xLeif", + "reason": "README.md changed by the issue-31 error contract PR after acceptance; rebinding evidence to current delivery inputs", + "timestamp": 1784413269, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784410680, + "digest": "7e2ed4c4d2aa668fdaaf4a205859a131720adf365fb7560b6f6d391d3f80e9bb", + "note": "Re-accept after CHG-0015." + }, + "prior_verification": { + "timestamp": 1784410672, + "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "contract_digest": "55d93919744f623103fb1d77e2cdb833a2d95075e3fc62ac31c6d09d506223b6", + "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", + "acceptance_input_digest": "7e6ba86dce639254b5adcfa1008f1c7dcc0a3ff9aa32d534f923b5d81a4c7ece", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "48d156cde46751f23dfe9c134d35c95faf3b8f173783dfb0b60872b118ab5cfb", + "entry_digest": "36a1e742298c093ae2ba5dd696140c37e443aacf8c11fc98903d3f57cab409ad", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "README.md", + "kind": "file", + "mode": 33188, + "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", + "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps/Aps.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "d41387167cb27edb9e448160642c67e3e9819ddcc8038b3b25768e40a5f14add", + "entry_digest": "b08d0765337a24358f50b9a61ac5faa4ba39b9da193b4845f7edb71af98fd721", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/CLIOutput.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "281d1cb75cf70f49afb03a272cefdb7786a7a04f057f6aa51df33a9b672c999b", + "entry_digest": "5373f2d9892d77adc64d10ac041d291256396d280dce6792d1ae79d6f37f752c", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoKey.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "25c58ea18945c28d0c0b1dcebfa2229470d137c6434c58c8edc21f06948e59f7", + "entry_digest": "baff1088e1c04eeb481be267b76094b189baba0093526105e37a9a98676712cc", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/StateStore.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", + "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "owners": [ + "state-store" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", + "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "owners": [ + "@exact:test" + ] + }, + { + "path": "specs/aps-cli/aps-cli.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", + "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7304a6233da2051242aeac63a51197aaf6f92d936692fe8655801eff842a2fd0", + "entry_digest": "e78ad0996c0135c37d41afa37c5fda13901bb63e14f00c3208896f7b104a72e8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", + "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ca6cafd22fb3c803eed3dc94cb980d8f535d51e0fd289b46bae82b7f255377de", + "entry_digest": "c349b8f073cefe7e387f8fabdd4dada3830be59b4b6d22a4ce79b2c29399c70e", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "472cbde38a2667652b54cda5c3ffa70ee30f91cd7de670804c0621459449bd84", + "entry_digest": "a016c672541231d3c30f08b033016d3f55b163084da74b5ec55e2ccc28c6b1be", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/state-store/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "12d848cab584c8a64f7da8bee796b581ec3b1b5efc72448299330dd493f39aa7", + "entry_digest": "76b734c278c05e3e74ff8f41dbb07aac3dcc7b1fbda64adcf32691e31365317c", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", + "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/state-store.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", + "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "0f02b782887467b358568bf22c2d68f39c40a00d56143bd6c64abdf8198cf433", + "entry_digest": "8f828f3625d4f24c3c71852bd28f76aac97cd31ace2a18d1a901a3d34642de8d", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "81ffb2b4e461759c297a2fcada8996026bfc8e18179c82365c987175829e6f05", + "entry_digest": "ca1f3e6e603b22125e5ad2ce203761b7f928029183cfbac6b5c292558197cf7b", + "owners": [ + "state-store" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-016", + "REQ-state-store-014" + ] + }, + "stale_acceptance_input_digest": "7e6ba86dce639254b5adcfa1008f1c7dcc0a3ff9aa32d534f923b5d81a4c7ece", + "current_acceptance_input_digest": "c2411e0e30480a17e034d35b6a44aa1322af55c8c98b67d2949148a35e1e6f57" } ] } diff --git a/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/state.json b/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/state.json index 960203e..e1f4b30 100644 --- a/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/state.json +++ b/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/state.json @@ -9,7 +9,7 @@ "canonical_applied": true, "base_commit": "d9500d41e7cfee56b7c40fc25886265c39f97e28", "created_at": 1784407712, - "updated_at": 1784410680, + "updated_at": 1784413307, "affected_specs": [ "aps-cli", "state-store" diff --git a/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/verification-attempts.json b/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/verification-attempts.json index 2ffced6..2ae1286 100644 --- a/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/verification-attempts.json +++ b/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/verification-attempts.json @@ -126,6 +126,24 @@ "REQ-aps-cli-016", "REQ-state-store-014" ] + }, + { + "timestamp": 1784413291, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "55d93919744f623103fb1d77e2cdb833a2d95075e3fc62ac31c6d09d506223b6", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-016", + "REQ-state-store-014" + ] } ] } diff --git a/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/verification.json b/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/verification.json index 5ae5413..3b6a57f 100644 --- a/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/verification.json +++ b/.specsync/changes/CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17/verification.json @@ -1,9 +1,9 @@ { - "timestamp": 1784410672, - "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "timestamp": 1784413291, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", "contract_digest": "55d93919744f623103fb1d77e2cdb833a2d95075e3fc62ac31c6d09d506223b6", - "workspace_digest": "59e3a9086dc67c553cf8fc0f628d31fc6582ceab2cfd269541fd229e59ea549f", - "acceptance_input_digest": "7e6ba86dce639254b5adcfa1008f1c7dcc0a3ff9aa32d534f923b5d81a4c7ece", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "acceptance_input_digest": "c2411e0e30480a17e034d35b6a44aa1322af55c8c98b67d2949148a35e1e6f57", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -21,8 +21,8 @@ "path": "README.md", "kind": "file", "mode": 33188, - "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", - "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "payload_digest": "3b917fc037efd07213569e55a7ae617b8a0223afcf7b90f2ac4d4555ad7af681", + "entry_digest": "585a51a96139d484ce531b9de06cb6ca65b53f8c58d505d255441aac11d42211", "owners": [ "@exact:delivery" ] @@ -31,8 +31,8 @@ "path": "Sources/aps/Aps.swift", "kind": "file", "mode": 33188, - "payload_digest": "d41387167cb27edb9e448160642c67e3e9819ddcc8038b3b25768e40a5f14add", - "entry_digest": "b08d0765337a24358f50b9a61ac5faa4ba39b9da193b4845f7edb71af98fd721", + "payload_digest": "b3dd9bb253ca4bca7e08b95dcb7da1b6a452130f32da0999ec9d85f1d5b7a491", + "entry_digest": "6326fa3f63954f3ad4614be6916ae3c2acb7c3f76b57ad1ac7ceb7ee215991c2", "owners": [ "aps-cli" ] @@ -41,8 +41,8 @@ "path": "Sources/aps/CLIOutput.swift", "kind": "file", "mode": 33188, - "payload_digest": "281d1cb75cf70f49afb03a272cefdb7786a7a04f057f6aa51df33a9b672c999b", - "entry_digest": "5373f2d9892d77adc64d10ac041d291256396d280dce6792d1ae79d6f37f752c", + "payload_digest": "28e5fb414d8272eb414a58c7c1e68e0e9d4bcaa0de64ba4c66b1888bb3dfc990", + "entry_digest": "4026319f59c9cbcb5b737efc1da34f4beaefb8502826a64affa9dc568864863b", "owners": [ "aps-cli" ] @@ -51,8 +51,8 @@ "path": "Sources/aps/DemoKey.swift", "kind": "file", "mode": 33188, - "payload_digest": "25c58ea18945c28d0c0b1dcebfa2229470d137c6434c58c8edc21f06948e59f7", - "entry_digest": "baff1088e1c04eeb481be267b76094b189baba0093526105e37a9a98676712cc", + "payload_digest": "a47ea6fbcd4538ead5097cdcaaa4aed28113289ad114bcbe9fafe7aafefe92cf", + "entry_digest": "c824f129c8ec4cfc23ded9895dd4110ae4f70a875ddc1ce81225e6d3f2e1b580", "owners": [ "aps-cli" ] @@ -61,8 +61,8 @@ "path": "Sources/aps/StateStore.swift", "kind": "file", "mode": 33188, - "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", - "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "payload_digest": "28d020ce5565b3c35cdfcd919a8191f7e2da45ff4c1ff35cef0348c44f2aa055", + "entry_digest": "a605fb78c95d63b1c1cdbf68b25896ab39dbe1cb78f2240162b17a7441dc4b5c", "owners": [ "state-store" ] @@ -71,8 +71,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", - "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "payload_digest": "10c11462b4b179680f6d8c468cef8c27dcd40e8792184743d26b8866059caf9e", + "entry_digest": "91593bbb8684eba55adb4ecb82d1f25d81a79443ea61729b21e9b35b365ab32d", "owners": [ "@exact:test" ] @@ -81,8 +81,8 @@ "path": "specs/aps-cli/aps-cli.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", - "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "payload_digest": "03f599b24a66c41336981a2058a5d26e7f75d8456a6f556f3f2096b4dedc9c4d", + "entry_digest": "8613eccbd83510588336bea997426747580c0ac512b8035613d5dcea7c203f53", "owners": [ "aps-cli" ] @@ -101,8 +101,8 @@ "path": "specs/aps-cli/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", - "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "payload_digest": "1ad52a139221d8bfabced8d08993e399140980a813852575769b37d4c075c901", + "entry_digest": "5668ffc635161c6fbf0f2d87569d3dd61d64bcc6d3c8f1437eb9dbdfbfe164b4", "owners": [ "aps-cli" ] @@ -141,8 +141,8 @@ "path": "specs/state-store/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", - "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "payload_digest": "2a29e8042e64f52435fd3e2762d91f6fd4d29862d5d7174ccfca26961bccf3e1", + "entry_digest": "7c39d633244caaecbf188f8567e635e88505a231758ae27a0e23b10eb1776960", "owners": [ "state-store" ] @@ -151,8 +151,8 @@ "path": "specs/state-store/state-store.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", - "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "payload_digest": "5e56d7201bcee99bc37e4efebd90c3aa1c126cffe4f1f07419270f5389922fd4", + "entry_digest": "1a4514d3e45b231b3d7f864f6f8366b75245f3dc891553bf5096c3d6be081739", "owners": [ "state-store" ] diff --git a/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/approvals.json b/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/approvals.json index ad92e3e..2f952fa 100644 --- a/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/approvals.json +++ b/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/approvals.json @@ -20,7 +20,84 @@ "timestamp": 1784409608, "digest": "79c7e5ff9d9f9a63e78a3467516661fa2e1d5bac682ac5bf1ca25e69f8ea3f71", "note": "Docs coverage matrix accepted." + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784413018, + "digest": "4d94602445c3c7e3f036457644f8921e79d057a21394afc32992899faaaee06b", + "note": null } ], - "reopenings": [] + "reopenings": [ + { + "schema_version": 1, + "change_id": "CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces", + "actor": "0xLeif", + "reason": "README.md / DemoKey.swift changed by the issue-31 error contract PR after acceptance; rebinding evidence to current delivery inputs", + "timestamp": 1784412987, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784409608, + "digest": "79c7e5ff9d9f9a63e78a3467516661fa2e1d5bac682ac5bf1ca25e69f8ea3f71", + "note": "Docs coverage matrix accepted." + }, + "prior_verification": { + "timestamp": 1784409601, + "commit": "e67ebd6084aa85beaf7f1ce219be5dde9cc30e0d", + "contract_digest": "a0e81300735aa82f646433bad125e6ac0043d3680d608e1ce6dbf42c7c730e6a", + "workspace_digest": "73b23f69f06702a7824f2aaa65bae3404b8f3763ad1c01e25bafca978ca51615", + "acceptance_input_digest": "e00e0d949cf2677b273b7fde220020008701f5dcc688bc78d7552696e89db537", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "af3c8353cd24a8a6592088883407b482cd73ea4324ccda5b230ccefcadb448f3", + "entry_digest": "cd3321bf077cb79382d5efc61d3bc66c5ab10ed2f5c3f236b3dcc1296c6ebc04", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "GOAL.md", + "kind": "file", + "mode": 33188, + "payload_digest": "20146bfa50165791ec6059f6245716170c43b23106e5123ec590a306a590a966", + "entry_digest": "68fbffe2a6cce1f402c5dcebbd733d2b1f3d5e3d259a0ee91dfc23bbc034fc56", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "README.md", + "kind": "file", + "mode": 33188, + "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", + "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "owners": [ + "@exact:delivery" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] + }, + "stale_acceptance_input_digest": "e00e0d949cf2677b273b7fde220020008701f5dcc688bc78d7552696e89db537", + "current_acceptance_input_digest": "1d5ee85f4b045fc6282a986e860058bb9d761ce58b0d4e9216103f9b72707f04" + } + ] } diff --git a/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/state.json b/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/state.json index f0482c1..5b80203 100644 --- a/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/state.json +++ b/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/state.json @@ -9,7 +9,7 @@ "canonical_applied": true, "base_commit": "20b6f8b24442e18257379fd238018b67615f536b", "created_at": 1784409533, - "updated_at": 1784409608, + "updated_at": 1784413018, "affected_specs": [], "affected_paths": [ "README.md", diff --git a/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/verification-attempts.json b/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/verification-attempts.json index 7127b07..893663a 100644 --- a/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/verification-attempts.json +++ b/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/verification-attempts.json @@ -15,6 +15,21 @@ } ], "requirement_ids": [] + }, + { + "timestamp": 1784413007, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "a0e81300735aa82f646433bad125e6ac0043d3680d608e1ce6dbf42c7c730e6a", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] } ] } diff --git a/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/verification.json b/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/verification.json index 9c15f90..b5c1ce1 100644 --- a/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/verification.json +++ b/.specsync/changes/CHG-0014-document-appstate-coverage-matrix-and-refresh-goal-for-0-2-dogfood-surfaces/verification.json @@ -1,9 +1,9 @@ { - "timestamp": 1784409601, - "commit": "e67ebd6084aa85beaf7f1ce219be5dde9cc30e0d", + "timestamp": 1784413007, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", "contract_digest": "a0e81300735aa82f646433bad125e6ac0043d3680d608e1ce6dbf42c7c730e6a", - "workspace_digest": "73b23f69f06702a7824f2aaa65bae3404b8f3763ad1c01e25bafca978ca51615", - "acceptance_input_digest": "e00e0d949cf2677b273b7fde220020008701f5dcc688bc78d7552696e89db537", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "acceptance_input_digest": "1d5ee85f4b045fc6282a986e860058bb9d761ce58b0d4e9216103f9b72707f04", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -31,8 +31,8 @@ "path": "README.md", "kind": "file", "mode": 33188, - "payload_digest": "2a129a3eaa24075114119341fb52421be111261ed531d6000c8dcf034daedb21", - "entry_digest": "11e067e782f8972c52f6637c4acb1da62e0da144c58bc02208cc88a5d662c54f", + "payload_digest": "3b917fc037efd07213569e55a7ae617b8a0223afcf7b90f2ac4d4555ad7af681", + "entry_digest": "585a51a96139d484ce531b9de06cb6ca65b53f8c58d505d255441aac11d42211", "owners": [ "@exact:delivery" ] diff --git a/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/approvals.json b/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/approvals.json index 6b10f84..a123e8f 100644 --- a/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/approvals.json +++ b/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/approvals.json @@ -20,7 +20,207 @@ "timestamp": 1784410559, "digest": "5754f11c6cad5575a3f450e75a926db5366e9555cf8c1e9b8b7d803f24136d8a", "note": "Accepted unreachable-code removal for issue 15." + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784413056, + "digest": "306c12783dd069b807380d51514ce0a846d86d6596a83b29509e57fafc9af3f8", + "note": null } ], - "reopenings": [] + "reopenings": [ + { + "schema_version": 1, + "change_id": "CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15", + "actor": "0xLeif", + "reason": "README.md / DemoKey.swift changed by the issue-31 error contract PR after acceptance; rebinding evidence to current delivery inputs", + "timestamp": 1784413031, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784410559, + "digest": "5754f11c6cad5575a3f450e75a926db5366e9555cf8c1e9b8b7d803f24136d8a", + "note": "Accepted unreachable-code removal for issue 15." + }, + "prior_verification": { + "timestamp": 1784410548, + "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "contract_digest": "30740768dcecceea1a7c3b740c383f846f150c509523673ef373157ac3e4aae5", + "workspace_digest": "db295578c927197cbe0a54d8e929d7745e7f8b949ed9feb668277d90e7f13324", + "acceptance_input_digest": "9b1532626af892ff0e3d64fad59ddb1691a36afd05940926edbe01d9b528ec34", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "84d780b51267d44f88acaf91878c56e8c4b0694608d1a9c53018ce5ea7b1de47", + "entry_digest": "25c7131b7c1001ce1ff855b28384ce161faf4270fa2111e7abdac2450b244f79", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps/DemoKey.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "25c58ea18945c28d0c0b1dcebfa2229470d137c6434c58c8edc21f06948e59f7", + "entry_digest": "baff1088e1c04eeb481be267b76094b189baba0093526105e37a9a98676712cc", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/Dependencies.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "14fced21000b935ed106a6a039bf574ed6cb9d8ba1598dc82001fbcd418a57a4", + "entry_digest": "0218077403628c671d6e778f6465e46e958a2c8f589d707f42a775f7da5cc799", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/StateStore.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", + "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "owners": [ + "state-store" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", + "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "owners": [ + "@exact:test" + ] + }, + { + "path": "specs/aps-cli/aps-cli.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", + "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7304a6233da2051242aeac63a51197aaf6f92d936692fe8655801eff842a2fd0", + "entry_digest": "e78ad0996c0135c37d41afa37c5fda13901bb63e14f00c3208896f7b104a72e8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", + "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ca6cafd22fb3c803eed3dc94cb980d8f535d51e0fd289b46bae82b7f255377de", + "entry_digest": "c349b8f073cefe7e387f8fabdd4dada3830be59b4b6d22a4ce79b2c29399c70e", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "472cbde38a2667652b54cda5c3ffa70ee30f91cd7de670804c0621459449bd84", + "entry_digest": "a016c672541231d3c30f08b033016d3f55b163084da74b5ec55e2ccc28c6b1be", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/state-store/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "12d848cab584c8a64f7da8bee796b581ec3b1b5efc72448299330dd493f39aa7", + "entry_digest": "76b734c278c05e3e74ff8f41dbb07aac3dcc7b1fbda64adcf32691e31365317c", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", + "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/state-store.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", + "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "0f02b782887467b358568bf22c2d68f39c40a00d56143bd6c64abdf8198cf433", + "entry_digest": "8f828f3625d4f24c3c71852bd28f76aac97cd31ace2a18d1a901a3d34642de8d", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "81ffb2b4e461759c297a2fcada8996026bfc8e18179c82365c987175829e6f05", + "entry_digest": "ca1f3e6e603b22125e5ad2ce203761b7f928029183cfbac6b5c292558197cf7b", + "owners": [ + "state-store" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-005", + "REQ-state-store-002" + ] + }, + "stale_acceptance_input_digest": "9b1532626af892ff0e3d64fad59ddb1691a36afd05940926edbe01d9b528ec34", + "current_acceptance_input_digest": "c79cb59788abf808060eabd2d5c6f0558ffe979bcee3bb1fa3fdf39755f6c26a" + } + ] } diff --git a/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/state.json b/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/state.json index f09b365..7d52383 100644 --- a/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/state.json +++ b/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/state.json @@ -9,7 +9,7 @@ "canonical_applied": true, "base_commit": "cb05f64c650260c5f3960b192d3be70c0e450627", "created_at": 1784410136, - "updated_at": 1784410559, + "updated_at": 1784413056, "affected_specs": [ "aps-cli", "state-store" diff --git a/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/verification-attempts.json b/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/verification-attempts.json index d10d595..0756ee6 100644 --- a/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/verification-attempts.json +++ b/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/verification-attempts.json @@ -18,6 +18,24 @@ "REQ-aps-cli-005", "REQ-state-store-002" ] + }, + { + "timestamp": 1784413043, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "30740768dcecceea1a7c3b740c383f846f150c509523673ef373157ac3e4aae5", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-005", + "REQ-state-store-002" + ] } ] } diff --git a/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/verification.json b/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/verification.json index f490095..b247b7b 100644 --- a/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/verification.json +++ b/.specsync/changes/CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15/verification.json @@ -1,9 +1,9 @@ { - "timestamp": 1784410548, - "commit": "b48db03409e43e5cc6fbd488967c9b08d6827243", + "timestamp": 1784413043, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", "contract_digest": "30740768dcecceea1a7c3b740c383f846f150c509523673ef373157ac3e4aae5", - "workspace_digest": "db295578c927197cbe0a54d8e929d7745e7f8b949ed9feb668277d90e7f13324", - "acceptance_input_digest": "9b1532626af892ff0e3d64fad59ddb1691a36afd05940926edbe01d9b528ec34", + "workspace_digest": "3f41d8768971ad9f900c5276e2d8db3f4eb334a5f190ee055d4bbec413074a9c", + "acceptance_input_digest": "c79cb59788abf808060eabd2d5c6f0558ffe979bcee3bb1fa3fdf39755f6c26a", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -21,8 +21,8 @@ "path": "Sources/aps/DemoKey.swift", "kind": "file", "mode": 33188, - "payload_digest": "25c58ea18945c28d0c0b1dcebfa2229470d137c6434c58c8edc21f06948e59f7", - "entry_digest": "baff1088e1c04eeb481be267b76094b189baba0093526105e37a9a98676712cc", + "payload_digest": "a47ea6fbcd4538ead5097cdcaaa4aed28113289ad114bcbe9fafe7aafefe92cf", + "entry_digest": "c824f129c8ec4cfc23ded9895dd4110ae4f70a875ddc1ce81225e6d3f2e1b580", "owners": [ "aps-cli" ] @@ -41,8 +41,8 @@ "path": "Sources/aps/StateStore.swift", "kind": "file", "mode": 33188, - "payload_digest": "08784dcb0adaf95e5238ad91c48cb5ece3dbf32a881203ee922422e127999fd7", - "entry_digest": "af5ccf11d9d32ba22e6aeba691e34a7804c70b387273084ffeab4eff137165ff", + "payload_digest": "28d020ce5565b3c35cdfcd919a8191f7e2da45ff4c1ff35cef0348c44f2aa055", + "entry_digest": "a605fb78c95d63b1c1cdbf68b25896ab39dbe1cb78f2240162b17a7441dc4b5c", "owners": [ "state-store" ] @@ -51,8 +51,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "aedc514d235cd22ab52b3dfc84c2b7db524d5f142a33636b0ed0a914885db119", - "entry_digest": "e2573b24dd5260ada29843c147236aaf95c34eaef99f7a41b57cbb2de4e8e91e", + "payload_digest": "10c11462b4b179680f6d8c468cef8c27dcd40e8792184743d26b8866059caf9e", + "entry_digest": "91593bbb8684eba55adb4ecb82d1f25d81a79443ea61729b21e9b35b365ab32d", "owners": [ "@exact:test" ] @@ -61,8 +61,8 @@ "path": "specs/aps-cli/aps-cli.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "25caae644bb805e03d820406e83b8919dfe2a4c7595e7e2038a50fee841e41a8", - "entry_digest": "03130d509f9a380a91ae679c516dbb1de655979ff44abc49473f77278f88f841", + "payload_digest": "03f599b24a66c41336981a2058a5d26e7f75d8456a6f556f3f2096b4dedc9c4d", + "entry_digest": "8613eccbd83510588336bea997426747580c0ac512b8035613d5dcea7c203f53", "owners": [ "aps-cli" ] @@ -81,8 +81,8 @@ "path": "specs/aps-cli/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "895175bc65752ec06e6c09c9fff940a1c90cbab26962ab16a0144b56b89d1ab3", - "entry_digest": "bb095b03817bb5ecefa8e48bc03a838a45b2a4ed41eb9e7f2311ac2711ac91d8", + "payload_digest": "1ad52a139221d8bfabced8d08993e399140980a813852575769b37d4c075c901", + "entry_digest": "5668ffc635161c6fbf0f2d87569d3dd61d64bcc6d3c8f1437eb9dbdfbfe164b4", "owners": [ "aps-cli" ] @@ -121,8 +121,8 @@ "path": "specs/state-store/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "af70549c9a989a19fcea57c018468acc4efccf7afaf93147c042f8ea818a79c2", - "entry_digest": "697641a26af004e1b5a813830a300f95955720b71b06e7832be4057c0a3cad00", + "payload_digest": "2a29e8042e64f52435fd3e2762d91f6fd4d29862d5d7174ccfca26961bccf3e1", + "entry_digest": "7c39d633244caaecbf188f8567e635e88505a231758ae27a0e23b10eb1776960", "owners": [ "state-store" ] @@ -131,8 +131,8 @@ "path": "specs/state-store/state-store.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "1fe618d7665983821eeff63239e16ceef57fc52d1647e14084254792dc0cbe7d", - "entry_digest": "f0388b2de7b948fba051afc436c5c4d55f664db8c3c31a8347e794f6967a09ee", + "payload_digest": "5e56d7201bcee99bc37e4efebd90c3aa1c126cffe4f1f07419270f5389922fd4", + "entry_digest": "1a4514d3e45b231b3d7f864f6f8366b75245f3dc891553bf5096c3d6be081739", "owners": [ "state-store" ] diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/approvals.json b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/approvals.json new file mode 100644 index 0000000..ed32752 --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/approvals.json @@ -0,0 +1,33 @@ +{ + "approvals": [ + { + "gate": "definition", + "actor": "0xLeif", + "timestamp": 1784412511, + "digest": "a8ced7b7a982e3df8712475214ae3f0608c01802915ab2ca7266d539e61984af", + "note": null + }, + { + "gate": "definition", + "actor": "0xLeif", + "timestamp": 1784412615, + "digest": "2055ab04e1e6e83466cc6a2b1d6a262fb97b745c60b7fc294050ffe0d6f1325b", + "note": null + }, + { + "gate": "definition", + "actor": "0xLeif", + "timestamp": 1784412720, + "digest": "c7399f27aecb96f006e9f3a43a9a1ec3096ce63574f1edf56db3e56a7fcc2a5a", + "note": null + }, + { + "gate": "acceptance", + "actor": "0xLeif", + "timestamp": 1784412742, + "digest": "0240fce373ff621cb02d634685a4173b532113d2b60f9f280f35b8c52a877ffe", + "note": null + } + ], + "reopenings": [] +} diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/change.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/change.md new file mode 100644 index 0000000..58c6460 --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/change.md @@ -0,0 +1,25 @@ +--- +id: CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31 +state: accepted +type: feature +base_commit: c0c5b4bfb7c712506f9795c80889f03094596629 +--- + +# Error contract: exit-code taxonomy and JSON error envelope (issue 31) + +## Intent + +Error contract: exit-code taxonomy and JSON error envelope (issue 31) + +## Affected Canonical Specs + +- `aps-cli` +- `state-store` + +## Acceptance Criteria + +- Exit codes follow the taxonomy (64 usage, 65 corrupt state, 69 unavailable, 70 internal, 73 persist-fail); domain errors emit a human stderr line plus a {error:{code,message,hint}} envelope with --json/--jsonl or APS_ERROR_JSON=1; corrupt note.json/profile.json exits 65 instead of returning the initial value; stdout stays empty on error; tests and smoke assert all of it. + +## No-spec Rationale + +Not applicable diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/context.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/context.md new file mode 100644 index 0000000..957f0cb --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/context.md @@ -0,0 +1,22 @@ +--- +change: CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31 +artifact: context +--- + +# Context + +Issue #31 (design decision 4): every domain error collapsed into +`ValidationError` and exit 64 with human-only text, so agents could not +distinguish "fix your invocation" from "environment broken". Corrupt +persisted state was silently masked as the initial value. + +## Decisions + +- sysexits-aligned taxonomy: 64 usage, 65 corrupt data, 69 unavailable, + 70 internal, 73 write did not persist (66 reserved). +- One failure path (`CLIOutput.fail`): human line on stderr, optional + `{"error":{"code","message","hint"}}` envelope, taxonomy exit code. +- Envelope timing: machine modes (`--json`/`--jsonl`) or `APS_ERROR_JSON=1`. +- Loud corrupt-state: `StateStore.ensureReadable` distinguishes missing + (initial value) from existing-but-undecodable (exit 65) before + `get` / `watch` output. diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/deltas/aps-cli.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/deltas/aps-cli.md new file mode 100644 index 0000000..1f05cdd --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/deltas/aps-cli.md @@ -0,0 +1,88 @@ +# aps-cli error contract: exit taxonomy and JSON envelope + +## MODIFIED + +### REQUIREMENT REQ-aps-cli-002 + +`set` SHALL reject values that cannot parse to the key's type and exit non-zero via `APSError.invalidValue`. All domain errors SHALL follow the error contract: human line on stderr, taxonomy exit code, and a JSON envelope when `--json` / `--jsonl` or `APS_ERROR_JSON=1`. + +Acceptance Criteria +- Non-integer `counter` values fail with an invalid-value message. +- Non-boolean `flag` values fail with an invalid-value message. +- `APSError.description` names the key and expected type. +- Exit codes: 64 usage, 65 corrupt persisted state, 69 unavailable, 70 internal, 73 write did not persist. +- The envelope shape is `{"error":{"code","message","hint"}}` with stable snake_case codes; stdout stays empty on error. +- Corrupt `note.json` / `profile.json` exits 65 instead of returning the initial value. + +### SPEC SECTION Public API + +| Export | Description | +|--------|-------------| +| `DemoKey` | Fixed schema enum including `profile`, `secret`, and `profileName`. | +| `ProfileDocument` | Codable `{name, version}` FileState document. | +| `name` | ProfileDocument display name field. | +| `version` | ProfileDocument integer version field. | +| `init` | ProfileDocument memberwise initializer. | +| `APSKeychain` | Well-known Keychain service/account for `secret`. | +| `service` | Reverse-DNS Keychain feature namespace (`dev.leif.aps`). | +| `account` | Keychain account id (`secret`). | +| `secretAccount` | Full account key (`dev.leif.aps/secret`). | +| `APSError` | Typed CLI/domain errors. | +| `counter` | Int key stored in AppState `State`. | +| `message` | String key stored in AppState `State`. | +| `flag` | Bool key stored in AppState `StoredState`. | +| `note` | String key stored in AppState `FileState`. | +| `profile` | ProfileDocument key stored in AppState `FileState`. | +| `secret` | String key stored in AppState `SecureState` (Keychain). | +| `profileName` | String Slice over `ProfileDocument.name`. | +| `invalidValue` | Value could not parse for the key type. | +| `encodingFailed` | UTF-8 JSON encode failure. | +| `decodingFailed` | UTF-8 JSON decode failure. | +| `persistenceFailed` | Disk/Keychain-backed key did not persist after write. | +| `keychainUnavailable` | SecureState unavailable without Apple Security. | +| `storage` | Human storage kind (`State` / `StoredState` / `FileState` / `SecureState` / `Slice`). | +| `valueType` | Human value type (`Int` / `String` / `Bool` / `ProfileDocument`). | +| `helpSummary` | Tab-separated key/type/storage columns for `keys`. | +| `detail` | One-line description for `keys`. | +| `description` | Actionable error text for humans. | +| `code` | Stable machine error code for the JSON envelope. | +| `exitCode` | sysexits-aligned process exit code. | +| `hint` | Actionable next step in the error envelope. | + +### SPEC SECTION Invariants + +1. The CLI entry point runs on the real main thread so AppState + `notifyChange()` assertions hold on Linux and macOS. +2. stdout for `get` / `set` / `watch` / `reset ` is only the value line(s); + stdout stays empty on error; help uses ArgumentParser defaults and domain + errors use the Error Cases contract (human line plus optional JSON envelope). +3. `State` keys are process-local; a new process must not be expected to retain + `counter` or `message`. +4. `watch` must flush each printed value immediately when stdout is not a TTY. +5. `keys` and `--help` do not mutate application state. + +### SPEC SECTION Error Cases + +Domain errors fail through a single contract: a human line on stderr, an +optional JSON envelope, and a taxonomy exit code. + +Exit codes (sysexits-aligned): + +| Code | Meaning | aps mapping | +|------|---------|-------------| +| 0 | success | stdout contract satisfied | +| 64 | EX_USAGE | caller-fixable: bad key/flags, `invalidValue`, reset arg conflicts | +| 65 | EX_DATAERR | corrupt persisted state: existing file does not decode (`decodingFailed`) | +| 69 | EX_UNAVAILABLE | `keychainUnavailable` on platforms without Apple Security | +| 70 | EX_SOFTWARE | internal bug: `encodingFailed` | +| 73 | EX_CANTCREAT | write did not persist: `persistenceFailed` | +| 66 | EX_NOINPUT | reserved for future explicit-file operations | + +- Missing state files are not errors: they mean the initial value. +- A disk-backed file that exists but does not decode fails loudly (65) via + `StateStore.ensureReadable` before `get` / `watch` output. +- With `--json` / `--jsonl`, or when `APS_ERROR_JSON=1`, stderr additionally + gets one `{"error":{"code","message","hint"}}` envelope; stdout stays empty + on error in every mode. +- Unknown `DemoKey` token and flag shape errors: ArgumentParser rejects + before `run()` with its own 64. diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/deltas/state-store.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/deltas/state-store.md new file mode 100644 index 0000000..e1c9e68 --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/deltas/state-store.md @@ -0,0 +1,56 @@ +# state-store error contract support + +## MODIFIED + +### REQUIREMENT REQ-state-store-011 + +`APSPaths.resolve(stateDir:)` SHALL prefer `--state-dir`, then `APS_HOME`, then `~/.aps` when configuring FileState paths from CLI boot. + +Acceptance Criteria +- Explicit stateDir wins over environment. +- Missing both returns the default `~/.aps` path. + +### SPEC SECTION Public API + +| Export | Description | +|--------|-------------| +| `StateStore` | MainActor facade over demo AppState keys. | +| `init` | Loads clock/jsonCoding/stats dependencies without forcing `~/.aps`. | +| `get` | Returns the current string rendering for a demo key. | +| `set` | Parses and writes a demo key value; records a stats mutation. | +| `reset` | Restores one demo key to its initial value; records a stats mutation. | +| `resetAll` | Restores every demo key. | +| `dump` | Pretty JSON snapshot with typed values. | +| `watchBlocking` | Observation + polling watch loop for demo keys. | +| `watchStatsBlocking` | Combine + polling watch loop for ObservedDependency stats. | +| `statsSnapshot` | Immutable view of DemoStats counters. | +| `resetStats` | Clears process-local DemoStats counters. | +| `profileDocument` | Typed profile FileState accessor. | +| `profileName` | Slice accessor for ProfileDocument.name. | +| `readNoteFromDisk` | Direct `note.json` read bypassing cache; corrupt file throws `decodingFailed`. | +| `readProfileFromDisk` | Direct `profile.json` read bypassing cache; corrupt file throws `decodingFailed`. | +| `ensureReadable` | Loud corrupt-state check before `get` / `watch` output. | +| `parseBool` | Bool token parser for flag values. | +| `APSClock` | Injected clock dependency protocol. | +| `now` | APSClock current instant. | +| `SystemAPSClock` | Date-backed clock. | +| `JSONCoding` | Shared encode helpers for dump output. | +| `encodePretty` | Pretty JSON encode helper. | +| `DemoStats` | ObservableObject mutation-stats dependency. | +| `mutationCount` | Number of recorded set/reset mutations. | +| `lastMutatedKey` | Raw demo key of the latest mutation. | +| `recordMutation` | Increments counters for a demo key. | +| `reset` | Clears DemoStats counters. | +| `DemoStatsSnapshot` | Codable snapshot of DemoStats. | + +### SPEC SECTION Error Cases + +- `set(.counter, value: "nope")` throws `APSError.invalidValue`. +- `set(.flag, value: "maybe")` throws `APSError.invalidValue`. +- JSONCoding encode failures surface as `APSError.encodingFailed` when UTF-8 + conversion fails. Profile JSON parse failures surface as `APSError.invalidValue`. +- `readNoteFromDisk` / `readProfileFromDisk`: missing or unreadable file throws + `APSError.persistenceFailed`; an existing file that does not decode throws + `APSError.decodingFailed` (exit 65 per the CLI error contract). +- `ensureReadable` ignores missing files (initial-value semantics) and throws + `APSError.decodingFailed` for corrupt `note.json` / `profile.json`. diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/design.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/design.md new file mode 100644 index 0000000..0a9c73c --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/design.md @@ -0,0 +1,20 @@ +--- +change: CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31 +artifact: design +--- + +# Design + +- `APSError` gains `code` (stable snake_case), `exitCode` (Int32 taxonomy), + and `hint` (actionable next step). +- `CLIOutput.ErrorEnvelope` (`{"error":{"code","message","hint"}}`) encodes + via `encodeLine` with `.withoutEscapingSlashes`. +- `CLIOutput.fail(_:json:)` writes the human line, conditionally the + envelope, then throws `ExitCode(error.exitCode)`. All commands catch + `APSError` through it; ArgumentParser usage errors keep their own 64. +- `readNoteFromDisk` / `readProfileFromDisk` split failure modes: + missing/unreadable -> `persistenceFailed`; existing-but-undecodable -> + `decodingFailed`. `set` write verification maps any read-back failure to + `persistenceFailed`. +- `ensureReadable(_:)` runs before `get` / `watch` output for disk-backed + keys; missing file passes, corrupt file throws `decodingFailed`. diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/docs.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/docs.md new file mode 100644 index 0000000..5295d8f --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/docs.md @@ -0,0 +1,10 @@ +--- +change: CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31 +artifact: docs +--- + +# Docs + +README gains an "Error contract" section: exit-code table (0/64/65/69/70/73 +plus 66 reserved), the missing-means-initial rule, the JSON envelope shape +with a real example, the stable code list, and `APS_ERROR_JSON=1`. diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/plan.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/plan.md new file mode 100644 index 0000000..961821d --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/plan.md @@ -0,0 +1,16 @@ +--- +change: CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31 +artifact: plan +--- + +# Plan + +1. Extend `APSError` with code/exitCode/hint. +2. Add `ErrorEnvelope`, `structuredErrorsEnabled`, `writeError`, and `fail` + to `CLIOutput`; route every command through `fail`. +3. Split disk-reader failure modes; add `ensureReadable`; map `set` + verification failures to `persistenceFailed`. +4. Tests: taxonomy mapping, envelope shape, ensureReadable semantics. +5. Smoke: exit codes 64/65/73, stdout purity, envelope greps, + `APS_ERROR_JSON=1`. +6. Specs and README in the same change (spec lockstep rule). diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/requirements.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/requirements.md new file mode 100644 index 0000000..6cce294 --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/requirements.md @@ -0,0 +1,13 @@ +--- +change: CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31 +artifact: requirements +--- + +# Requirements + +- REQ-aps-cli-002 extended: taxonomy exit codes (64/65/69/70/73), + envelope shape with stable codes, stdout purity on error, corrupt state + exits 65. +- New REQ-state-store-011: `ensureReadable` distinguishes missing from + corrupt persisted state; readers throw `persistenceFailed` when missing, + `decodingFailed` when corrupt. diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/state.json b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/state.json new file mode 100644 index 0000000..2a1a535 --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/state.json @@ -0,0 +1,44 @@ +{ + "schema_version": 1, + "id": "CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31", + "slug": "error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31", + "title": "Error contract: exit-code taxonomy and JSON error envelope (issue 31)", + "description": "Error contract: exit-code taxonomy and JSON error envelope (issue 31)", + "kind": "feature", + "state": "accepted", + "canonical_applied": true, + "base_commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "created_at": 1784412172, + "updated_at": 1784412742, + "affected_specs": [ + "aps-cli", + "state-store" + ], + "affected_paths": [ + "Sources/aps", + "Tests/apsTests", + "Scripts", + "specs", + "README.md", + ".specsync/change-sequence.json" + ], + "no_spec_change": false, + "no_spec_change_rationale": null, + "acceptance_criteria": [ + "Exit codes follow the taxonomy (64 usage, 65 corrupt state, 69 unavailable, 70 internal, 73 persist-fail); domain errors emit a human stderr line plus a {error:{code,message,hint}} envelope with --json/--jsonl or APS_ERROR_JSON=1; corrupt note.json/profile.json exits 65 instead of returning the initial value; stdout stays empty on error; tests and smoke assert all of it." + ], + "selected_artifacts": [ + "context", + "requirements", + "plan", + "tasks", + "testing", + "docs", + "design" + ], + "dependencies": [], + "answers": { + "architecture_risk": "no", + "public_contract": "yes" + } +} diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/tasks.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/tasks.md new file mode 100644 index 0000000..c05a2f6 --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/tasks.md @@ -0,0 +1,12 @@ +--- +change: CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31 +artifact: tasks +--- + +# Tasks + +- [x] APSError code/exitCode/hint +- [x] CLIOutput envelope + fail path in all commands +- [x] Disk reader failure split + ensureReadable +- [x] Unit tests and smoke assertions +- [x] Spec and README updates diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/testing.md b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/testing.md new file mode 100644 index 0000000..6fca83d --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/testing.md @@ -0,0 +1,18 @@ +--- +change: CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31 +artifact: testing +--- + +# Testing + +## Requirement evidence + +| Requirement | Evidence | +| --- | --- | +| REQ-aps-cli-002 | `testAPSErrorContractCodesAndExitCodes`, `testErrorEnvelopeEncodesStableShape`, `testStructuredErrorsEnabledModes`; smoke asserts exit 64/65/73, stdout purity, and both envelope greps | +| REQ-state-store-011 | `testEnsureReadableMissingFileIsInitialSemantics`, `testEnsureReadableCorruptFileThrowsDecodingFailed`, `testEnsureReadableCorruptProfileThrowsDecodingFailed` | + +## Suites + +- `swift test` +- `fledge lanes run verify` diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/verification-attempts.json b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/verification-attempts.json new file mode 100644 index 0000000..2bae2c6 --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/verification-attempts.json @@ -0,0 +1,41 @@ +{ + "schema_version": 1, + "attempts": [ + { + "timestamp": 1784412643, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "2055ab04e1e6e83466cc6a2b1d6a262fb97b745c60b7fc294050ffe0d6f1325b", + "workspace_digest": "9218d4a8d76b11fd5beff08195e932dd34f5dee74a91729cf16c57854502213b", + "passed": false, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-002", + "REQ-state-store-011" + ] + }, + { + "timestamp": 1784412733, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "c7399f27aecb96f006e9f3a43a9a1ec3096ce63574f1edf56db3e56a7fcc2a5a", + "workspace_digest": "9218d4a8d76b11fd5beff08195e932dd34f5dee74a91729cf16c57854502213b", + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-002", + "REQ-state-store-011" + ] + } + ] +} diff --git a/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/verification.json b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/verification.json new file mode 100644 index 0000000..530be75 --- /dev/null +++ b/.specsync/changes/CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31/verification.json @@ -0,0 +1,264 @@ +{ + "timestamp": 1784412733, + "commit": "c0c5b4bfb7c712506f9795c80889f03094596629", + "contract_digest": "c7399f27aecb96f006e9f3a43a9a1ec3096ce63574f1edf56db3e56a7fcc2a5a", + "workspace_digest": "9218d4a8d76b11fd5beff08195e932dd34f5dee74a91729cf16c57854502213b", + "acceptance_input_digest": "6ebf2970722f8dfa730f59979dfc2551b9d76a397dab4545e5e3e5a7880916a3", + "acceptance_manifest": { + "schema_version": 1, + "entries": [ + { + "path": ".specsync/change-sequence.json", + "kind": "file", + "mode": 33188, + "payload_digest": "1021b7ce0dcb4b5bd0527699bd412a2ac43d63d4c096e3c02fc5f5ad05cd96e8", + "entry_digest": "8324b42e44a5388a0f96b0304f501967fc696e36e23d9dc2a249d0905fa1b60c", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "README.md", + "kind": "file", + "mode": 33188, + "payload_digest": "3b917fc037efd07213569e55a7ae617b8a0223afcf7b90f2ac4d4555ad7af681", + "entry_digest": "585a51a96139d484ce531b9de06cb6ca65b53f8c58d505d255441aac11d42211", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Scripts", + "kind": "non_file", + "mode": 0, + "payload_digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "entry_digest": "51cf8778887c4859764e714d4dc47ba1a49c518cd9b9f15846e599e747660ddc", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Scripts/smoke.sh", + "kind": "file", + "mode": 33261, + "payload_digest": "ffeb56eb5676f0460aa4fac09df71bf6f8c6e010e833159ff1e76130c5b6185a", + "entry_digest": "76a215ac9afa32180fb2643cbcec67035a05ace4162cb7ba74c9df4c82350e6b", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps", + "kind": "non_file", + "mode": 0, + "payload_digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "entry_digest": "c3673ff01b557be1dd094e3ec158466bdfb7f5b5070a6dee2d1db77809eb01d3", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps/Aps.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "b3dd9bb253ca4bca7e08b95dcb7da1b6a452130f32da0999ec9d85f1d5b7a491", + "entry_digest": "6326fa3f63954f3ad4614be6916ae3c2acb7c3f76b57ad1ac7ceb7ee215991c2", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/CLIOutput.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "28e5fb414d8272eb414a58c7c1e68e0e9d4bcaa0de64ba4c66b1888bb3dfc990", + "entry_digest": "4026319f59c9cbcb5b737efc1da34f4beaefb8502826a64affa9dc568864863b", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoKey.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "a47ea6fbcd4538ead5097cdcaaa4aed28113289ad114bcbe9fafe7aafefe92cf", + "entry_digest": "c824f129c8ec4cfc23ded9895dd4110ae4f70a875ddc1ce81225e6d3f2e1b580", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoState.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "62cd24efb557b5cef77e7aae804f933f4b53caf47195968764f05cc5a0a0d9b8", + "entry_digest": "fb710f893d343d67c1fd7a5a5cc50c5c49caf0aa182e5af17ee7c97ff98f8122", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/Dependencies.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "14fced21000b935ed106a6a039bf574ed6cb9d8ba1598dc82001fbcd418a57a4", + "entry_digest": "0218077403628c671d6e778f6465e46e958a2c8f589d707f42a775f7da5cc799", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/StateStore.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "28d020ce5565b3c35cdfcd919a8191f7e2da45ff4c1ff35cef0348c44f2aa055", + "entry_digest": "a605fb78c95d63b1c1cdbf68b25896ab39dbe1cb78f2240162b17a7441dc4b5c", + "owners": [ + "state-store" + ] + }, + { + "path": "Tests/apsTests", + "kind": "non_file", + "mode": 0, + "payload_digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "entry_digest": "ca9ac243b7ab4799cae661668bac2f1370bb7fdbce8c856eb3d54925e91eeabb", + "owners": [ + "@exact:test" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "10c11462b4b179680f6d8c468cef8c27dcd40e8792184743d26b8866059caf9e", + "entry_digest": "91593bbb8684eba55adb4ecb82d1f25d81a79443ea61729b21e9b35b365ab32d", + "owners": [ + "@exact:test" + ] + }, + { + "path": "specs", + "kind": "non_file", + "mode": 0, + "payload_digest": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "entry_digest": "d654cba9ce3c5b8a9c72964ac1f99b8ae3d9a6de6b54d1baa36d7558aac4bb42", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "specs/aps-cli/aps-cli.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "03f599b24a66c41336981a2058a5d26e7f75d8456a6f556f3f2096b4dedc9c4d", + "entry_digest": "8613eccbd83510588336bea997426747580c0ac512b8035613d5dcea7c203f53", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7304a6233da2051242aeac63a51197aaf6f92d936692fe8655801eff842a2fd0", + "entry_digest": "e78ad0996c0135c37d41afa37c5fda13901bb63e14f00c3208896f7b104a72e8", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "1ad52a139221d8bfabced8d08993e399140980a813852575769b37d4c075c901", + "entry_digest": "5668ffc635161c6fbf0f2d87569d3dd61d64bcc6d3c8f1437eb9dbdfbfe164b4", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "ca6cafd22fb3c803eed3dc94cb980d8f535d51e0fd289b46bae82b7f255377de", + "entry_digest": "c349b8f073cefe7e387f8fabdd4dada3830be59b4b6d22a4ce79b2c29399c70e", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/aps-cli/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "472cbde38a2667652b54cda5c3ffa70ee30f91cd7de670804c0621459449bd84", + "entry_digest": "a016c672541231d3c30f08b033016d3f55b163084da74b5ec55e2ccc28c6b1be", + "owners": [ + "aps-cli" + ] + }, + { + "path": "specs/state-store/context.md", + "kind": "file", + "mode": 33188, + "payload_digest": "12d848cab584c8a64f7da8bee796b581ec3b1b5efc72448299330dd493f39aa7", + "entry_digest": "76b734c278c05e3e74ff8f41dbb07aac3dcc7b1fbda64adcf32691e31365317c", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/requirements.md", + "kind": "file", + "mode": 33188, + "payload_digest": "2a29e8042e64f52435fd3e2762d91f6fd4d29862d5d7174ccfca26961bccf3e1", + "entry_digest": "7c39d633244caaecbf188f8567e635e88505a231758ae27a0e23b10eb1776960", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/state-store.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "5e56d7201bcee99bc37e4efebd90c3aa1c126cffe4f1f07419270f5389922fd4", + "entry_digest": "1a4514d3e45b231b3d7f864f6f8366b75245f3dc891553bf5096c3d6be081739", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/tasks.md", + "kind": "file", + "mode": 33188, + "payload_digest": "0f02b782887467b358568bf22c2d68f39c40a00d56143bd6c64abdf8198cf433", + "entry_digest": "8f828f3625d4f24c3c71852bd28f76aac97cd31ace2a18d1a901a3d34642de8d", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/testing.md", + "kind": "file", + "mode": 33188, + "payload_digest": "81ffb2b4e461759c297a2fcada8996026bfc8e18179c82365c987175829e6f05", + "entry_digest": "ca1f3e6e603b22125e5ad2ce203761b7f928029183cfbac6b5c292558197cf7b", + "owners": [ + "state-store" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [ + "REQ-aps-cli-002", + "REQ-state-store-011" + ] +} diff --git a/README.md b/README.md index 4ead34c..d23a064 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,29 @@ swift run aps stats --json `watch` uses Swift Observation for in-process updates and polls as a fallback so disk-backed `FileState` / `StoredState` changes can still surface, including updates written by another `aps` process. For `note` and `profile`, polling reads the JSON files directly so AppState's FileState cache cannot hide cross-process writes. +### Error contract + +Domain errors always print a human line to stderr and keep stdout empty, with a sysexits-aligned exit code: + +| Code | Meaning | When | +|------|---------|------| +| 0 | success | stdout contract satisfied | +| 64 | EX_USAGE | caller-fixable input: bad key/flags, invalid value | +| 65 | EX_DATAERR | corrupt persisted state (existing file does not decode) | +| 69 | EX_UNAVAILABLE | `secret` on a platform without Apple Security | +| 70 | EX_SOFTWARE | internal bug | +| 73 | EX_CANTCREAT | write did not persist (unwritable state root) | + +64 means fix the invocation; 65+ means environment or data; 70 means an aps bug. Missing state files are not errors: they mean the initial value. + +With `--json` / `--jsonl`, or when `APS_ERROR_JSON=1`, stderr additionally gets one structured envelope: + +```json +{"error":{"code":"invalid_value","hint":"Run `aps keys` to see expected types per key.","message":"Invalid value 'nope' for counter (Int)"}} +``` + +`code` is stable and safe to match on: `invalid_value`, `encoding_failed`, `decoding_failed`, `persistence_failed`, `keychain_unavailable`. + ## Tests and smoke ```bash diff --git a/Scripts/smoke.sh b/Scripts/smoke.sh index d8f1407..7c20092 100755 --- a/Scripts/smoke.sh +++ b/Scripts/smoke.sh @@ -91,4 +91,30 @@ if "$bin" set counter nope >/dev/null 2>&1; then exit 1 fi +# Error contract: taxonomy exit codes, stdout purity, JSON envelope. +out="$("$bin" set counter nope 2>/dev/null)" && { echo "expected invalid counter to fail" >&2; exit 1; } +test $? -eq 64 || { echo "expected exit 64 for invalid value" >&2; exit 1; } +test -z "$out" || { echo "stdout must stay empty on error" >&2; exit 1; } +err="$("$bin" set counter nope --json 2>&1 >/dev/null || true)" +echo "$err" | grep -q '"code":"invalid_value"' + +# Corrupt persisted state exits 65 with a decoding_failed envelope. +echo 'garbage{{' > "$APS_HOME/note.json" +out="$("$bin" get note 2>/dev/null)" && { echo "expected corrupt note to fail" >&2; exit 1; } +test $? -eq 65 || { echo "expected exit 65 for corrupt state" >&2; exit 1; } +test -z "$out" || { echo "stdout must stay empty on corrupt state" >&2; exit 1; } +err="$("$bin" get note --json 2>&1 >/dev/null || true)" +echo "$err" | grep -q '"code":"decoding_failed"' +"$bin" reset note >/dev/null + +# Unwritable state root exits 73 on write. +BADROOT="$(mktemp "${TMPDIR:-/tmp}/aps-smoke-file.XXXXXX")" +out="$("$bin" set note x --state-dir "$BADROOT" 2>/dev/null)" && { echo "expected unwritable root to fail" >&2; exit 1; } +test $? -eq 73 || { echo "expected exit 73 for unwritable state root" >&2; exit 1; } +rm -f "$BADROOT" + +# APS_ERROR_JSON=1 opts into structured errors without --json. +err="$(APS_ERROR_JSON=1 "$bin" set flag maybe 2>&1 >/dev/null || true)" +echo "$err" | grep -q '"code":"invalid_value"' + echo "smoke ok" diff --git a/Sources/aps/Aps.swift b/Sources/aps/Aps.swift index 15553db..14ac0f6 100644 --- a/Sources/aps/Aps.swift +++ b/Sources/aps/Aps.swift @@ -51,16 +51,21 @@ extension Aps { try onMainThread { boot(stateDir: options.stateDir) let store = StateStore() - if options.json { - let payload = CLIOutput.KeyValuePayload( - key: key.rawValue, - type: key.valueType, - storage: key.storage, - value: try CLIOutput.typedValue(for: key, store: store) - ) - print(try CLIOutput.encodePretty(payload)) - } else { - print(store.get(key)) + do { + try store.ensureReadable(key) + if options.json { + let payload = CLIOutput.KeyValuePayload( + key: key.rawValue, + type: key.valueType, + storage: key.storage, + value: try CLIOutput.typedValue(for: key, store: store) + ) + print(try CLIOutput.encodePretty(payload)) + } else { + print(store.get(key)) + } + } catch let error as APSError { + try CLIOutput.fail(error, json: options.json) } } } @@ -86,19 +91,19 @@ extension Aps { let store = StateStore() do { try store.set(key, value: value) + if options.json { + let payload = CLIOutput.KeyValuePayload( + key: key.rawValue, + type: key.valueType, + storage: key.storage, + value: try CLIOutput.typedValue(for: key, store: store) + ) + print(try CLIOutput.encodePretty(payload)) + } else { + print(store.get(key)) + } } catch let error as APSError { - throw ValidationError(error.description) - } - if options.json { - let payload = CLIOutput.KeyValuePayload( - key: key.rawValue, - type: key.valueType, - storage: key.storage, - value: try CLIOutput.typedValue(for: key, store: store) - ) - print(try CLIOutput.encodePretty(payload)) - } else { - print(store.get(key)) + try CLIOutput.fail(error, json: options.json) } } } @@ -131,35 +136,40 @@ extension Aps { try onMainThread { boot(stateDir: stateDir) let store = StateStore() - let deadline = timeout.map { Date().addingTimeInterval($0) } - var emitted = 0 - - store.watchBlocking( - key, - pollInterval: TimeInterval(interval) / 1000.0, - shouldContinue: { - if let count, emitted >= count { return false } - if let deadline, Date() >= deadline { return false } - return true - } - ) { value in - emitted += 1 - if jsonl { - // Parse the fresh `value` from watchBlocking. Do not re-query - // the store: FileState cache can lag cross-process disk writes. - let event = try? CLIOutput.watchEvent( - key: key, - rawValue: value, - timestamp: store.now - ) - if let event, let line = try? CLIOutput.encodeLine(event) { - CLIOutput.writeLine(line) + do { + try store.ensureReadable(key) + let deadline = timeout.map { Date().addingTimeInterval($0) } + var emitted = 0 + + store.watchBlocking( + key, + pollInterval: TimeInterval(interval) / 1000.0, + shouldContinue: { + if let count, emitted >= count { return false } + if let deadline, Date() >= deadline { return false } + return true + } + ) { value in + emitted += 1 + if jsonl { + // Parse the fresh `value` from watchBlocking. Do not re-query + // the store: FileState cache can lag cross-process disk writes. + let event = try? CLIOutput.watchEvent( + key: key, + rawValue: value, + timestamp: store.now + ) + if let event, let line = try? CLIOutput.encodeLine(event) { + CLIOutput.writeLine(line) + } else { + CLIOutput.writeLine(value) + } } else { CLIOutput.writeLine(value) } - } else { - CLIOutput.writeLine(value) } + } catch let error as APSError { + try CLIOutput.fail(error, json: jsonl) } } } @@ -178,7 +188,11 @@ extension Aps { boot(stateDir: options.stateDir) // dump is always JSON; --json is accepted for agent symmetry. _ = options.json - print(try StateStore().dump()) + do { + print(try StateStore().dump()) + } catch let error as APSError { + try CLIOutput.fail(error, json: true) + } } } } @@ -299,26 +313,30 @@ extension Aps { try onMainThread { boot(stateDir: options.stateDir) let store = StateStore() - if all { - store.resetAll() - if options.json { - let payload = CLIOutput.ResetPayload(reset: "all", key: nil, value: nil) - print(try CLIOutput.encodePretty(payload)) - } else { - print("reset all keys") - } - } else if let key { - store.reset(key) - if options.json { - let payload = CLIOutput.ResetPayload( - reset: "key", - key: key.rawValue, - value: try CLIOutput.typedValue(for: key, store: store) - ) - print(try CLIOutput.encodePretty(payload)) - } else { - print(store.get(key)) + do { + if all { + store.resetAll() + if options.json { + let payload = CLIOutput.ResetPayload(reset: "all", key: nil, value: nil) + print(try CLIOutput.encodePretty(payload)) + } else { + print("reset all keys") + } + } else if let key { + store.reset(key) + if options.json { + let payload = CLIOutput.ResetPayload( + reset: "key", + key: key.rawValue, + value: try CLIOutput.typedValue(for: key, store: store) + ) + print(try CLIOutput.encodePretty(payload)) + } else { + print(store.get(key)) + } } + } catch let error as APSError { + try CLIOutput.fail(error, json: options.json) } } } diff --git a/Sources/aps/CLIOutput.swift b/Sources/aps/CLIOutput.swift index dc7b506..2145e12 100644 --- a/Sources/aps/CLIOutput.swift +++ b/Sources/aps/CLIOutput.swift @@ -82,7 +82,7 @@ enum CLIOutput { static func encodeLine(_ value: T) throws -> String { let encoder = JSONEncoder() - encoder.outputFormatting = [.sortedKeys] + encoder.outputFormatting = [.sortedKeys, .withoutEscapingSlashes] encoder.dateEncodingStrategy = .iso8601 let data = try encoder.encode(value) guard let string = String(data: data, encoding: .utf8) else { @@ -97,6 +97,51 @@ enum CLIOutput { } } + // MARK: - Error contract + + /// Structured error envelope emitted on stderr in machine modes. + /// Stable shape: codes are snake_case APSError cases, never removed. + struct ErrorEnvelope: Encodable { + struct Body: Encodable { + let code: String + let message: String + let hint: String + } + let error: Body + } + + /// True when structured errors should accompany the human line: machine + /// mode was requested, or APS_ERROR_JSON=1 opts in unconditionally. + static func structuredErrorsEnabled(json: Bool) -> Bool { + json || ProcessInfo.processInfo.environment["APS_ERROR_JSON"] == "1" + } + + static func writeError(_ line: String) { + if let data = (line + "\n").data(using: .utf8) { + FileHandle.standardError.write(data) + } + } + + /// Single failure path for domain errors: human line on stderr, optional + /// JSON envelope, then the taxonomy exit code. Usage/shape errors from + /// ArgumentParser itself still exit 64 on their own. + static func fail(_ error: APSError, json: Bool) throws -> Never { + writeError("Error: \(error.description)") + if structuredErrorsEnabled(json: json), + let envelope = try? encodeLine( + ErrorEnvelope( + error: .init( + code: error.code, + message: error.description, + hint: error.hint + ) + ) + ) { + writeError(envelope) + } + throw ExitCode(error.exitCode) + } + @MainActor static func typedValue(for key: DemoKey, store: StateStore) throws -> JSONValue { try typedValue(for: key, from: store.get(key)) diff --git a/Sources/aps/DemoKey.swift b/Sources/aps/DemoKey.swift index e7c1cf1..eed6b05 100644 --- a/Sources/aps/DemoKey.swift +++ b/Sources/aps/DemoKey.swift @@ -105,4 +105,43 @@ public enum APSError: Error, CustomStringConvertible, Equatable { return "SecureState (Keychain) is unavailable on this platform. The secret key requires macOS Keychain." } } + + /// Stable machine code for the JSON error envelope. Never removed or renamed. + public var code: String { + switch self { + case .invalidValue: return "invalid_value" + case .encodingFailed: return "encoding_failed" + case .decodingFailed: return "decoding_failed" + case .persistenceFailed: return "persistence_failed" + case .keychainUnavailable: return "keychain_unavailable" + } + } + + /// sysexits-aligned exit code. 64 means the caller can fix the invocation; + /// 65+ means environment or data, 70 means an aps bug. + public var exitCode: Int32 { + switch self { + case .invalidValue: return 64 // EX_USAGE + case .decodingFailed: return 65 // EX_DATAERR + case .keychainUnavailable: return 69 // EX_UNAVAILABLE + case .encodingFailed: return 70 // EX_SOFTWARE + case .persistenceFailed: return 73 // EX_CANTCREAT + } + } + + /// Actionable next step for humans and agents. + public var hint: String { + switch self { + case .invalidValue: + return "Run `aps keys` to see expected types per key." + case .encodingFailed: + return "The value could not be JSON-encoded; please report this if it reproduces." + case .decodingFailed: + return "A state file exists but is not valid for its key; inspect or remove it under the state root (--state-dir / APS_HOME)." + case .persistenceFailed: + return "Check that the state root exists and is writable (--state-dir / APS_HOME)." + case .keychainUnavailable: + return "The secret key currently requires macOS Keychain; an encrypted-file store is planned (issue #35)." + } + } } diff --git a/Sources/aps/StateStore.swift b/Sources/aps/StateStore.swift index 995b0d0..9338ac4 100644 --- a/Sources/aps/StateStore.swift +++ b/Sources/aps/StateStore.swift @@ -104,7 +104,12 @@ public final class StateStore { state.value = value // AppState FileState swallows save errors after updating its cache. // Confirm the value is actually on disk before claiming success. - let onDisk = try Self.readNoteFromDisk() + let onDisk: String + do { + onDisk = try Self.readNoteFromDisk() + } catch { + throw APSError.persistenceFailed(key: .note) + } guard onDisk == value else { throw APSError.persistenceFailed(key: .note) } @@ -120,7 +125,12 @@ public final class StateStore { } var state = Application.fileState(\.profile) state.value = document - let onDisk = try Self.readProfileFromDisk() + let onDisk: ProfileDocument + do { + onDisk = try Self.readProfileFromDisk() + } catch { + throw APSError.persistenceFailed(key: .profile) + } guard onDisk == document else { throw APSError.persistenceFailed(key: .profile) } @@ -142,7 +152,12 @@ public final class StateStore { let expectedVersion = (try? Self.readProfileFromDisk())?.version ?? 0 var slice = Application.slice(\.profile, \.name) slice.value = value - let onDisk = try Self.readProfileFromDisk() + let onDisk: ProfileDocument + do { + onDisk = try Self.readProfileFromDisk() + } catch { + throw APSError.persistenceFailed(key: .profileName) + } guard onDisk.name == value, onDisk.version == expectedVersion else { throw APSError.persistenceFailed(key: .profileName) } @@ -300,24 +315,61 @@ public final class StateStore { /// Read `note.json` without touching AppState's in-memory FileState cache. /// /// Mirrors AppState's non-Base64 FileState encoding: UTF-8 JSON via `JSONEncoder`. + /// Missing/unreadable file throws `persistenceFailed`; an existing file that + /// does not decode throws `decodingFailed` (data corruption, exit 65). public static func readNoteFromDisk() throws -> String { let fileURL = Self.fileStateURL(filename: "note.json") + let data: Data do { - let data = try Data(contentsOf: fileURL) - return try JSONDecoder().decode(String.self, from: data) + data = try Data(contentsOf: fileURL) } catch { throw APSError.persistenceFailed(key: .note) } + do { + return try JSONDecoder().decode(String.self, from: data) + } catch { + throw APSError.decodingFailed + } } + /// Read `profile.json` without touching AppState's in-memory FileState cache. + /// Missing/unreadable throws `persistenceFailed`; existing-but-undecodable + /// throws `decodingFailed`. public static func readProfileFromDisk() throws -> ProfileDocument { let fileURL = Self.fileStateURL(filename: "profile.json") + let data: Data do { - let data = try Data(contentsOf: fileURL) - return try JSONDecoder().decode(ProfileDocument.self, from: data) + data = try Data(contentsOf: fileURL) } catch { throw APSError.persistenceFailed(key: .profile) } + do { + return try JSONDecoder().decode(ProfileDocument.self, from: data) + } catch { + throw APSError.decodingFailed + } + } + + /// Loud corrupt-state check: a disk-backed file that exists but does not + /// decode is a data error (`decodingFailed`, exit 65), never silently the + /// initial value. Missing files are fine: they mean the initial value. + public func ensureReadable(_ key: DemoKey) throws { + let fileURL: URL + switch key { + case .note: + fileURL = Self.fileStateURL(filename: "note.json") + case .profile, .profileName: + fileURL = Self.fileStateURL(filename: "profile.json") + case .counter, .message, .flag, .secret: + return + } + guard FileManager.default.fileExists(atPath: fileURL.path) else { return } + switch key { + case .note: + _ = try Self.readNoteFromDisk() + default: + _ = try Self.readProfileFromDisk() + } } /// Loads `profile.json` into AppState's FileState cache when present. diff --git a/Tests/apsTests/APSTests.swift b/Tests/apsTests/APSTests.swift index 25fc6e8..d942d03 100644 --- a/Tests/apsTests/APSTests.swift +++ b/Tests/apsTests/APSTests.swift @@ -601,4 +601,80 @@ final class APSTests: XCTestCase { XCTAssertTrue(persistence.description.contains("note")) XCTAssertTrue(persistence.description.contains("persist")) } + + func testAPSErrorContractCodesAndExitCodes() { + XCTAssertEqual(APSError.invalidValue(key: .counter, value: "x").code, "invalid_value") + XCTAssertEqual(APSError.encodingFailed.code, "encoding_failed") + XCTAssertEqual(APSError.decodingFailed.code, "decoding_failed") + XCTAssertEqual(APSError.persistenceFailed(key: .note).code, "persistence_failed") + XCTAssertEqual(APSError.keychainUnavailable.code, "keychain_unavailable") + + XCTAssertEqual(APSError.invalidValue(key: .counter, value: "x").exitCode, 64) + XCTAssertEqual(APSError.decodingFailed.exitCode, 65) + XCTAssertEqual(APSError.keychainUnavailable.exitCode, 69) + XCTAssertEqual(APSError.encodingFailed.exitCode, 70) + XCTAssertEqual(APSError.persistenceFailed(key: .note).exitCode, 73) + + for error in [APSError.invalidValue(key: .flag, value: "x"), .encodingFailed, .decodingFailed, .persistenceFailed(key: .flag), .keychainUnavailable] as [APSError] { + XCTAssertFalse(error.hint.isEmpty, "hint required for \(error.code)") + } + } + + func testErrorEnvelopeEncodesStableShape() throws { + let envelope = CLIOutput.ErrorEnvelope( + error: .init( + code: APSError.decodingFailed.code, + message: APSError.decodingFailed.description, + hint: APSError.decodingFailed.hint + ) + ) + let line = try CLIOutput.encodeLine(envelope) + XCTAssertTrue(line.contains(#""code":"decoding_failed""#)) + XCTAssertTrue(line.contains(#""message":"#)) + XCTAssertTrue(line.contains(#""hint":"#)) + XCTAssertTrue(line.hasPrefix(#"{"error":{"#)) + } + + func testStructuredErrorsEnabledModes() { + XCTAssertTrue(CLIOutput.structuredErrorsEnabled(json: true)) + let env = ProcessInfo.processInfo.environment["APS_ERROR_JSON"] + XCTAssertEqual(CLIOutput.structuredErrorsEnabled(json: false), env == "1") + } + + @MainActor + func testEnsureReadableMissingFileIsInitialSemantics() throws { + let store = StateStore() + // Fresh temp dir per setUp: no note.json yet, so no error. + try store.ensureReadable(.note) + try store.ensureReadable(.profile) + try store.ensureReadable(.counter) + } + + @MainActor + func testEnsureReadableCorruptFileThrowsDecodingFailed() throws { + let store = StateStore() + try store.set(.note, value: "ok") + + let path = FileManager.defaultFileStatePath + let url = URL(fileURLWithPath: path).appendingPathComponent("note.json") + try "garbage{{".write(to: url, atomically: false, encoding: .utf8) + + XCTAssertThrowsError(try store.ensureReadable(.note)) { error in + XCTAssertEqual(error as? APSError, .decodingFailed) + } + } + + @MainActor + func testEnsureReadableCorruptProfileThrowsDecodingFailed() throws { + let store = StateStore() + try store.set(.profile, value: #"{"name":"a","version":1}"#) + + let path = FileManager.defaultFileStatePath + let url = URL(fileURLWithPath: path).appendingPathComponent("profile.json") + try "not json".write(to: url, atomically: false, encoding: .utf8) + + XCTAssertThrowsError(try store.ensureReadable(.profile)) { error in + XCTAssertEqual(error as? APSError, .decodingFailed) + } + } } diff --git a/specs/aps-cli/aps-cli.spec.md b/specs/aps-cli/aps-cli.spec.md index 831c6a5..5de6dcb 100644 --- a/specs/aps-cli/aps-cli.spec.md +++ b/specs/aps-cli/aps-cli.spec.md @@ -1,6 +1,6 @@ --- module: aps-cli -version: 16 +version: 18 status: active files: - Sources/aps/Aps.swift @@ -49,14 +49,18 @@ agents can get, set, watch, dump, list, and reset typed application state. | `valueType` | Human value type (`Int` / `String` / `Bool` / `ProfileDocument`). | | `helpSummary` | Tab-separated key/type/storage columns for `keys`. | | `detail` | One-line description for `keys`. | -| `description` | Actionable error text for humans and ValidationError bridging. | +| `description` | Actionable error text for humans. | +| `code` | Stable machine error code for the JSON envelope. | +| `exitCode` | sysexits-aligned process exit code. | +| `hint` | Actionable next step in the error envelope. | ## Invariants 1. The CLI entry point runs on the real main thread so AppState `notifyChange()` assertions hold on Linux and macOS. 2. stdout for `get` / `set` / `watch` / `reset ` is only the value line(s); - help and errors use ArgumentParser defaults. + stdout stays empty on error; help uses ArgumentParser defaults and domain + errors use the Error Cases contract (human line plus optional JSON envelope). 3. `State` keys are process-local; a new process must not be expected to retain `counter` or `message`. 4. `watch` must flush each printed value immediately when stdout is not a TTY. @@ -90,12 +94,29 @@ Then the watcher prints `changed` within one poll interval. ## Error Cases -- Unknown `DemoKey` token: ArgumentParser rejects before `run()`. -- Non-integer `counter` value: `APSError.invalidValue` -> ValidationError. -- Non-boolean `flag` value: `APSError.invalidValue` -> ValidationError. -- `reset` with neither a key nor `--all`: ValidationError. -- `reset` with both a key and `--all`: ValidationError. -- Failed `note` disk persistence: `APSError.persistenceFailed` -> ValidationError. +Domain errors fail through a single contract: a human line on stderr, an +optional JSON envelope, and a taxonomy exit code. + +Exit codes (sysexits-aligned): + +| Code | Meaning | aps mapping | +|------|---------|-------------| +| 0 | success | stdout contract satisfied | +| 64 | EX_USAGE | caller-fixable: bad key/flags, `invalidValue`, reset arg conflicts | +| 65 | EX_DATAERR | corrupt persisted state: existing file does not decode (`decodingFailed`) | +| 69 | EX_UNAVAILABLE | `keychainUnavailable` on platforms without Apple Security | +| 70 | EX_SOFTWARE | internal bug: `encodingFailed` | +| 73 | EX_CANTCREAT | write did not persist: `persistenceFailed` | +| 66 | EX_NOINPUT | reserved for future explicit-file operations | + +- Missing state files are not errors: they mean the initial value. +- A disk-backed file that exists but does not decode fails loudly (65) via + `StateStore.ensureReadable` before `get` / `watch` output. +- With `--json` / `--jsonl`, or when `APS_ERROR_JSON=1`, stderr additionally + gets one `{"error":{"code","message","hint"}}` envelope; stdout stays empty + on error in every mode. +- Unknown `DemoKey` token and flag shape errors: ArgumentParser rejects + before `run()` with its own 64. ## Dependencies @@ -122,3 +143,5 @@ Then the watcher prints `changed` within one poll interval. | 2026-07-18 | CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17: Dogfood AppState Slice via profileName for issue 17 | | 2026-07-18 | CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15: Remove unreachable APSError.unknownKey | | 2026-07-18 | CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15: Remove unreachable APSError.unknownKey and JSONCoding.decode for issue 15 | +| 2026-07-18 | CHG-0016-error-contract-exit-taxonomy-and-json-envelope-for-issue-31: Error contract: exit-code taxonomy and JSON error envelope for issue 31 | +| 2026-07-18 | CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31: Error contract: exit-code taxonomy and JSON error envelope (issue 31) | diff --git a/specs/aps-cli/requirements.md b/specs/aps-cli/requirements.md index b3d6fb2..2d025f6 100644 --- a/specs/aps-cli/requirements.md +++ b/specs/aps-cli/requirements.md @@ -17,12 +17,15 @@ Acceptance Criteria ### REQ-aps-cli-002 -`set` SHALL reject values that cannot parse to the key's type and exit non-zero via `APSError.invalidValue`. +`set` SHALL reject values that cannot parse to the key's type and exit non-zero via `APSError.invalidValue`. All domain errors SHALL follow the error contract: human line on stderr, taxonomy exit code, and a JSON envelope when `--json` / `--jsonl` or `APS_ERROR_JSON=1`. Acceptance Criteria - Non-integer `counter` values fail with an invalid-value message. - Non-boolean `flag` values fail with an invalid-value message. - `APSError.description` names the key and expected type. +- Exit codes: 64 usage, 65 corrupt persisted state, 69 unavailable, 70 internal, 73 write did not persist. +- The envelope shape is `{"error":{"code","message","hint"}}` with stable snake_case codes; stdout stays empty on error. +- Corrupt `note.json` / `profile.json` exits 65 instead of returning the initial value. ### REQ-aps-cli-003 diff --git a/specs/state-store/requirements.md b/specs/state-store/requirements.md index 4b6a17b..de1409a 100644 --- a/specs/state-store/requirements.md +++ b/specs/state-store/requirements.md @@ -88,3 +88,12 @@ Acceptance Criteria - After set(.profileName, "x"), profileDocument().name is "x" and profile.json reflects it. - get(.profileName) matches the parent name field. + +### REQ-state-store-011 + +`ensureReadable` SHALL distinguish missing from corrupt persisted state: missing files keep initial-value semantics, and existing-but-undecodable `note.json` / `profile.json` SHALL throw `APSError.decodingFailed` before `get` / `watch` output. + +Acceptance Criteria +- Missing state file: no error, initial value. +- Corrupt state file: `APSError.decodingFailed` (exit 65 per the CLI contract). +- `readNoteFromDisk` / `readProfileFromDisk` throw `persistenceFailed` when missing/unreadable and `decodingFailed` when corrupt. diff --git a/specs/state-store/state-store.spec.md b/specs/state-store/state-store.spec.md index a7ed421..2d7fa1d 100644 --- a/specs/state-store/state-store.spec.md +++ b/specs/state-store/state-store.spec.md @@ -1,6 +1,6 @@ --- module: state-store -version: 16 +version: 18 status: active files: - Sources/aps/StateStore.swift @@ -36,8 +36,9 @@ non-UI use. | `resetStats` | Clears process-local DemoStats counters. | | `profileDocument` | Typed profile FileState accessor. | | `profileName` | Slice accessor for ProfileDocument.name. | -| `readNoteFromDisk` | Direct `note.json` read bypassing cache. | -| `readProfileFromDisk` | Direct `profile.json` read bypassing cache. | +| `readNoteFromDisk` | Direct `note.json` read bypassing cache; corrupt file throws `decodingFailed`. | +| `readProfileFromDisk` | Direct `profile.json` read bypassing cache; corrupt file throws `decodingFailed`. | +| `ensureReadable` | Loud corrupt-state check before `get` / `watch` output. | | `parseBool` | Bool token parser for flag values. | | `APSClock` | Injected clock dependency protocol. | | `now` | APSClock current instant. | @@ -92,6 +93,11 @@ Then keys include message with value "hi" and a timestamp field exists. - `set(.flag, value: "maybe")` throws `APSError.invalidValue`. - JSONCoding encode failures surface as `APSError.encodingFailed` when UTF-8 conversion fails. Profile JSON parse failures surface as `APSError.invalidValue`. +- `readNoteFromDisk` / `readProfileFromDisk`: missing or unreadable file throws + `APSError.persistenceFailed`; an existing file that does not decode throws + `APSError.decodingFailed` (exit 65 per the CLI error contract). +- `ensureReadable` ignores missing files (initial-value semantics) and throws + `APSError.decodingFailed` for corrupt `note.json` / `profile.json`. ## Dependencies @@ -118,3 +124,5 @@ Then keys include message with value "hi" and a timestamp field exists. | 2026-07-18 | CHG-0013-dogfood-appstate-slice-via-profilename-for-issue-17: Dogfood AppState Slice via profileName for issue 17 | | 2026-07-18 | CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15: Remove JSONCoding.decode | | 2026-07-18 | CHG-0015-remove-unreachable-apserror-unknownkey-and-jsoncoding-decode-for-issue-15: Remove unreachable APSError.unknownKey and JSONCoding.decode for issue 15 | +| 2026-07-18 | CHG-0016-error-contract-exit-taxonomy-and-json-envelope-for-issue-31: ensureReadable and corrupt-state decodingFailed semantics for issue 31 | +| 2026-07-18 | CHG-0016-error-contract-exit-code-taxonomy-and-json-error-envelope-issue-31: Error contract: exit-code taxonomy and JSON error envelope (issue 31) |