diff --git a/.specsync/change-sequence.json b/.specsync/change-sequence.json index e02bcdb..854b1e0 100644 --- a/.specsync/change-sequence.json +++ b/.specsync/change-sequence.json @@ -1,6 +1,6 @@ { "schema_version": 1, - "sequence": 11, - "id": "CHG-0011-dogfood-observeddependency-demostats-for-issue-18", + "sequence": 12, + "id": "CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16", "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 fdb1b4e..ff31800 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 @@ -34,6 +34,13 @@ "timestamp": 1784406159, "digest": "f671751498caaf34b8ace57e36f2a29afee15995b6f818e3d8aa1c565e7e3928", "note": "Re-accept after README stats docs." + }, + { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784406725, + "digest": "ad8c07e762798385f5b9c3cc7adfc4d767b6db046b24603e45239c02e8c7eebf", + "note": "Re-accept after SecureState secret dogfood." } ], "reopenings": [ @@ -274,6 +281,125 @@ }, "stale_acceptance_input_digest": "80328d5ff7e9ef1c9eb3031447397375a429ba5b8fc3148ab6ef3a2d21f5fc68", "current_acceptance_input_digest": "7b57d808c5b347b143805b067546eb72d5e5763b2aa447a34f43939f8179f7eb" + }, + { + "schema_version": 1, + "change_id": "CHG-0003-archive-merged-v1-specsync-change-and-publish-aps-v1-1-goal", + "actor": "agent:cursor", + "reason": "README.md and GOAL.md updated for SecureState secret (issue 16).", + "timestamp": 1784406701, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784406159, + "digest": "f671751498caaf34b8ace57e36f2a29afee15995b6f818e3d8aa1c565e7e3928", + "note": "Re-accept after README stats docs." + }, + "prior_verification": { + "timestamp": 1784406149, + "commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", + "contract_digest": "641111b8f92bdfff2ba1f3bcda40f5299d9dc8a65449cd2a4a49333fac21100f", + "workspace_digest": "cba16ef6fcb3382751c8c739ab790c2680228fbf82f4326eca52dbe3a12650b6", + "acceptance_input_digest": "7b57d808c5b347b143805b067546eb72d5e5763b2aa447a34f43939f8179f7eb", + "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": "8e6aaa8b615b6e0b9653d798928677e472dbd3b905243bc7fc44a5d7ec8bd888", + "entry_digest": "6306e8c2a11cad3c2485ddeddfbbfc4bbc0852a7aa217676deeaebaeb411ba08", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "README.md", + "kind": "file", + "mode": 33188, + "payload_digest": "7bf716236429a7ddb95fc6857b0221add844069df5dd3979d81f88b10ba889ce", + "entry_digest": "488fe3c608cf0cd86e9842b9560718564786ec8b97afd1274bd833a7887635b3", + "owners": [ + "@exact:delivery" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] + }, + "stale_acceptance_input_digest": "7b57d808c5b347b143805b067546eb72d5e5763b2aa447a34f43939f8179f7eb", + "current_acceptance_input_digest": "e119cc714f300179a485b64b37c775e8f46269f2da671f12d37a63df996fcd62" } ] } 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 eb4153d..94371c5 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": 1784406159, + "updated_at": 1784406725, "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 9879a1e..58e6ad8 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 @@ -45,6 +45,21 @@ } ], "requirement_ids": [] + }, + { + "timestamp": 1784406716, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", + "contract_digest": "641111b8f92bdfff2ba1f3bcda40f5299d9dc8a65449cd2a4a49333fac21100f", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "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 fabf247..a7d4a81 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": 1784406149, - "commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", + "timestamp": 1784406716, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", "contract_digest": "641111b8f92bdfff2ba1f3bcda40f5299d9dc8a65449cd2a4a49333fac21100f", - "workspace_digest": "cba16ef6fcb3382751c8c739ab790c2680228fbf82f4326eca52dbe3a12650b6", - "acceptance_input_digest": "7b57d808c5b347b143805b067546eb72d5e5763b2aa447a34f43939f8179f7eb", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "acceptance_input_digest": "e119cc714f300179a485b64b37c775e8f46269f2da671f12d37a63df996fcd62", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -71,8 +71,8 @@ "path": "GOAL.md", "kind": "file", "mode": 33188, - "payload_digest": "8e6aaa8b615b6e0b9653d798928677e472dbd3b905243bc7fc44a5d7ec8bd888", - "entry_digest": "6306e8c2a11cad3c2485ddeddfbbfc4bbc0852a7aa217676deeaebaeb411ba08", + "payload_digest": "c34a620e0b2475f28eaf2d46d4cb204d8422614e3d5f60cca21d03524e2cbfe5", + "entry_digest": "34005569bdf3050c14e75dd3c6cddc40128e606286ccb2880dd6526b9a682b48", "owners": [ "@exact:delivery" ] @@ -81,8 +81,8 @@ "path": "README.md", "kind": "file", "mode": 33188, - "payload_digest": "7bf716236429a7ddb95fc6857b0221add844069df5dd3979d81f88b10ba889ce", - "entry_digest": "488fe3c608cf0cd86e9842b9560718564786ec8b97afd1274bd833a7887635b3", + "payload_digest": "5e749cdac635ab7ac710cd578476f85e6382322976f9aa5eb7355e719df44c7d", + "entry_digest": "4ed2987acbd813fa66e6f9939a67f76d82edb10025fb0e5b07357077ab9455b0", "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 6dcf6d8..c5585a9 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 @@ -76,6 +76,13 @@ "timestamp": 1784406500, "digest": "5a32a0eb202cdbb46fd333c632e34e072d441ae943607fb10eabde46ff0c52e9", "note": "Re-accept after ObservedDependency spec updates." + }, + { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784406758, + "digest": "040dc50f3d7f2dfa7138a911e30f147e1647eb69d0812b8d6e08d1706d8975b9", + "note": "Re-accept after SecureState secret dogfood." } ], "reopenings": [ @@ -1668,6 +1675,324 @@ }, "stale_acceptance_input_digest": "6cc1947864ee6f7ec6991cfdaf1b66dcbc33c0ba459cdb1e5c1ba09bbc57bd30", "current_acceptance_input_digest": "ccba1fa7a97b22ebc58df6dcdf3b6953b0513ba0b8fa2f015064499921a60483" + }, + { + "schema_version": 1, + "change_id": "CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate", + "actor": "agent:cursor", + "reason": "Scripts/smoke.sh and specs updated for SecureState secret (issue 16).", + "timestamp": 1784406732, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784406500, + "digest": "5a32a0eb202cdbb46fd333c632e34e072d441ae943607fb10eabde46ff0c52e9", + "note": "Re-accept after ObservedDependency spec updates." + }, + "prior_verification": { + "timestamp": 1784406492, + "commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", + "contract_digest": "73c000607b3050c54981b4c29532280dd08bf00b7a15c8ec1801e045fa5496c9", + "workspace_digest": "bb3599290bcbfb3a8ade83b86609a8db1bb591851af425f55141dde65dc69d07", + "acceptance_input_digest": "ccba1fa7a97b22ebc58df6dcdf3b6953b0513ba0b8fa2f015064499921a60483", + "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": "763653c94681d4069bfb95b24294c7b54341aeacff005c36ef882695e18a78be", + "entry_digest": "0b60a483651ce270afe8e2ca3d0647d855ccd265f1aaa72fed5c8b17c8dcedce", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps/Aps.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "333a996e3a51c6d78c4a9502ae09d0470f6eca197b181eeb48f818540000b416", + "entry_digest": "f3c7e0f74a2a8623a691cfe99bda648af22bc956ae8a7fac28f3ff2206cebfb6", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/CLIOutput.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "fe14a33b5e6805d5ad2e865e693697a01dca1cc5a0971212ce6e158812b4d768", + "entry_digest": "f3b3dcb1785afd5622937de748dfc2e9955c5be81aa0fca5a633f94ba8bd8538", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoKey.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "aec3b0c43de6f034cfd1c2da09f03888ca3901dc537d5b129119170eada0ccd7", + "entry_digest": "6c15798408d9a15b994c22971dc86c85cac49b8608b26ffbbd5f9cad187ebbc3", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoState.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "c85826d89ad5a03c3b8a29bb7e7402a0fe12840f931bb147d64047f4a7496a8b", + "entry_digest": "815d2e1d5fddf50743ee7c5397de8ee421e0a0c0df92b653ed371276e17dff3b", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/Dependencies.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "c03c26c27083514c297c231842295d687eb4bcaa3f6f3b0227d38fa2d59ceb81", + "entry_digest": "f6a43d2af1f63877afd6f98003f5b3984c6aa5516f21cc04bb23751861e93b5d", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/StateStore.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "3295b11292d07101f689ef687933f9160f25393cc512abe81f6f21467c17edd0", + "entry_digest": "235772b7a55a804fa06f3708a0943887c59441a5faefb77105ca79532ecc5011", + "owners": [ + "state-store" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "a008f745a8237c3eeef89ce3edb4a55aa172d5c72c63ad5ba4b986d5d64f221c", + "entry_digest": "da6675b6951ca344adae1c062ca2db175f201d8ce7f3da188621a3b954f81361", + "owners": [ + "@exact:test" + ] + }, + { + "path": "specs/aps-cli/aps-cli.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "96b3788bdb5af7b116cc515a6da20f1d6674263e96120bda2ae6de2af20bb7f8", + "entry_digest": "71b355c5e6c6824c175b2a85a0bcdc1cad5fe12fa0c6f991059f81a0c7b41827", + "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": "ad23b4c7b6e891935a454a9d86b49c8392dce944f43567aa579f339aa49aa832", + "entry_digest": "20dc27fe60d3207d3817520d1e16ed9feb38354e05efa69927767be31174db20", + "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": "5752de35353b9e7c3091baf7e77f8e0cf41f41fac0b4c40bcba91aa5e1f5f346", + "entry_digest": "a6aa2eff60d7834cb47eddc56ea42fbcd7f58f15a78724ee0cbe93b2c2f63202", + "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": "1ab5ae114c27f4a93e595cf363aaf0862ec0ad33c5c29bacb6b83d7d3e829928", + "entry_digest": "7e148902903eab8228400a7c7e5fb1aecb9e72aa86e5530fa9214e2a79e10ad1", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/state-store.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "cd194cda0c57a77759ca002d6de13692b222c707e7bc1bc1e5018cb135fd6962", + "entry_digest": "7ef08382f34d49122a7d3ca5a3e5ca5c3bfbbfeb0a3b95d483b6a981d964d905", + "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": "644b2199865d8da3f8ac5fa362f269d8ef82d38b9576eba05c9a7babba6c6eab", + "entry_digest": "f6bbb2deec989ec26b1fdfed599ec571b76c2c6c582bfb215e37485d5a04671c", + "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": "ccba1fa7a97b22ebc58df6dcdf3b6953b0513ba0b8fa2f015064499921a60483", + "current_acceptance_input_digest": "3c0d538569336b55213f636157d828942d95b94b94cd9c9c152713fb9fdc9bb6" } ] } 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 bbaa454..8a8b9cf 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": 1784406500, + "updated_at": 1784406758, "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 5090e6b..3c16cab 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 @@ -216,6 +216,30 @@ "REQ-state-store-010", "REQ-state-store-011" ] + }, + { + "timestamp": 1784406750, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", + "contract_digest": "73c000607b3050c54981b4c29532280dd08bf00b7a15c8ec1801e045fa5496c9", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "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 ab5de7f..729c94a 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": 1784406492, - "commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", + "timestamp": 1784406750, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", "contract_digest": "73c000607b3050c54981b4c29532280dd08bf00b7a15c8ec1801e045fa5496c9", - "workspace_digest": "bb3599290bcbfb3a8ade83b86609a8db1bb591851af425f55141dde65dc69d07", - "acceptance_input_digest": "ccba1fa7a97b22ebc58df6dcdf3b6953b0513ba0b8fa2f015064499921a60483", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "acceptance_input_digest": "3c0d538569336b55213f636157d828942d95b94b94cd9c9c152713fb9fdc9bb6", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -101,8 +101,8 @@ "path": "Scripts/smoke.sh", "kind": "file", "mode": 33261, - "payload_digest": "763653c94681d4069bfb95b24294c7b54341aeacff005c36ef882695e18a78be", - "entry_digest": "0b60a483651ce270afe8e2ca3d0647d855ccd265f1aaa72fed5c8b17c8dcedce", + "payload_digest": "a47cc5a36369f2cbd7bc0cf96d7c1666c84456be7c7e83e175e2848af614eb06", + "entry_digest": "ee2bdbc4a2a76a66f37a05b7103aec19ea4a42acf9da67b587818dd9998088b5", "owners": [ "@exact:delivery" ] @@ -111,8 +111,8 @@ "path": "Sources/aps/Aps.swift", "kind": "file", "mode": 33188, - "payload_digest": "333a996e3a51c6d78c4a9502ae09d0470f6eca197b181eeb48f818540000b416", - "entry_digest": "f3c7e0f74a2a8623a691cfe99bda648af22bc956ae8a7fac28f3ff2206cebfb6", + "payload_digest": "d54c7ceac335d50437a704582e8c4165114129edaf818c66819f09073569f00d", + "entry_digest": "a5c521279ca4bdbf40f235c227e67a42ab44403d9d0f0ce083719cd5cc9a6fbd", "owners": [ "aps-cli" ] @@ -121,8 +121,8 @@ "path": "Sources/aps/CLIOutput.swift", "kind": "file", "mode": 33188, - "payload_digest": "fe14a33b5e6805d5ad2e865e693697a01dca1cc5a0971212ce6e158812b4d768", - "entry_digest": "f3b3dcb1785afd5622937de748dfc2e9955c5be81aa0fca5a633f94ba8bd8538", + "payload_digest": "21f5521bf8aea7b04e486e0dd0fb78da437f63fdd06274dd591cc83df217b3eb", + "entry_digest": "6a01213cc9e2cb1bb671068dc52ef663227831ce66834b15063e90819302ee88", "owners": [ "aps-cli" ] @@ -131,8 +131,8 @@ "path": "Sources/aps/DemoKey.swift", "kind": "file", "mode": 33188, - "payload_digest": "aec3b0c43de6f034cfd1c2da09f03888ca3901dc537d5b129119170eada0ccd7", - "entry_digest": "6c15798408d9a15b994c22971dc86c85cac49b8608b26ffbbd5f9cad187ebbc3", + "payload_digest": "a904d8aa3258eed051b77a98fb038c414bb8a46764fe32d846c9070a2391fe93", + "entry_digest": "d04d9352a3918b9ae90d69dfa341a057fd697f0033c905fffb31bf6eb6667949", "owners": [ "aps-cli" ] @@ -141,8 +141,8 @@ "path": "Sources/aps/DemoState.swift", "kind": "file", "mode": 33188, - "payload_digest": "c85826d89ad5a03c3b8a29bb7e7402a0fe12840f931bb147d64047f4a7496a8b", - "entry_digest": "815d2e1d5fddf50743ee7c5397de8ee421e0a0c0df92b653ed371276e17dff3b", + "payload_digest": "62cd24efb557b5cef77e7aae804f933f4b53caf47195968764f05cc5a0a0d9b8", + "entry_digest": "fb710f893d343d67c1fd7a5a5cc50c5c49caf0aa182e5af17ee7c97ff98f8122", "owners": [ "state-store" ] @@ -161,8 +161,8 @@ "path": "Sources/aps/StateStore.swift", "kind": "file", "mode": 33188, - "payload_digest": "3295b11292d07101f689ef687933f9160f25393cc512abe81f6f21467c17edd0", - "entry_digest": "235772b7a55a804fa06f3708a0943887c59441a5faefb77105ca79532ecc5011", + "payload_digest": "da2a9197f1099ecbdaa6b6a568abfb4c06641c5ba6ad26531dde7120507594aa", + "entry_digest": "cc554a0ab4db5a9bffac07473af65fb1cae52a8609b4446ad9e98cc963fbf4dd", "owners": [ "state-store" ] @@ -171,8 +171,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "a008f745a8237c3eeef89ce3edb4a55aa172d5c72c63ad5ba4b986d5d64f221c", - "entry_digest": "da6675b6951ca344adae1c062ca2db175f201d8ce7f3da188621a3b954f81361", + "payload_digest": "ae9357465fd4069ef0ca3ab4708d5f80fb2a678f664b2ddab82a39db515e553c", + "entry_digest": "31cd6b2bc889715a5d740b4dfb68237559799cc587ffc7fc4045805e62fb3f5d", "owners": [ "@exact:test" ] @@ -181,8 +181,8 @@ "path": "specs/aps-cli/aps-cli.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "96b3788bdb5af7b116cc515a6da20f1d6674263e96120bda2ae6de2af20bb7f8", - "entry_digest": "71b355c5e6c6824c175b2a85a0bcdc1cad5fe12fa0c6f991059f81a0c7b41827", + "payload_digest": "973bca816ee102af6ab85c50a222a9eb9a139aa7206bb8187f1aeaa2e132024b", + "entry_digest": "1367c88441c61078739b684cda4ebcb6c6be7c8e1a00d72a63f07c0b72beba5a", "owners": [ "aps-cli" ] @@ -201,8 +201,8 @@ "path": "specs/aps-cli/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "ad23b4c7b6e891935a454a9d86b49c8392dce944f43567aa579f339aa49aa832", - "entry_digest": "20dc27fe60d3207d3817520d1e16ed9feb38354e05efa69927767be31174db20", + "payload_digest": "419f0c845d29d743d9cd2a5ea4e62bcba42c84d9e13016e99dea99f959d9bfa3", + "entry_digest": "8bf291fbff34b25f6cef55de5baa2989d08b9f84c63f6650c7ade3d21716a26a", "owners": [ "aps-cli" ] @@ -221,8 +221,8 @@ "path": "specs/aps-cli/testing.md", "kind": "file", "mode": 33188, - "payload_digest": "5752de35353b9e7c3091baf7e77f8e0cf41f41fac0b4c40bcba91aa5e1f5f346", - "entry_digest": "a6aa2eff60d7834cb47eddc56ea42fbcd7f58f15a78724ee0cbe93b2c2f63202", + "payload_digest": "2fcfc9b754510a08801f14b98127ca0eda86326b7bf7caa16128d2e318ed691a", + "entry_digest": "17a075d68802d2d76c19e657aa503bf9ca985d904ec31052806ac136887798e8", "owners": [ "aps-cli" ] @@ -241,8 +241,8 @@ "path": "specs/state-store/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "1ab5ae114c27f4a93e595cf363aaf0862ec0ad33c5c29bacb6b83d7d3e829928", - "entry_digest": "7e148902903eab8228400a7c7e5fb1aecb9e72aa86e5530fa9214e2a79e10ad1", + "payload_digest": "a8c769c76d81e449b0ee8a6a8b47868e443803fdcee30ce97add5e3ac839cd8b", + "entry_digest": "ea79f9ff77a39a21e4776fab146a65895f6ed8c30191cf2ccec8a4f5ae983113", "owners": [ "state-store" ] @@ -251,8 +251,8 @@ "path": "specs/state-store/state-store.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "cd194cda0c57a77759ca002d6de13692b222c707e7bc1bc1e5018cb135fd6962", - "entry_digest": "7ef08382f34d49122a7d3ca5a3e5ca5c3bfbbfeb0a3b95d483b6a981d964d905", + "payload_digest": "8f5398060951a8b2e4167d05f7428c7daf851f2a01fae4e7bb936629424a85ad", + "entry_digest": "790137b2cba8b552c0c435c3cc9c82d1eb13b2c5e1c0bf7110c176db02a7b1ea", "owners": [ "state-store" ] @@ -271,8 +271,8 @@ "path": "specs/state-store/testing.md", "kind": "file", "mode": 33188, - "payload_digest": "644b2199865d8da3f8ac5fa362f269d8ef82d38b9576eba05c9a7babba6c6eab", - "entry_digest": "f6bbb2deec989ec26b1fdfed599ec571b76c2c6c582bfb215e37485d5a04671c", + "payload_digest": "d47e253f2849935f12af34254746516b0832c3631df041cb193aefc55f33db67", + "entry_digest": "76f63952285d96b292fc822601cb35c8a40ba97d9d43703ef046cea5b790a013", "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 6c26584..aa8eb86 100644 --- a/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/approvals.json +++ b/.specsync/changes/CHG-0007-hermetic-userdefaults-in-tests/approvals.json @@ -20,6 +20,13 @@ "timestamp": 1784406365, "digest": "11709792c043ccdb5516fc1722dcfaf7762f67e17e5f24245150d9c3fc57036d", "note": "Re-accept after ObservedDependency tests." + }, + { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784406793, + "digest": "3e4baf2845a9a075b9bcef077f5b4620e7154e3ed2da8e584025d4083b04ea86", + "note": "Re-accept after SecureState secret dogfood." } ], "reopenings": [ @@ -81,6 +88,65 @@ }, "stale_acceptance_input_digest": "2a1eb31208096ff339578eeb085f5c37cc17c2b2628ff21ddd366a7d8b01ad3c", "current_acceptance_input_digest": "37f5f78408754c1691d127f10f8041608cfcbff64a6b35de4dd87344c102a3b8" + }, + { + "schema_version": 1, + "change_id": "CHG-0007-hermetic-userdefaults-in-tests", + "actor": "agent:cursor", + "reason": "APSTests.swift extended for SecureState secret coverage (issue 16).", + "timestamp": 1784406765, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784406365, + "digest": "11709792c043ccdb5516fc1722dcfaf7762f67e17e5f24245150d9c3fc57036d", + "note": "Re-accept after ObservedDependency tests." + }, + "prior_verification": { + "timestamp": 1784406357, + "commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", + "contract_digest": "0c72a7f1bfaa780b21b7b2ac7904ab5352e8186a8293caf6f91354fddff40610", + "workspace_digest": "5a4b9f38f73e7940d9cbe17529be96a3b1bad3fe58049d470b748c41726049ec", + "acceptance_input_digest": "37f5f78408754c1691d127f10f8041608cfcbff64a6b35de4dd87344c102a3b8", + "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": "a008f745a8237c3eeef89ce3edb4a55aa172d5c72c63ad5ba4b986d5d64f221c", + "entry_digest": "da6675b6951ca344adae1c062ca2db175f201d8ce7f3da188621a3b954f81361", + "owners": [ + "@exact:test" + ] + } + ] + }, + "passed": true, + "commands": [ + { + "command": "fledge lanes run verify", + "success": true, + "exit_code": 0 + } + ], + "requirement_ids": [] + }, + "stale_acceptance_input_digest": "37f5f78408754c1691d127f10f8041608cfcbff64a6b35de4dd87344c102a3b8", + "current_acceptance_input_digest": "1f329fd97b079998a4dc8522049819bca4880cee833244b22576f2d27f748a2e" } ] } 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 74da164..dd71961 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": 1784406365, + "updated_at": 1784406793, "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 d7c5a07..dbe0455 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 @@ -30,6 +30,21 @@ } ], "requirement_ids": [] + }, + { + "timestamp": 1784406784, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", + "contract_digest": "0c72a7f1bfaa780b21b7b2ac7904ab5352e8186a8293caf6f91354fddff40610", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "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 5c12049..9276afd 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": 1784406357, - "commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", + "timestamp": 1784406784, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", "contract_digest": "0c72a7f1bfaa780b21b7b2ac7904ab5352e8186a8293caf6f91354fddff40610", - "workspace_digest": "5a4b9f38f73e7940d9cbe17529be96a3b1bad3fe58049d470b748c41726049ec", - "acceptance_input_digest": "37f5f78408754c1691d127f10f8041608cfcbff64a6b35de4dd87344c102a3b8", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "acceptance_input_digest": "1f329fd97b079998a4dc8522049819bca4880cee833244b22576f2d27f748a2e", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -21,8 +21,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "a008f745a8237c3eeef89ce3edb4a55aa172d5c72c63ad5ba4b986d5d64f221c", - "entry_digest": "da6675b6951ca344adae1c062ca2db175f201d8ce7f3da188621a3b954f81361", + "payload_digest": "ae9357465fd4069ef0ca3ab4708d5f80fb2a678f664b2ddab82a39db515e553c", + "entry_digest": "31cd6b2bc889715a5d740b4dfb68237559799cc587ffc7fc4045805e62fb3f5d", "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 9a19092..db7c6bf 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 @@ -20,7 +20,247 @@ "timestamp": 1784406380, "digest": "b992d9bcee5871ecf16c4dc76ee84f1725a10a5457db7d39a1a2d3e9888b5bfa", "note": "Accept ObservedDependency DemoStats dogfood." + }, + { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784406884, + "digest": "ca57f7310fb73b8be62dd2af0cfdeadaa338c4f116bcf1ac44cd3e3482202ae9", + "note": "Re-accept after SecureState secret dogfood." } ], - "reopenings": [] + "reopenings": [ + { + "schema_version": 1, + "change_id": "CHG-0011-dogfood-observeddependency-demostats-for-issue-18", + "actor": "agent:cursor", + "reason": "Specs/source paths shared with SecureState secret dogfood (issue 16).", + "timestamp": 1784406800, + "from_state": "accepted", + "to_state": "verifying", + "superseded_approval": { + "gate": "acceptance", + "actor": "agent:cursor", + "timestamp": 1784406380, + "digest": "b992d9bcee5871ecf16c4dc76ee84f1725a10a5457db7d39a1a2d3e9888b5bfa", + "note": "Accept ObservedDependency DemoStats dogfood." + }, + "prior_verification": { + "timestamp": 1784406374, + "commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", + "contract_digest": "1defd9f7ba999c7d4156cd0b80b236b4a173d5ba0985832e9f89b8b2ed5155ca", + "workspace_digest": "5a4b9f38f73e7940d9cbe17529be96a3b1bad3fe58049d470b748c41726049ec", + "acceptance_input_digest": "fc7dc410324e983c16a597b2d0ae15cf59dc53ace8f93f90a7aa470d94767069", + "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": "7bf716236429a7ddb95fc6857b0221add844069df5dd3979d81f88b10ba889ce", + "entry_digest": "488fe3c608cf0cd86e9842b9560718564786ec8b97afd1274bd833a7887635b3", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Scripts/smoke.sh", + "kind": "file", + "mode": 33261, + "payload_digest": "763653c94681d4069bfb95b24294c7b54341aeacff005c36ef882695e18a78be", + "entry_digest": "0b60a483651ce270afe8e2ca3d0647d855ccd265f1aaa72fed5c8b17c8dcedce", + "owners": [ + "@exact:delivery" + ] + }, + { + "path": "Sources/aps/Aps.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "333a996e3a51c6d78c4a9502ae09d0470f6eca197b181eeb48f818540000b416", + "entry_digest": "f3c7e0f74a2a8623a691cfe99bda648af22bc956ae8a7fac28f3ff2206cebfb6", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/CLIOutput.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "fe14a33b5e6805d5ad2e865e693697a01dca1cc5a0971212ce6e158812b4d768", + "entry_digest": "f3b3dcb1785afd5622937de748dfc2e9955c5be81aa0fca5a633f94ba8bd8538", + "owners": [ + "aps-cli" + ] + }, + { + "path": "Sources/aps/DemoState.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "c85826d89ad5a03c3b8a29bb7e7402a0fe12840f931bb147d64047f4a7496a8b", + "entry_digest": "815d2e1d5fddf50743ee7c5397de8ee421e0a0c0df92b653ed371276e17dff3b", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/Dependencies.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "c03c26c27083514c297c231842295d687eb4bcaa3f6f3b0227d38fa2d59ceb81", + "entry_digest": "f6a43d2af1f63877afd6f98003f5b3984c6aa5516f21cc04bb23751861e93b5d", + "owners": [ + "state-store" + ] + }, + { + "path": "Sources/aps/StateStore.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "3295b11292d07101f689ef687933f9160f25393cc512abe81f6f21467c17edd0", + "entry_digest": "235772b7a55a804fa06f3708a0943887c59441a5faefb77105ca79532ecc5011", + "owners": [ + "state-store" + ] + }, + { + "path": "Tests/apsTests/APSTests.swift", + "kind": "file", + "mode": 33188, + "payload_digest": "a008f745a8237c3eeef89ce3edb4a55aa172d5c72c63ad5ba4b986d5d64f221c", + "entry_digest": "da6675b6951ca344adae1c062ca2db175f201d8ce7f3da188621a3b954f81361", + "owners": [ + "@exact:test" + ] + }, + { + "path": "specs/aps-cli/aps-cli.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "96b3788bdb5af7b116cc515a6da20f1d6674263e96120bda2ae6de2af20bb7f8", + "entry_digest": "71b355c5e6c6824c175b2a85a0bcdc1cad5fe12fa0c6f991059f81a0c7b41827", + "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": "ad23b4c7b6e891935a454a9d86b49c8392dce944f43567aa579f339aa49aa832", + "entry_digest": "20dc27fe60d3207d3817520d1e16ed9feb38354e05efa69927767be31174db20", + "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": "5752de35353b9e7c3091baf7e77f8e0cf41f41fac0b4c40bcba91aa5e1f5f346", + "entry_digest": "a6aa2eff60d7834cb47eddc56ea42fbcd7f58f15a78724ee0cbe93b2c2f63202", + "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": "1ab5ae114c27f4a93e595cf363aaf0862ec0ad33c5c29bacb6b83d7d3e829928", + "entry_digest": "7e148902903eab8228400a7c7e5fb1aecb9e72aa86e5530fa9214e2a79e10ad1", + "owners": [ + "state-store" + ] + }, + { + "path": "specs/state-store/state-store.spec.md", + "kind": "file", + "mode": 33188, + "payload_digest": "cd194cda0c57a77759ca002d6de13692b222c707e7bc1bc1e5018cb135fd6962", + "entry_digest": "7ef08382f34d49122a7d3ca5a3e5ca5c3bfbbfeb0a3b95d483b6a981d964d905", + "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": "644b2199865d8da3f8ac5fa362f269d8ef82d38b9576eba05c9a7babba6c6eab", + "entry_digest": "f6bbb2deec989ec26b1fdfed599ec571b76c2c6c582bfb215e37485d5a04671c", + "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": "fc7dc410324e983c16a597b2d0ae15cf59dc53ace8f93f90a7aa470d94767069", + "current_acceptance_input_digest": "1c2f0aa51ceb72efd8e815fccbab4a9782562d0278b950f5658e3cedd6422bec" + } + ] } 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 0217865..28f1673 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": 1784406380, + "updated_at": 1784406884, "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 79cdb9e..f6c0a1c 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 @@ -36,6 +36,24 @@ "REQ-aps-cli-014", "REQ-state-store-012" ] + }, + { + "timestamp": 1784406876, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", + "contract_digest": "1defd9f7ba999c7d4156cd0b80b236b4a173d5ba0985832e9f89b8b2ed5155ca", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "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 e1ee94e..feb0bef 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": 1784406374, - "commit": "4daccc6061dafcbcb6a9639cf7f2087b7b1e783d", + "timestamp": 1784406876, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", "contract_digest": "1defd9f7ba999c7d4156cd0b80b236b4a173d5ba0985832e9f89b8b2ed5155ca", - "workspace_digest": "5a4b9f38f73e7940d9cbe17529be96a3b1bad3fe58049d470b748c41726049ec", - "acceptance_input_digest": "fc7dc410324e983c16a597b2d0ae15cf59dc53ace8f93f90a7aa470d94767069", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "acceptance_input_digest": "1c2f0aa51ceb72efd8e815fccbab4a9782562d0278b950f5658e3cedd6422bec", "acceptance_manifest": { "schema_version": 1, "entries": [ @@ -21,8 +21,8 @@ "path": "README.md", "kind": "file", "mode": 33188, - "payload_digest": "7bf716236429a7ddb95fc6857b0221add844069df5dd3979d81f88b10ba889ce", - "entry_digest": "488fe3c608cf0cd86e9842b9560718564786ec8b97afd1274bd833a7887635b3", + "payload_digest": "5e749cdac635ab7ac710cd578476f85e6382322976f9aa5eb7355e719df44c7d", + "entry_digest": "4ed2987acbd813fa66e6f9939a67f76d82edb10025fb0e5b07357077ab9455b0", "owners": [ "@exact:delivery" ] @@ -31,8 +31,8 @@ "path": "Scripts/smoke.sh", "kind": "file", "mode": 33261, - "payload_digest": "763653c94681d4069bfb95b24294c7b54341aeacff005c36ef882695e18a78be", - "entry_digest": "0b60a483651ce270afe8e2ca3d0647d855ccd265f1aaa72fed5c8b17c8dcedce", + "payload_digest": "a47cc5a36369f2cbd7bc0cf96d7c1666c84456be7c7e83e175e2848af614eb06", + "entry_digest": "ee2bdbc4a2a76a66f37a05b7103aec19ea4a42acf9da67b587818dd9998088b5", "owners": [ "@exact:delivery" ] @@ -41,8 +41,8 @@ "path": "Sources/aps/Aps.swift", "kind": "file", "mode": 33188, - "payload_digest": "333a996e3a51c6d78c4a9502ae09d0470f6eca197b181eeb48f818540000b416", - "entry_digest": "f3c7e0f74a2a8623a691cfe99bda648af22bc956ae8a7fac28f3ff2206cebfb6", + "payload_digest": "d54c7ceac335d50437a704582e8c4165114129edaf818c66819f09073569f00d", + "entry_digest": "a5c521279ca4bdbf40f235c227e67a42ab44403d9d0f0ce083719cd5cc9a6fbd", "owners": [ "aps-cli" ] @@ -51,8 +51,8 @@ "path": "Sources/aps/CLIOutput.swift", "kind": "file", "mode": 33188, - "payload_digest": "fe14a33b5e6805d5ad2e865e693697a01dca1cc5a0971212ce6e158812b4d768", - "entry_digest": "f3b3dcb1785afd5622937de748dfc2e9955c5be81aa0fca5a633f94ba8bd8538", + "payload_digest": "21f5521bf8aea7b04e486e0dd0fb78da437f63fdd06274dd591cc83df217b3eb", + "entry_digest": "6a01213cc9e2cb1bb671068dc52ef663227831ce66834b15063e90819302ee88", "owners": [ "aps-cli" ] @@ -61,8 +61,8 @@ "path": "Sources/aps/DemoState.swift", "kind": "file", "mode": 33188, - "payload_digest": "c85826d89ad5a03c3b8a29bb7e7402a0fe12840f931bb147d64047f4a7496a8b", - "entry_digest": "815d2e1d5fddf50743ee7c5397de8ee421e0a0c0df92b653ed371276e17dff3b", + "payload_digest": "62cd24efb557b5cef77e7aae804f933f4b53caf47195968764f05cc5a0a0d9b8", + "entry_digest": "fb710f893d343d67c1fd7a5a5cc50c5c49caf0aa182e5af17ee7c97ff98f8122", "owners": [ "state-store" ] @@ -81,8 +81,8 @@ "path": "Sources/aps/StateStore.swift", "kind": "file", "mode": 33188, - "payload_digest": "3295b11292d07101f689ef687933f9160f25393cc512abe81f6f21467c17edd0", - "entry_digest": "235772b7a55a804fa06f3708a0943887c59441a5faefb77105ca79532ecc5011", + "payload_digest": "da2a9197f1099ecbdaa6b6a568abfb4c06641c5ba6ad26531dde7120507594aa", + "entry_digest": "cc554a0ab4db5a9bffac07473af65fb1cae52a8609b4446ad9e98cc963fbf4dd", "owners": [ "state-store" ] @@ -91,8 +91,8 @@ "path": "Tests/apsTests/APSTests.swift", "kind": "file", "mode": 33188, - "payload_digest": "a008f745a8237c3eeef89ce3edb4a55aa172d5c72c63ad5ba4b986d5d64f221c", - "entry_digest": "da6675b6951ca344adae1c062ca2db175f201d8ce7f3da188621a3b954f81361", + "payload_digest": "ae9357465fd4069ef0ca3ab4708d5f80fb2a678f664b2ddab82a39db515e553c", + "entry_digest": "31cd6b2bc889715a5d740b4dfb68237559799cc587ffc7fc4045805e62fb3f5d", "owners": [ "@exact:test" ] @@ -101,8 +101,8 @@ "path": "specs/aps-cli/aps-cli.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "96b3788bdb5af7b116cc515a6da20f1d6674263e96120bda2ae6de2af20bb7f8", - "entry_digest": "71b355c5e6c6824c175b2a85a0bcdc1cad5fe12fa0c6f991059f81a0c7b41827", + "payload_digest": "973bca816ee102af6ab85c50a222a9eb9a139aa7206bb8187f1aeaa2e132024b", + "entry_digest": "1367c88441c61078739b684cda4ebcb6c6be7c8e1a00d72a63f07c0b72beba5a", "owners": [ "aps-cli" ] @@ -121,8 +121,8 @@ "path": "specs/aps-cli/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "ad23b4c7b6e891935a454a9d86b49c8392dce944f43567aa579f339aa49aa832", - "entry_digest": "20dc27fe60d3207d3817520d1e16ed9feb38354e05efa69927767be31174db20", + "payload_digest": "419f0c845d29d743d9cd2a5ea4e62bcba42c84d9e13016e99dea99f959d9bfa3", + "entry_digest": "8bf291fbff34b25f6cef55de5baa2989d08b9f84c63f6650c7ade3d21716a26a", "owners": [ "aps-cli" ] @@ -141,8 +141,8 @@ "path": "specs/aps-cli/testing.md", "kind": "file", "mode": 33188, - "payload_digest": "5752de35353b9e7c3091baf7e77f8e0cf41f41fac0b4c40bcba91aa5e1f5f346", - "entry_digest": "a6aa2eff60d7834cb47eddc56ea42fbcd7f58f15a78724ee0cbe93b2c2f63202", + "payload_digest": "2fcfc9b754510a08801f14b98127ca0eda86326b7bf7caa16128d2e318ed691a", + "entry_digest": "17a075d68802d2d76c19e657aa503bf9ca985d904ec31052806ac136887798e8", "owners": [ "aps-cli" ] @@ -161,8 +161,8 @@ "path": "specs/state-store/requirements.md", "kind": "file", "mode": 33188, - "payload_digest": "1ab5ae114c27f4a93e595cf363aaf0862ec0ad33c5c29bacb6b83d7d3e829928", - "entry_digest": "7e148902903eab8228400a7c7e5fb1aecb9e72aa86e5530fa9214e2a79e10ad1", + "payload_digest": "a8c769c76d81e449b0ee8a6a8b47868e443803fdcee30ce97add5e3ac839cd8b", + "entry_digest": "ea79f9ff77a39a21e4776fab146a65895f6ed8c30191cf2ccec8a4f5ae983113", "owners": [ "state-store" ] @@ -171,8 +171,8 @@ "path": "specs/state-store/state-store.spec.md", "kind": "file", "mode": 33188, - "payload_digest": "cd194cda0c57a77759ca002d6de13692b222c707e7bc1bc1e5018cb135fd6962", - "entry_digest": "7ef08382f34d49122a7d3ca5a3e5ca5c3bfbbfeb0a3b95d483b6a981d964d905", + "payload_digest": "8f5398060951a8b2e4167d05f7428c7daf851f2a01fae4e7bb936629424a85ad", + "entry_digest": "790137b2cba8b552c0c435c3cc9c82d1eb13b2c5e1c0bf7110c176db02a7b1ea", "owners": [ "state-store" ] @@ -191,8 +191,8 @@ "path": "specs/state-store/testing.md", "kind": "file", "mode": 33188, - "payload_digest": "644b2199865d8da3f8ac5fa362f269d8ef82d38b9576eba05c9a7babba6c6eab", - "entry_digest": "f6bbb2deec989ec26b1fdfed599ec571b76c2c6c582bfb215e37485d5a04671c", + "payload_digest": "d47e253f2849935f12af34254746516b0832c3631df041cb193aefc55f33db67", + "entry_digest": "76f63952285d96b292fc822601cb35c8a40ba97d9d43703ef046cea5b790a013", "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 new file mode 100644 index 0000000..f6b72aa --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/approvals.json @@ -0,0 +1,26 @@ +{ + "approvals": [ + { + "gate": "definition", + "actor": "agent:cursor", + "timestamp": 1784406924, + "digest": "f931b6b50deee8ecac029eb31f0a16fbdd8e24dd7c2e272dc3c183a91806d91b", + "note": "Approve SecureState secret dogfood." + }, + { + "gate": "definition", + "actor": "agent:cursor", + "timestamp": 1784407027, + "digest": "f931b6b50deee8ecac029eb31f0a16fbdd8e24dd7c2e272dc3c183a91806d91b", + "note": "Re-approve after committing SecureState implementation." + }, + { + "gate": "definition", + "actor": "agent:cursor", + "timestamp": 1784407183, + "digest": "85e6e4977d59bf2a77046990bf23888ad3476bae934473868e0b15d76cfb0576", + "note": "Re-approve with requirement evidence table." + } + ], + "reopenings": [] +} diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/change.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/change.md new file mode 100644 index 0000000..ca0713e --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/change.md @@ -0,0 +1,25 @@ +--- +id: CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16 +state: implementing +type: feature +base_commit: 6312f8d8379c3b87b05c3eb4436c0b503f161d1a +--- + +# Dogfood SecureState secret Keychain demo key for issue 16 + +## Intent + +Dogfood SecureState secret Keychain demo key for issue 16 + +## Affected Canonical Specs + +- `aps-cli` +- `state-store` + +## Acceptance Criteria + +- secret SecureState Keychain dogfood with Darwin round-trip tests, Linux keychainUnavailable, README/GOAL/smoke/specs updated. + +## No-spec Rationale + +Not applicable diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/context.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/context.md new file mode 100644 index 0000000..1f2328a --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/context.md @@ -0,0 +1,2 @@ +# Context +Issue #16: dogfood AppState SecureState via fixed demo key `secret` (Keychain). diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/deltas/aps-cli.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/deltas/aps-cli.md new file mode 100644 index 0000000..9c6c726 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/deltas/aps-cli.md @@ -0,0 +1,56 @@ +# APS CLI SecureState secret dogfood + +## ADDED + +### REQUIREMENT REQ-aps-cli-015 + +`secret` SHALL use a well-named Keychain identity and document headless/CI availability. + +Acceptance Criteria +- Keychain account is `dev.leif.aps/secret` (`APSKeychain.secretAccount`). +- `aps reset secret` deletes the Keychain item on macOS. +- README documents macOS Keychain access, Linux unavailability, and headless CI caveats. +- `set secret` on platforms without Security surfaces `keychainUnavailable`. + +## MODIFIED + +### REQUIREMENT REQ-aps-cli-001 + +The fixed demo schema SHALL include `profile` and `secret` alongside `counter`, `message`, `flag`, and `note`. + +Acceptance Criteria +- `aps keys` lists `profile` and `secret`. +- `aps set profile '{"name":"a","version":1}'` round-trips through get/dump/reset. +- `aps set secret ...` round-trips on macOS through get/dump/reset. + +### SPEC SECTION Public API + +| Export | Description | +|--------|-------------| +| `DemoKey` | Fixed schema enum including `profile` and `secret`. | +| `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). | +| `unknownKey` | Unknown demo key token. | +| `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`). | +| `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. | diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/deltas/state-store.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/deltas/state-store.md new file mode 100644 index 0000000..c5d3151 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/deltas/state-store.md @@ -0,0 +1,12 @@ +# State Store SecureState secret dogfood + +## ADDED + +### REQUIREMENT REQ-state-store-013 + +`StateStore` SHALL expose `secret` as Keychain-backed `SecureState` with account `dev.leif.aps/secret`, verify Keychain read-back after set, and delete the item on reset. + +Acceptance Criteria +- Round-trip get/set works on macOS. +- `reset(.secret)` leaves get as "" and removes the Keychain item. +- Without Security, `set(.secret, ...)` throws `APSError.keychainUnavailable`. diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/design.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/design.md new file mode 100644 index 0000000..794ee21 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/design.md @@ -0,0 +1,2 @@ +# Design +`#if canImport(Security)` gates SecureState. Account `dev.leif.aps/secret`. diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/docs.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/docs.md new file mode 100644 index 0000000..469c26d --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/docs.md @@ -0,0 +1,2 @@ +# Docs +README SecureState section; GOAL in-scope APS-12. diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/plan.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/plan.md new file mode 100644 index 0000000..2e4c107 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/plan.md @@ -0,0 +1,4 @@ +# Plan +1. Add secret DemoKey + APSKeychain + SecureState wiring +2. Darwin round-trip / Linux keychainUnavailable +3. Docs, smoke, specs diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/requirements.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/requirements.md new file mode 100644 index 0000000..b7de474 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/requirements.md @@ -0,0 +1,2 @@ +# Requirements +See deltas for REQ-aps-cli-015 and REQ-state-store-013 (and schema updates). diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/research.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/research.md new file mode 100644 index 0000000..d39d716 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/research.md @@ -0,0 +1,4 @@ +# Research + +AppState SecureState is Keychain-backed behind `#if canImport(Security)`. +CLI dogfood uses account `dev.leif.aps/secret`. Linux has no Security module. 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 new file mode 100644 index 0000000..e867651 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/state.json @@ -0,0 +1,48 @@ +{ + "schema_version": 1, + "id": "CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16", + "slug": "dogfood-securestate-secret-keychain-demo-key-for-issue-16", + "title": "Dogfood SecureState secret Keychain demo key for issue 16", + "description": "Dogfood SecureState secret Keychain demo key for issue 16", + "kind": "feature", + "state": "verifying", + "base_commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", + "created_at": 1784406691, + "updated_at": 1784407199, + "affected_specs": [ + "aps-cli", + "state-store" + ], + "affected_paths": [ + "Sources/aps/DemoKey.swift", + "Sources/aps/DemoState.swift", + "Sources/aps/StateStore.swift", + "Sources/aps/Aps.swift", + "Sources/aps/CLIOutput.swift", + "Tests/apsTests/APSTests.swift", + "Scripts/smoke.sh", + "README.md", + "GOAL.md", + ".specsync/change-sequence.json" + ], + "no_spec_change": false, + "no_spec_change_rationale": null, + "acceptance_criteria": [ + "secret SecureState Keychain dogfood with Darwin round-trip tests, Linux keychainUnavailable, README/GOAL/smoke/specs updated." + ], + "selected_artifacts": [ + "context", + "requirements", + "plan", + "tasks", + "testing", + "docs", + "design", + "research" + ], + "dependencies": [], + "answers": { + "architecture_risk": "yes", + "public_contract": "yes" + } +} diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/tasks.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/tasks.md new file mode 100644 index 0000000..3fa0e7a --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/tasks.md @@ -0,0 +1,4 @@ +# Tasks +- [x] secret key + SecureState +- [x] tests/smoke +- [x] README/GOAL/specs diff --git a/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/testing.md b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/testing.md new file mode 100644 index 0000000..88fe46e --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/testing.md @@ -0,0 +1,15 @@ +# Testing + +## Requirement evidence + +| Requirement | Evidence | +| --- | --- | +| REQ-aps-cli-001 | `testDemoKeyMetadata`, `Scripts/smoke.sh` (`keys` lists secret) | +| REQ-aps-cli-015 | `testSecretSecureStateRoundTrip`, `testSecretResetDeletesKeychainItem`, README Keychain section, Darwin smoke secret block | +| REQ-state-store-013 | `testSecretSecureStateRoundTrip`, `testSecretResetDeletesKeychainItem`, `testSecretPersistsAcrossStateStoreInstances` / `testSecretSetFailsWithoutKeychain` | + +## Suites + +- `swift test` (Darwin SecureState tests; Linux keychainUnavailable) +- `./Scripts/smoke.sh` (secret round-trip on Darwin only) +- `fledge lanes run verify` 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 new file mode 100644 index 0000000..c3df164 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification-attempts.json @@ -0,0 +1,62 @@ +{ + "schema_version": 1, + "attempts": [ + { + "timestamp": 1784406974, + "commit": "6312f8d8379c3b87b05c3eb4436c0b503f161d1a", + "contract_digest": "f931b6b50deee8ecac029eb31f0a16fbdd8e24dd7c2e272dc3c183a91806d91b", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "passed": false, + "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" + ] + }, + { + "timestamp": 1784407084, + "commit": "8f5a1624ad57d16fe43bd12256faa36f80d31644", + "contract_digest": "f931b6b50deee8ecac029eb31f0a16fbdd8e24dd7c2e272dc3c183a91806d91b", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "passed": false, + "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" + ] + }, + { + "timestamp": 1784407198, + "commit": "48cc3c7d58782e361b7b6f12b62c63511c97018f", + "contract_digest": "85e6e4977d59bf2a77046990bf23888ad3476bae934473868e0b15d76cfb0576", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "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 new file mode 100644 index 0000000..2e71705 --- /dev/null +++ b/.specsync/changes/CHG-0012-dogfood-securestate-secret-keychain-demo-key-for-issue-16/verification.json @@ -0,0 +1,19 @@ +{ + "timestamp": 1784407198, + "commit": "48cc3c7d58782e361b7b6f12b62c63511c97018f", + "contract_digest": "85e6e4977d59bf2a77046990bf23888ad3476bae934473868e0b15d76cfb0576", + "workspace_digest": "5ac00f7647356c08d2115b807cc2e47946b4e3c72637be8c8ba39142366fa4ea", + "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/GOAL.md b/GOAL.md index 81374a0..0b2f665 100644 --- a/GOAL.md +++ b/GOAL.md @@ -23,13 +23,18 @@ Make `aps` reliable enough for agents to inspect, mutate, and watch fixed demo A - [x] SpecSync artifacts from 0.1.x are archived; active SpecSync tracks 0.2.0 work. - [x] README documents JSON mode, state root, watch bounds, and `profile`. - [x] CLI `--version` reports `0.2.0`. +- [x] Demo key `secret` uses AppState `SecureState` (Keychain) with round-trip tests and documented CI behavior. ## Explicit out of scope for 0.x -- SyncState, SecureState, and ModelState +- SyncState and ModelState - Plugin APIs, daemon mode, network APIs, or background services - Dynamic schema language or user-defined state keys +## In scope (added) + +- SecureState dogfood via the fixed `secret` demo key (Keychain-backed String). Available where Apple's Security framework exists (macOS). Linux/headless CI behavior is documented; Keychain round-trips are not required on Linux. + ## Tickets | ID | Item | @@ -45,6 +50,7 @@ Make `aps` reliable enough for agents to inspect, mutate, and watch fixed demo A | APS-09 | Structured `profile` FileState | | APS-10 | SpecSync archive hygiene | | APS-11 | README agent usage | +| APS-12 | SecureState `secret` Keychain dogfood | ## Definition of done diff --git a/README.md b/README.md index 3a60b0a..394b317 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,18 @@ State root resolution: `--state-dir` > `APS_HOME` > `~/.aps`. | `flag` | `Bool` | `StoredState` | Persisted (`UserDefaults`; CLI calls `synchronize()` so Linux flushes) | | `note` | `String` | `FileState` | Persisted (`$APS_HOME/note.json`) | | `profile` | `{name,version}` | `FileState` | Persisted structured Codable (`$APS_HOME/profile.json`) | +| `secret` | `String` | `SecureState` | Persisted in Keychain (macOS); account `dev.leif.aps/secret` | Dynamic / user-declared keys are intentionally out of scope for 0.x. +### SecureState / Keychain (`secret`) + +- AppState `SecureState` stores the value under Keychain account `dev.leif.aps/secret` (feature `dev.leif.aps`, id `secret`). +- `aps reset secret` (and `aps reset --all`) deletes that Keychain item; get then returns an empty string. +- **macOS:** works when the process can access the login Keychain (interactive sessions and typical self-hosted macOS CI). +- **Linux / headless without Security:** Apple's Security framework is unavailable. `aps keys` still lists `secret`, `get` returns empty, and `set secret` fails with a clear Keychain-unavailable error. Linux smoke skips secret round-trips. +- **Headless macOS CI:** if the Keychain is locked or inaccessible, set/get may fail with persistence errors. Unlock or use a runner with an available login Keychain. + ### Dependencies `aps` injects real services with `@AppDependency` / `Application.dependency`, plus one @@ -159,7 +168,7 @@ See [`GOAL.md`](GOAL.md) for **aps 0.2.0**: agent-ready AppState dogfood harness ## Non-goals (0.x) -- No iCloud `SyncState`, Keychain `SecureState`, or SwiftData `ModelState` +- No iCloud `SyncState` or SwiftData `ModelState` (see `docs/spikes/`) - No plugin system, daemon, or network API - No dynamic schema language: fixed demo keys only diff --git a/Scripts/smoke.sh b/Scripts/smoke.sh index f1387ac..a7bdfb7 100755 --- a/Scripts/smoke.sh +++ b/Scripts/smoke.sh @@ -18,7 +18,9 @@ bin="$APS_BIN" test "$("$bin" --version)" = "0.2.0" "$bin" keys | grep -q counter "$bin" keys | grep -q profile +"$bin" keys | grep -q secret "$bin" keys --json | grep -q '"key" : "profile"' +"$bin" keys --json | grep -q '"key" : "secret"' # `set` prints the value; State is process-local so don't expect get in a new process. test "$("$bin" set counter 11)" = "11" @@ -37,6 +39,21 @@ test "$("$bin" get note)" = "smoke-note" "$bin" get profile --json | grep -q '"name" : "smoke"' "$bin" get profile --json | grep -q '"version" : 2' +# SecureState / Keychain is Darwin-only (Security framework). +if [[ "$(uname -s)" == "Darwin" ]]; then + "$bin" set secret "smoke-secret" >/dev/null + test "$("$bin" get secret)" = "smoke-secret" + "$bin" get secret --json | grep -q '"storage" : "SecureState"' + "$bin" reset secret >/dev/null + test -z "$("$bin" get secret)" +else + # Linux: schema lists secret, but set must fail clearly without Keychain. + if "$bin" set secret "smoke-secret" >/dev/null 2>&1; then + echo "expected secret set to fail without Keychain" >&2 + exit 1 + fi +fi + # --state-dir overrides APS_HOME OTHER="$(mktemp -d "${TMPDIR:-/tmp}/aps-smoke-other.XXXXXX")" "$bin" set note "other-root" --state-dir "$OTHER" >/dev/null @@ -45,6 +62,7 @@ test "$("$bin" get note)" = "smoke-note" "$bin" dump | grep -q '"key" : "flag"' "$bin" dump --json | grep -q '"key" : "profile"' +"$bin" dump --json | grep -q '"key" : "secret"' "$bin" reset flag >/dev/null test "$("$bin" get flag)" = "false" diff --git a/Sources/aps/Aps.swift b/Sources/aps/Aps.swift index d3b5b3b..2adfef1 100644 --- a/Sources/aps/Aps.swift +++ b/Sources/aps/Aps.swift @@ -14,6 +14,7 @@ struct Aps: ParsableCommand { flag Bool StoredState (UserDefaults) note String FileState (~/.aps/note.json) profile ProfileDocument FileState (~/.aps/profile.json) + secret String SecureState (Keychain; macOS) State root: --state-dir > APS_HOME > ~/.aps @@ -39,7 +40,7 @@ extension Aps { abstract: "Print the current value for a demo key." ) - @Argument(help: "Demo key: counter | message | flag | note | profile") + @Argument(help: "Demo key: counter | message | flag | note | profile | secret") var key: DemoKey @OptionGroup @@ -69,10 +70,10 @@ extension Aps { abstract: "Set a demo key to a value." ) - @Argument(help: "Demo key: counter | message | flag | note | profile") + @Argument(help: "Demo key: counter | message | flag | note | profile | secret") var key: DemoKey - @Argument(help: "New value (Bool: true/false/1/0; Int for counter; JSON for profile)") + @Argument(help: "New value (Bool: true/false/1/0; Int for counter; JSON for profile; String for secret)") var value: String @OptionGroup @@ -107,7 +108,7 @@ extension Aps { abstract: "Print the value whenever it changes (Observation + polling)." ) - @Argument(help: "Demo key: counter | message | flag | note | profile") + @Argument(help: "Demo key: counter | message | flag | note | profile | secret") var key: DemoKey @Option(name: .long, help: "Poll interval in milliseconds (fallback for disk-backed keys).") diff --git a/Sources/aps/CLIOutput.swift b/Sources/aps/CLIOutput.swift index ec8991b..d4a5f13 100644 --- a/Sources/aps/CLIOutput.swift +++ b/Sources/aps/CLIOutput.swift @@ -114,7 +114,7 @@ enum CLIOutput { throw APSError.invalidValue(key: key, value: raw) } return .int(intValue) - case .message, .note: + case .message, .note, .secret: return .string(raw) case .flag: guard let boolValue = StateStore.parseBool(raw) else { diff --git a/Sources/aps/DemoKey.swift b/Sources/aps/DemoKey.swift index 74a21be..6995057 100644 --- a/Sources/aps/DemoKey.swift +++ b/Sources/aps/DemoKey.swift @@ -8,19 +8,21 @@ public enum DemoKey: String, CaseIterable, ExpressibleByArgument, Sendable { case flag case note case profile + case secret public var storage: String { switch self { case .counter, .message: return "State" case .flag: return "StoredState" case .note, .profile: return "FileState" + case .secret: return "SecureState" } } public var valueType: String { switch self { case .counter: return "Int" - case .message, .note: return "String" + case .message, .note, .secret: return "String" case .flag: return "Bool" case .profile: return "ProfileDocument" } @@ -43,6 +45,8 @@ public enum DemoKey: String, CaseIterable, ExpressibleByArgument, Sendable { return "String via FileState (~/.aps/note.json)" case .profile: return "Codable {name,version} via FileState (~/.aps/profile.json)" + case .secret: + return "String via SecureState / Keychain (\(APSKeychain.secretAccount))" } } } @@ -58,12 +62,30 @@ public struct ProfileDocument: Codable, Equatable, Sendable { } } +/// Well-known Keychain identity for the `secret` SecureState demo key. +/// +/// AppState stores SecureState under `Scope.key` (`feature/id`) as the Keychain +/// account (`kSecAttrAccount`). The feature string is the service-style namespace. +public enum APSKeychain: Sendable { + /// Reverse-DNS style service / feature namespace. + public static let service = "dev.leif.aps" + + /// Account id within the service namespace. + public static let account = "secret" + + /// Full Keychain account key used by AppState (`service/account`). + public static var secretAccount: String { + "\(service)/\(account)" + } +} + public enum APSError: Error, CustomStringConvertible, Equatable { case unknownKey(String) case invalidValue(key: DemoKey, value: String) case encodingFailed case decodingFailed case persistenceFailed(key: DemoKey) + case keychainUnavailable public var description: String { switch self { @@ -76,7 +98,9 @@ public enum APSError: Error, CustomStringConvertible, Equatable { case .decodingFailed: return "Failed to decode value from UTF-8 JSON" case .persistenceFailed(let key): - return "Failed to persist \(key.rawValue) to disk" + return "Failed to persist \(key.rawValue)" + case .keychainUnavailable: + return "SecureState (Keychain) is unavailable on this platform. The secret key requires macOS Keychain." } } } diff --git a/Sources/aps/DemoState.swift b/Sources/aps/DemoState.swift index 5f881ec..b21fc09 100644 --- a/Sources/aps/DemoState.swift +++ b/Sources/aps/DemoState.swift @@ -40,6 +40,20 @@ extension Application { ) } +#if canImport(Security) + /// Sensitive string via Keychain-backed `SecureState`. + /// + /// Keychain account: `APSKeychain.secretAccount` (`dev.leif.aps/secret`). + /// Initial value is `nil` so `reset` deletes the Keychain item. + var secret: SecureState { + secureState( + initial: nil, + feature: APSKeychain.service, + id: APSKeychain.account + ) + } +#endif + /// Wall-clock used when stamping watch/dump output. var clock: Dependency { dependency(SystemAPSClock()) diff --git a/Sources/aps/StateStore.swift b/Sources/aps/StateStore.swift index e0b0117..34f7a05 100644 --- a/Sources/aps/StateStore.swift +++ b/Sources/aps/StateStore.swift @@ -27,6 +27,9 @@ public final class StateStore { Application.load(dependency: \.clock) Application.load(dependency: \.jsonCoding) Application.load(dependency: \.stats) +#if canImport(Security) + Application.load(dependency: \.keychain) +#endif } /// Wall clock from the injected `APSClock` dependency. @@ -57,6 +60,12 @@ public final class StateStore { return Application.fileState(\.note).value case .profile: return (try? encodeProfile(Application.fileState(\.profile).value)) ?? "{\"name\":\"\",\"version\":0}" + case .secret: +#if canImport(Security) + return Application.secureState(\.secret).value ?? "" +#else + return "" +#endif } } @@ -105,6 +114,17 @@ public final class StateStore { guard onDisk == document else { throw APSError.persistenceFailed(key: .profile) } + case .secret: +#if canImport(Security) + var state = Application.secureState(\.secret) + state.value = value + let stored = Application.dependency(\.keychain).get(APSKeychain.secretAccount) + guard stored == value else { + throw APSError.persistenceFailed(key: .secret) + } +#else + throw APSError.keychainUnavailable +#endif } stats.recordMutation(key: key) } @@ -122,6 +142,12 @@ public final class StateStore { Application.reset(fileState: \.note) case .profile: Application.reset(fileState: \.profile) + case .secret: +#if canImport(Security) + Application.reset(secureState: \.secret) +#else + break +#endif } stats.recordMutation(key: key) } @@ -192,7 +218,7 @@ public final class StateStore { /// Blocking watch for the synchronous CLI: Observation + RunLoop polling. /// /// - Observation covers in-process mutations (`State`). - /// - Polling re-reads values so `FileState` / `StoredState` updates can surface when + /// - Polling re-reads values so `FileState` / `StoredState` / `SecureState` updates can surface when /// Observation alone would not (e.g. another process wrote the file). /// - For disk-backed keys, polling reads files directly so AppState's FileState cache /// cannot hide cross-process writes. @@ -241,7 +267,7 @@ public final class StateStore { return (try? encodeProfile(document)) ?? get(key) } return get(key) - case .counter, .message, .flag: + case .counter, .message, .flag, .secret: return get(key) } } @@ -294,6 +320,10 @@ public final class StateStore { _ = Application.fileState(\.note).value case .profile: _ = Application.fileState(\.profile).value + case .secret: +#if canImport(Security) + _ = Application.secureState(\.secret).value +#endif } } diff --git a/Tests/apsTests/APSTests.swift b/Tests/apsTests/APSTests.swift index 18656bf..a8c7a61 100644 --- a/Tests/apsTests/APSTests.swift +++ b/Tests/apsTests/APSTests.swift @@ -51,11 +51,15 @@ final class APSTests: XCTestCase { XCTAssertEqual(DemoKey.flag.storage, "StoredState") XCTAssertEqual(DemoKey.note.storage, "FileState") XCTAssertEqual(DemoKey.profile.storage, "FileState") + XCTAssertEqual(DemoKey.secret.storage, "SecureState") XCTAssertEqual(DemoKey.counter.valueType, "Int") XCTAssertEqual(DemoKey.profile.valueType, "ProfileDocument") - XCTAssertEqual(DemoKey.allCases.count, 5) + XCTAssertEqual(DemoKey.secret.valueType, "String") + XCTAssertEqual(DemoKey.allCases.count, 6) XCTAssertTrue(DemoKey.note.detail.contains("FileState")) XCTAssertTrue(DemoKey.profile.detail.contains("profile.json")) + XCTAssertTrue(DemoKey.secret.detail.contains("Keychain")) + XCTAssertEqual(APSKeychain.secretAccount, "dev.leif.aps/secret") } @MainActor @@ -97,6 +101,64 @@ final class APSTests: XCTestCase { XCTAssertEqual(try StateStore.readProfileFromDisk(), document) } + +#if canImport(Security) + @MainActor + func testSecretSecureStateRoundTrip() async throws { + let store = StateStore() + try store.set(.secret, value: "top-secret") + XCTAssertEqual(store.get(.secret), "top-secret") + + let keychain = Application.dependency(\.keychain) + XCTAssertEqual(keychain.get(APSKeychain.secretAccount), "top-secret") + + try store.set(.secret, value: "rotated") + XCTAssertEqual(store.get(.secret), "rotated") + XCTAssertEqual(keychain.get(APSKeychain.secretAccount), "rotated") + } + + @MainActor + func testSecretResetDeletesKeychainItem() async throws { + let store = StateStore() + try store.set(.secret, value: "ephemeral") + XCTAssertEqual( + Application.dependency(\.keychain).get(APSKeychain.secretAccount), + "ephemeral" + ) + + store.reset(.secret) + XCTAssertEqual(store.get(.secret), "") + XCTAssertNil(Application.dependency(\.keychain).get(APSKeychain.secretAccount)) + } + + @MainActor + func testSecretPersistsAcrossStateStoreInstances() async throws { + let writer = StateStore() + try writer.set(.secret, value: "shared-secret") + + let reader = StateStore() + XCTAssertEqual(reader.get(.secret), "shared-secret") + + reader.reset(.secret) + XCTAssertEqual(StateStore().get(.secret), "") + XCTAssertNil(Application.dependency(\.keychain).get(APSKeychain.secretAccount)) + } +#else + @MainActor + func testSecretSetFailsWithoutKeychain() async { + let store = StateStore() + do { + try store.set(.secret, value: "nope") + XCTFail("Expected keychainUnavailable on platforms without Security") + } catch let error as APSError { + XCTAssertEqual(error, .keychainUnavailable) + } catch { + XCTFail("Unexpected error: \(error)") + } + XCTAssertEqual(store.get(.secret), "") + } +#endif + @MainActor func testInvalidProfileJSON() async { let store = StateStore() diff --git a/specs/aps-cli/aps-cli.spec.md b/specs/aps-cli/aps-cli.spec.md index 4e22ab1..93c0574 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: 11 +version: 12 status: active files: - Sources/aps/Aps.swift @@ -23,7 +23,7 @@ agents can get, set, watch, dump, list, and reset typed application state. | Export | Description | |--------|-------------| -| `DemoKey` | Fixed schema enum including `profile`. | +| `DemoKey` | Fixed schema enum including `profile` and `secret`. | | `ProfileDocument` | Codable `{name, version}` FileState document. | | `name` | ProfileDocument display name field. | | `version` | ProfileDocument integer version field. | @@ -34,12 +34,18 @@ agents can get, set, watch, dump, list, and reset typed application 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). | +| `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`). | +| `keychainUnavailable` | SecureState unavailable without Apple Security. | | `unknownKey` | Unknown demo key token. | | `invalidValue` | Value could not parse for the key type. | | `encodingFailed` | UTF-8 JSON encode failure. | | `decodingFailed` | UTF-8 JSON decode failure. | | `persistenceFailed` | Disk-backed key did not persist after write. | -| `storage` | Human storage kind (`State` / `StoredState` / `FileState`). | +| `storage` | Human storage kind (`State` / `StoredState` / `FileState` / `SecureState`). | | `valueType` | Human value type (`Int` / `String` / `Bool` / `ProfileDocument`). | | `helpSummary` | Tab-separated key/type/storage columns for `keys`. | | `detail` | One-line description for `keys`. | @@ -109,4 +115,5 @@ Then the watcher prints `changed` within one poll interval. | 2026-07-18 | CHG-0002-fix-filestate-watch-cache-and-path-isolation-from-review: Fix FileState watch cache and path isolation from review | | 2026-07-18 | CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate: Ship aps 0.2.0 agent-ready JSON state-dir watch and profile FileState | | 2026-07-18 | CHG-0011-dogfood-observeddependency-demostats-for-issue-18: ObservedDependency DemoStats dogfood | +| 2026-07-18 | CHG-0012-dogfood-securestate-secret-keychain-demo-key: SecureState secret Keychain dogfood | | 2026-07-18 | CHG-0011-dogfood-observeddependency-demostats-for-issue-18: Dogfood ObservedDependency DemoStats for issue 18 | diff --git a/specs/aps-cli/requirements.md b/specs/aps-cli/requirements.md index a15cc3a..67e7c51 100644 --- a/specs/aps-cli/requirements.md +++ b/specs/aps-cli/requirements.md @@ -8,10 +8,10 @@ spec: aps-cli.spec.md ### REQ-aps-cli-001 -The fixed demo schema SHALL include `profile` alongside `counter`, `message`, `flag`, and `note`. +The fixed demo schema SHALL include `profile` and `secret` alongside `counter`, `message`, `flag`, and `note`. Acceptance Criteria -- `aps keys` lists `profile`. +- `aps keys` lists `profile` and `secret`. - `aps set profile '{"name":"a","version":1}'` round-trips through get/dump/reset. ### REQ-aps-cli-002 @@ -89,3 +89,14 @@ Acceptance Criteria - `aps stats --json` includes `mutationCount` and `lastMutatedKey`. - `aps stats --watch --count 1` exits after printing the initial snapshot. + + +### REQ-aps-cli-015 + +`secret` SHALL use a well-named Keychain identity and document headless/CI availability. + +Acceptance Criteria +- Keychain account is `dev.leif.aps/secret` (`APSKeychain.secretAccount`). +- `aps reset secret` deletes the Keychain item on macOS. +- README documents macOS Keychain access, Linux unavailability, and headless CI caveats. +- `set secret` on platforms without Security surfaces `keychainUnavailable`. diff --git a/specs/aps-cli/testing.md b/specs/aps-cli/testing.md index 44398d8..152cd00 100644 --- a/specs/aps-cli/testing.md +++ b/specs/aps-cli/testing.md @@ -4,3 +4,5 @@ - Integration: StateStore round-trips via `@testable import aps` - Unit: ObservedDependency stats mutation + Combine observation - Smoke: `Scripts/smoke.sh` for flag/note persistence, reset, and `aps stats` + +- SecureState `secret` round-trip / Keychain delete (Darwin) or keychainUnavailable (else). diff --git a/specs/state-store/requirements.md b/specs/state-store/requirements.md index aeae2d9..1ea9170 100644 --- a/specs/state-store/requirements.md +++ b/specs/state-store/requirements.md @@ -67,3 +67,13 @@ Acceptance Criteria - `watchStatsBlocking` emits the current snapshot first, then a distinct snapshot after a mutation. - A unit test shows Combine observation (`$mutationCount`) fires on dependency mutation. + + +### REQ-state-store-013 + +`StateStore` SHALL expose `secret` as Keychain-backed `SecureState` with account `dev.leif.aps/secret`, verify Keychain read-back after set, and delete the item on reset. + +Acceptance Criteria +- Round-trip get/set works on macOS. +- `reset(.secret)` leaves get as "" and removes the Keychain item. +- Without Security, `set(.secret, ...)` throws `APSError.keychainUnavailable`. diff --git a/specs/state-store/state-store.spec.md b/specs/state-store/state-store.spec.md index 10a9e17..e6e3d5a 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: 11 +version: 12 status: active files: - Sources/aps/StateStore.swift @@ -111,4 +111,5 @@ Then keys include message with value "hi" and a timestamp field exists. | 2026-07-18 | CHG-0002-fix-filestate-watch-cache-and-path-isolation-from-review: Fix FileState watch cache and path isolation from review | | 2026-07-18 | CHG-0004-ship-aps-0-2-0-agent-ready-json-state-dir-watch-and-profile-filestate: Ship aps 0.2.0 agent-ready JSON state-dir watch and profile FileState | | 2026-07-18 | CHG-0011-dogfood-observeddependency-demostats-for-issue-18: ObservedDependency DemoStats dogfood | +| 2026-07-18 | CHG-0012-dogfood-securestate-secret-keychain-demo-key: SecureState secret Keychain dogfood | | 2026-07-18 | CHG-0011-dogfood-observeddependency-demostats-for-issue-18: Dogfood ObservedDependency DemoStats for issue 18 | diff --git a/specs/state-store/testing.md b/specs/state-store/testing.md index 08f6ec6..7110fc2 100644 --- a/specs/state-store/testing.md +++ b/specs/state-store/testing.md @@ -6,3 +6,5 @@ - dump includes dependency-driven timestamp and all keys - DemoStats ObservedDependency records mutations and Combine publishes on change - watchStatsBlocking detects dependency mutation + +- SecureState `secret` round-trip / Keychain delete (Darwin) or keychainUnavailable (else).