Compare commits

..
Author SHA1 Message Date
m d19fcceff3 docs: record authenticated mobile acceptance
CI / test (push) Successful in 57s
CI / test (pull_request) Successful in 54s
2026-08-24 14:02:47 +03:00
m 8c6f23dc49 Merge pull request 'DEV-000290 Fix mobile chat header overflow' (#268) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m5s
2026-08-24 10:59:52 +00:00
m 19c58b6d25 fix: constrain mobile chat header
CI / test (push) Successful in 54s
CI / test (pull_request) Successful in 51s
2026-08-24 13:57:39 +03:00
m e9d5b1ca8a Merge pull request 'DEV-000289 Fix authenticated chat mobile overflow' (#267) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 50s
2026-08-24 10:52:31 +00:00
m 444d9d825a fix: constrain chat layout on mobile
CI / test (push) Successful in 55s
CI / test (pull_request) Successful in 54s
2026-08-24 13:50:16 +03:00
m a2ff01050e Merge pull request 'DEV-000288 Refresh production evidence revision' (#266) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 57s
2026-08-24 10:40:57 +00:00
m 443e0ceb2e docs: refresh production evidence revision
CI / test (push) Successful in 58s
CI / test (pull_request) Successful in 52s
2026-08-24 13:38:42 +03:00
m 3e91ae503c Merge pull request 'DEV-000287 Reconcile live audit evidence' (#265) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 54s
2026-08-24 10:33:25 +00:00
m bf6f2365d4 Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status
CI / test (push) Successful in 1m2s
CI / test (pull_request) Successful in 53s
2026-08-24 13:30:52 +03:00
m 9b7091bbe9 docs: reconcile live audit evidence
CI / test (push) Successful in 59s
CI / test (pull_request) Successful in 55s
2026-08-24 13:28:26 +03:00
m 12725511c0 Merge pull request 'DEV-000286 Reject irrelevant research excerpts' (#264) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m8s
2026-08-24 08:51:04 +00:00
m 3c46a5f07f fix: reject irrelevant research excerpts
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 53s
2026-08-24 06:40:31 +03:00
m b0a8b3e5a7 Merge pull request 'DEV-000285 Route source-dependent questions to research' (#263) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 50s
2026-08-24 03:32:34 +00:00
m f6f68ffb04 fix: route source-dependent questions to research
CI / test (push) Successful in 54s
CI / test (pull_request) Successful in 53s
2026-08-24 06:29:56 +03:00
m 5c98bc3568 Merge pull request 'DEV-000284 Record live generic graph synthesis evidence' (#262) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 57s
2026-08-24 01:59:32 +00:00
m de1d2f5107 docs: record live generic graph synthesis evidence
CI / test (push) Successful in 52s
CI / test (pull_request) Successful in 52s
2026-08-24 04:56:50 +03:00
m bcbd41f217 Merge pull request 'DEV-000283 Verify generic Work Graph candidate envelopes' (#261) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 52s
2026-08-24 01:41:09 +00:00
m f5616fe3ea feat: verify generic work graph candidate envelopes
CI / test (push) Successful in 53s
CI / test (pull_request) Successful in 52s
2026-08-24 04:38:40 +03:00
m 94baf98060 Merge pull request 'DEV-000282 Record final Portainer auto-deploy evidence' (#260) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 53s
2026-08-24 01:32:19 +00:00
m c499ae090e docs: record final Portainer auto-deploy evidence
CI / test (push) Successful in 55s
CI / test (pull_request) Successful in 55s
2026-08-24 04:29:20 +03:00
m e132ec6ba9 Merge pull request 'DEV-000281 Reconcile live generic execution evidence' (#259) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 54s
2026-08-24 00:38:54 +00:00
m 344f8ad106 docs: reconcile live generic execution evidence
CI / test (push) Successful in 54s
CI / test (pull_request) Successful in 54s
2026-08-24 03:36:06 +03:00
m 080bd44021 Merge pull request 'DEV-000280 Add local generic Agent Execution' (#258) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m16s
2026-08-24 00:01:15 +00:00
m 318a68c3dd chore: activate generic agent execution task
CI / test (push) Successful in 50s
CI / test (pull_request) Successful in 50s
2026-08-24 02:58:40 +03:00
m 73d7ffd012 feat: execute generic local work graph candidates
CI / test (push) Failing after 37s
CI / test (pull_request) Failing after 35s
2026-08-24 02:56:19 +03:00
m 51db6377a4 Merge pull request 'DEV-000279 Reconcile current audit production evidence' (#257) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 51s
2026-08-23 23:46:18 +00:00
m c3b4283101 docs: reconcile current audit production evidence
CI / test (push) Successful in 55s
CI / test (pull_request) Successful in 50s
2026-08-24 02:42:58 +03:00
m ffd7ddcce5 Merge pull request 'DEV-000278 Reconcile live research evidence' (#256) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 51s
2026-08-23 23:38:24 +00:00
m dd83442ff9 docs: reconcile live research evidence
CI / test (push) Successful in 53s
CI / test (pull_request) Successful in 53s
2026-08-24 02:36:08 +03:00
m f1c3abb3b7 Merge pull request 'DEV-000277 Parse current research provider markup' (#255) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 52s
2026-08-23 23:25:30 +00:00
m a56957138d fix: parse current research provider markup
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 53s
2026-08-24 02:23:07 +03:00
m 5058d5ac0e Merge pull request 'DEV-000276 Use available bounded research search provider' (#254) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 53s
2026-08-23 23:17:23 +00:00
m 00d7f70ffb fix: use available bounded research search provider
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 53s
2026-08-24 02:15:03 +03:00
m c09be4c9b5 Merge pull request 'DEV-000275 Semantic web research routing' (#253) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 53s
2026-08-23 23:07:13 +00:00
m 976edccfd8 feat: route factual questions through bounded research
CI / test (push) Successful in 55s
CI / test (pull_request) Successful in 53s
2026-08-24 02:04:57 +03:00
m 1eb3e39286 Merge pull request 'DEV-000274 Truth-first chat and intent-aware model routing' (#252) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 53s
2026-08-23 22:51:07 +00:00
m 15088d1d7d Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status
CI / test (push) Successful in 49s
CI / test (pull_request) Successful in 52s
2026-08-24 01:48:49 +03:00
m 4cdd1a5276 fix: route model mentions by intent
CI / test (push) Successful in 50s
CI / test (pull_request) Successful in 49s
2026-08-24 01:46:34 +03:00
m e93f810946 fix: require verification before factual chat answers
CI / test (push) Successful in 47s
2026-08-24 01:42:41 +03:00
m 459d836468 Merge pull request 'DEV-000273 reconcile live audit evidence' (#251) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 59s
2026-08-23 22:39:40 +00:00
m 52058459f6 Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status
CI / test (push) Successful in 48s
CI / test (pull_request) Successful in 48s
2026-08-24 01:36:20 +03:00
m 9b91be0764 docs: reconcile live audit evidence
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 50s
2026-08-24 01:31:24 +03:00
m e4f8d6fa70 Merge pull request 'DEV-000270 local-first model scope' (#250) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m3s
2026-08-23 22:19:43 +00:00
m d7151f1d78 Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 49s
2026-08-24 01:17:31 +03:00
m d357e7714a docs: define local-first model scope
CI / test (push) Successful in 54s
CI / test (pull_request) Successful in 52s
2026-08-24 01:15:00 +03:00
m 9160654a07 Merge pull request 'DEV-000268 fresh GPU timeout evidence' (#249) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 55s
2026-08-23 22:06:28 +00:00
m 29dad403ec docs: record gpu timeout after bounded retry
CI / test (push) Successful in 52s
CI / test (pull_request) Successful in 1m1s
2026-08-24 01:03:52 +03:00
m 041c1ce849 Merge pull request 'DEV-000268 configurable GPU provider timeout' (#248) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m0s
2026-08-23 21:59:45 +00:00
m ba162f6433 fix: make gpu provider timeout configurable
CI / test (push) Successful in 48s
CI / test (pull_request) Successful in 53s
2026-08-24 00:57:24 +03:00
m 3ca7ffab16 Merge pull request 'DEV-000267 refresh GPU and UI gap evidence' (#247) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 50s
2026-08-23 21:49:18 +00:00
m 615a3e4765 docs: refresh gpu and ui gap evidence
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 49s
2026-08-24 00:47:02 +03:00
m c459cd563c Merge pull request 'DEV-000266 live Agent Execution bridge evidence' (#246) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 50s
2026-08-23 21:40:02 +00:00
m b2753a64c4 Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status
CI / test (push) Successful in 48s
CI / test (pull_request) Successful in 49s
2026-08-24 00:37:49 +03:00
m 649dd602dd docs: record live agent execution bridge
CI / test (push) Successful in 58s
CI / test (pull_request) Successful in 51s
2026-08-24 00:34:58 +03:00
m 7a004e524a Merge pull request 'DEV-000266 local Agent Execution bridge' (#245) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m6s
2026-08-23 21:32:10 +00:00
m cb78c35f24 Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status
CI / test (push) Successful in 49s
CI / test (pull_request) Successful in 49s
2026-08-24 00:29:32 +03:00
m b1a79ede91 feat: add local agent execution bridge
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 49s
2026-08-24 00:27:10 +03:00
m e012694108 Merge pull request 'DEV-000265 local agent readiness evidence' (#244) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 51s
2026-08-23 21:16:09 +00:00
m c4f3f02933 docs(evidence): record local agent readiness
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 1m0s
2026-08-24 00:13:37 +03:00
m 102a57aaff Merge pull request 'DEV-000264 capability-aware agent worker readiness' (#243) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 51s
2026-08-23 21:11:33 +00:00
m 381e4d52e8 fix(agent-worker): report local capability readiness
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 1m18s
2026-08-24 00:08:18 +03:00
m c8dc9edbdc Merge pull request 'DEV-000263 record GPU qualification timeout' (#242) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 51s
2026-08-23 21:05:54 +00:00
m a0b0b32f0a docs(evidence): record gpu qualification timeout
CI / test (push) Successful in 50s
CI / test (pull_request) Successful in 51s
2026-08-24 00:03:29 +03:00
m 1f2068512e Merge pull request 'DEV-000262 additional evaluation runtime live evidence' (#241) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m16s
2026-08-23 20:57:34 +00:00
m b97c910434 docs(evidence): record additional evaluation runtime
CI / test (push) Successful in 59s
CI / test (pull_request) Successful in 50s
2026-08-23 23:55:09 +03:00
m b789878d01 Merge pull request 'DEV-000261 expose additional evaluation scheduling endpoint' (#240) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 50s
2026-08-23 20:52:29 +00:00
m 705e4a8ed6 Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status
CI / test (push) Successful in 52s
CI / test (pull_request) Successful in 49s
2026-08-23 23:49:33 +03:00
m 531dfa85ad feat(work): expose additional evaluation scheduling
CI / test (push) Successful in 54s
CI / test (pull_request) Successful in 49s
2026-08-23 23:45:45 +03:00
m 6e215b5b57 Merge pull request 'DEV-000261 durable additional task evaluation scheduling' (#239) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 54s
2026-08-23 20:42:20 +00:00
m d46c0cf719 chore(governance): align additional evaluation task base
CI / test (push) Successful in 59s
CI / test (pull_request) Successful in 49s
2026-08-23 23:38:32 +03:00
m 3eecc710ed Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status 2026-08-23 23:37:57 +03:00
m 1b4284592b feat(work): schedule bounded additional evaluations 2026-08-23 23:37:56 +03:00
m d2dd38b9ae Merge pull request 'DEV-000260 deep authenticated acceptance evidence' (#238) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 49s
2026-08-23 19:45:54 +00:00
m fb6b5e9677 Merge remote-tracking branch 'origin/main' into codex/dev-000248-self-development-runtime-status
CI / test (push) Successful in 53s
CI / test (pull_request) Successful in 58s
2026-08-23 22:43:57 +03:00
m 40735ce9c1 docs(acceptance): record deep authenticated evidence
CI / test (push) Successful in 47s
CI / test (pull_request) Successful in 49s
2026-08-23 22:41:26 +03:00
m 9f40c6a793 Merge pull request 'docs(governance): refresh post-responsive evidence' (#237) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m0s
2026-08-23 19:18:35 +00:00
m a9dbc3b624 docs(governance): refresh post-responsive evidence
CI / test (push) Successful in 52s
CI / test (pull_request) Successful in 49s
2026-08-23 22:16:20 +03:00
m d4b5416126 Merge pull request 'test(creator-web): guard responsive console layout' (#236) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 59s
2026-08-23 19:12:44 +00:00
m 7c4981ea09 test(creator-web): guard responsive console layout
CI / test (push) Successful in 53s
CI / test (pull_request) Successful in 50s
2026-08-23 22:10:31 +03:00
m 5e8286695f Merge pull request 'docs(governance): record operations acceptance smoke' (#235) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m0s
2026-08-23 19:05:31 +00:00
m 2b11baa170 docs(governance): record operations acceptance smoke
CI / test (push) Successful in 54s
CI / test (pull_request) Successful in 51s
2026-08-23 22:03:12 +03:00
m 8868258afd Merge pull request 'feat(capability): expose reachability rating evidence' (#234) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 57s
2026-08-23 18:57:40 +00:00
m 90cb2b1590 feat(capability): expose reachability rating evidence
CI / test (push) Successful in 50s
CI / test (pull_request) Successful in 52s
2026-08-23 21:55:13 +03:00
m 3d97b348f7 Merge pull request 'docs(governance): refresh live audit status' (#233) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 58s
2026-08-23 18:50:58 +00:00
m 3eea9e58cd docs(governance): refresh live audit status
CI / test (push) Successful in 49s
CI / test (pull_request) Successful in 49s
2026-08-23 21:48:43 +03:00
m 12ab124411 Merge pull request 'feat(governance): add development gate evidence transitions' (#232) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m0s
2026-08-23 18:40:44 +00:00
m 0886342031 feat(governance): add development gate evidence transitions
CI / test (push) Successful in 47s
CI / test (pull_request) Successful in 47s
2026-08-23 21:38:46 +03:00
m 3bfe76278b Merge pull request 'feat(governance): connect verifier to DevelopmentGate' (#231) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 56s
2026-08-23 18:32:28 +00:00
m 067facdfa4 feat(governance): connect verifier to development gate
CI / test (push) Successful in 47s
CI / test (pull_request) Successful in 52s
2026-08-23 21:29:53 +03:00
m f2e520e31e Merge pull request 'feat(governance): add local bounded verification executor' (#230) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m1s
2026-08-23 18:23:18 +00:00
m cf91807fab feat(governance): add local bounded verification executor
CI / test (push) Successful in 48s
CI / test (pull_request) Successful in 50s
2026-08-23 21:21:06 +03:00
m c925089fd2 Merge pull request 'fix(reflection): preserve cognition-required candidates' (#229) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 57s
2026-08-23 18:09:09 +00:00
m a2d0a3bf19 fix(reflection): preserve cognition-required candidates
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 48s
2026-08-23 21:07:02 +03:00
m c1cde67bcf Merge pull request 'fix(work-graph): preserve scheduled task evaluation revision' (#228) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 56s
2026-08-23 18:01:18 +00:00
m aeafc6e0ac fix(work-graph): preserve scheduled task evaluation revision
CI / test (push) Successful in 50s
CI / test (pull_request) Successful in 49s
2026-08-23 20:58:50 +03:00
m 1f5569db15 chore(governance): activate task evaluation callback repair 2026-08-23 20:57:52 +03:00
m 2b8cea5aa8 Merge pull request 'fix(cognitive): fail over task evaluation candidates' (#227) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 55s
2026-08-23 17:54:12 +00:00
m 7b09b3b2ec fix(cognitive): fail over task evaluation candidates
CI / test (push) Successful in 45s
CI / test (pull_request) Successful in 46s
2026-08-23 20:52:09 +03:00
m 9cc5101a30 Merge pull request 'test(work-graph): lock task evaluation wire contract' (#226) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 1m6s
2026-08-23 17:37:45 +00:00
m f90ed7af35 test(work-graph): lock task evaluation wire contract
CI / test (push) Successful in 50s
CI / test (pull_request) Successful in 50s
2026-08-23 20:35:27 +03:00
m 6f61601d77 Merge pull request 'fix(work-graph): diagnose task evaluation scheduling' (#225) from codex/dev-000248-self-development-runtime-status into main
CI / test (push) Successful in 54s
2026-08-23 17:19:14 +00:00
m 259e7343e3 fix(work-graph): log task evaluation scheduling rejection
CI / test (push) Successful in 50s
CI / test (pull_request) Successful in 51s
2026-08-23 20:17:08 +03:00
m cdc72e17be Merge pull request 'feat(research): expose truth runtime status' (#224) from codex/dev-000247-research-runtime-status into main
CI / test (push) Successful in 58s
2026-08-23 17:05:32 +00:00
m 931a0b0f0c feat(research): expose truth runtime status
CI / test (push) Successful in 1m5s
CI / test (pull_request) Successful in 47s
2026-08-23 20:03:14 +03:00
m e70be0103a Merge pull request 'feat(work-graph): expose runtime status' (#223) from codex/dev-000246-work-graph-runtime-status into main
CI / test (push) Successful in 59s
2026-08-23 16:59:37 +00:00
m 2353f1f3bb feat(work-graph): expose runtime status
CI / test (push) Successful in 48s
CI / test (pull_request) Successful in 46s
2026-08-23 19:57:32 +03:00
m 355ba7db56 Merge pull request 'feat(agent-worker): expose recovery cycle evidence' (#222) from codex/dev-000245-agent-worker-recovery-status into main
CI / test (push) Successful in 56s
2026-08-23 16:52:17 +00:00
m 4839f139e8 feat(agent-worker): expose recovery cycle evidence
CI / test (push) Successful in 47s
CI / test (pull_request) Successful in 46s
2026-08-23 19:50:19 +03:00
m ea90bf0fab Merge pull request 'feat(reflection): expose worker runtime status' (#221) from codex/dev-000244-reflection-worker-observability into main
CI / test (push) Successful in 58s
2026-08-23 16:44:09 +00:00
m 7978b495d1 feat(reflection): expose worker runtime status
CI / test (push) Successful in 48s
CI / test (pull_request) Successful in 46s
2026-08-23 19:42:07 +03:00
m d73d4fc118 Merge pull request 'feat(capability): consume qualification evidence in selection' (#220) from codex/dev-000243-qualification-selection-evidence into main
CI / test (push) Successful in 1m8s
feat(capability): consume qualification evidence in selection
2026-08-23 16:30:23 +00:00
m c81e2269e1 feat(capability): consume qualification evidence in selection
CI / test (push) Successful in 1m16s
CI / test (pull_request) Successful in 1m26s
2026-08-23 19:28:13 +03:00
m 47327cc6d8 Merge pull request 'feat(capability): persist model qualification observations' (#219) from codex/dev-000242-durable-qualification-observations into main
CI / test (push) Successful in 1m16s
feat(capability): persist model qualification observations
2026-08-23 16:22:58 +00:00
m d0ede46352 feat(capability): persist model qualification observations
CI / test (push) Successful in 1m14s
CI / test (pull_request) Successful in 1m15s
2026-08-23 19:21:04 +03:00
m e4ba1cb5fa Merge pull request 'docs(fleet): record medium and gpu qualification' (#218) from codex/dev-000241-fleet-qualification-evidence into main
CI / test (push) Successful in 1m15s
docs(fleet): record medium and gpu qualification
2026-08-23 16:13:58 +00:00
m 90104d2553 docs(fleet): record medium and gpu qualification
CI / test (push) Successful in 1m12s
CI / test (pull_request) Successful in 1m7s
2026-08-23 19:11:41 +03:00
m ca0d002073 Merge pull request 'docs(fleet): record CPU to lifeboat failover' (#217) from codex/dev-000240-cpu-lifeboat-failover-evidence into main
CI / test (push) Successful in 52s
docs(fleet): record CPU to lifeboat failover
2026-08-23 16:07:31 +00:00
m 20db36f52e docs(fleet): record CPU to lifeboat failover
CI / test (push) Successful in 51s
CI / test (pull_request) Successful in 1m11s
2026-08-23 19:05:49 +03:00
m 383d4c6e4f Merge pull request 'fix(cognitive): resolve providers by stable instance identity' (#216) from codex/dev-000239-stable-provider-resolution into main
CI / test (push) Successful in 50s
fix(cognitive): resolve providers by stable instance identity
2026-08-23 16:03:07 +00:00
m f3ddb45fe5 fix(cognitive): resolve providers by stable instance identity
CI / test (push) Successful in 1m12s
CI / test (pull_request) Successful in 1m12s
2026-08-23 19:01:18 +03:00
m 6061eb5c13 Merge pull request 'docs(runtime): record local fallback gateway evidence' (#215) from codex/dev-000238-local-fallback-evidence into main
CI / test (push) Successful in 1m16s
docs(runtime): record local fallback gateway evidence
2026-08-23 15:54:47 +00:00
m 0e46a64702 docs(runtime): record local fallback gateway evidence
CI / test (push) Successful in 1m15s
CI / test (pull_request) Successful in 47s
2026-08-23 18:52:27 +03:00
m 0609561dfe Merge pull request 'docs(ui): record authenticated console section smoke' (#214) from codex/dev-000237-ui-authenticated-section-smoke into main
CI / test (push) Successful in 1m0s
docs(ui): record authenticated console section smoke
2026-08-23 15:46:29 +00:00
m 1b88a35b82 docs(ui): record authenticated console section smoke
CI / test (push) Successful in 1m13s
CI / test (pull_request) Successful in 1m14s
2026-08-23 18:44:36 +03:00
m be01b6b883 Merge pull request 'fix(cognitive): satisfy qualification advisory contract' (#213) from codex/dev-000236-qualification-locale-fix into main
CI / test (push) Successful in 1m13s
fix(cognitive): satisfy qualification advisory contract
2026-08-23 15:37:10 +00:00
m deaddbca12 fix(cognitive): satisfy qualification advisory contract
CI / test (push) Successful in 1m19s
CI / test (pull_request) Successful in 1m8s
2026-08-23 18:34:45 +03:00
m b20611590a Merge pull request 'feat(cognitive): add bounded model qualification probe' (#212) from codex/dev-000235-model-qualification-probe into main
CI / test (push) Successful in 51s
feat(cognitive): add bounded model qualification probe
2026-08-23 15:30:02 +00:00
150 changed files with 4395 additions and 103 deletions
+197
View File
@@ -0,0 +1,197 @@
// Package developmentexecutor contains the local, read-only verification
// adapter for governed development candidates. It is deliberately not a
// source mutator, promotion authority, secret resolver, or deployment tool.
package developmentexecutor
import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
type CheckKind string
const (
CheckGoTest CheckKind = "GO_TEST"
CheckGoVet CheckKind = "GO_VET"
CheckGitDiffCheck CheckKind = "GIT_DIFF_CHECK"
)
type Check struct {
Kind CheckKind
Packages []string
}
type Plan struct {
Worktree string
WorktreeID string
BaseRepoRevision string
ProtectedMainAccess string
ProductionAccess string
SecretAccess string
Checks []Check
}
type CheckResult struct {
Kind CheckKind `json:"kind"`
Passed bool `json:"passed"`
ExitCode int `json:"exit_code"`
DurationMillis int64 `json:"duration_millis"`
}
type Result struct {
WorktreeID string `json:"worktree_id"`
BaseRepoRevision string `json:"base_repo_revision"`
Passed bool `json:"passed"`
Checks []CheckResult `json:"checks"`
EvidenceRef string `json:"evidence_ref"`
}
var (
ErrInvalidPlan = errors.New("invalid governed verification plan")
ErrUnsupportedCheck = errors.New("unsupported governed verification check")
ErrProtectedWorktree = errors.New("protected worktree is not executable")
)
type CommandRunner interface {
Run(context.Context, string, string, []string) int
}
type OSCommandRunner struct{}
func (OSCommandRunner) Run(ctx context.Context, dir, name string, args []string) int {
command := exec.CommandContext(ctx, name, args...)
command.Dir = dir
command.Stdin = nil
command.Stdout = io.Discard
command.Stderr = io.Discard
command.Env = safeEnvironment()
if err := command.Run(); err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
return exitErr.ExitCode()
}
return -1
}
return 0
}
func ValidatePlan(plan Plan) error {
if strings.TrimSpace(plan.Worktree) == "" || strings.TrimSpace(plan.WorktreeID) == "" || strings.TrimSpace(plan.BaseRepoRevision) == "" || len(plan.Checks) == 0 {
return ErrInvalidPlan
}
if !filepath.IsAbs(plan.Worktree) {
return ErrInvalidPlan
}
if plan.ProtectedMainAccess != "DENIED" || plan.ProductionAccess != "DENIED" || plan.SecretAccess != "DENIED" {
return ErrProtectedWorktree
}
switch strings.ToLower(filepath.Base(filepath.Clean(plan.WorktreeID))) {
case "main", "protected-main", "production":
return ErrProtectedWorktree
}
for _, check := range plan.Checks {
if err := validateCheck(check); err != nil {
return err
}
}
return nil
}
func validateCheck(check Check) error {
switch check.Kind {
case CheckGoTest, CheckGoVet:
if len(check.Packages) == 0 {
return ErrInvalidPlan
}
for _, packagePath := range check.Packages {
if strings.TrimSpace(packagePath) == "" || strings.ContainsAny(packagePath, ";&|`$()<>\r\n") || unsafePackagePath(packagePath) {
return ErrInvalidPlan
}
}
case CheckGitDiffCheck:
if len(check.Packages) != 0 {
return ErrInvalidPlan
}
default:
return ErrUnsupportedCheck
}
return nil
}
func unsafePackagePath(value string) bool {
value = strings.ReplaceAll(value, "\\", "/")
for _, part := range strings.Split(value, "/") {
if part == ".." {
return true
}
}
return strings.HasPrefix(value, "../") || strings.HasPrefix(value, "/") || (len(value) > 1 && value[1] == ':')
}
func Execute(ctx context.Context, plan Plan, runner CommandRunner) (Result, error) {
if err := ValidatePlan(plan); err != nil {
return Result{}, err
}
if runner == nil {
runner = OSCommandRunner{}
}
if _, err := os.Stat(plan.Worktree); err != nil {
return Result{}, fmt.Errorf("worktree unavailable: %w", err)
}
result := Result{WorktreeID: plan.WorktreeID, BaseRepoRevision: plan.BaseRepoRevision, Passed: true, Checks: make([]CheckResult, 0, len(plan.Checks))}
for _, check := range plan.Checks {
started := time.Now()
name, args := commandFor(check)
exitCode := runner.Run(ctx, plan.Worktree, name, args)
passed := exitCode == 0
result.Checks = append(result.Checks, CheckResult{Kind: check.Kind, Passed: passed, ExitCode: exitCode, DurationMillis: time.Since(started).Milliseconds()})
if !passed {
result.Passed = false
}
if ctx.Err() != nil {
result.Passed = false
break
}
}
result.EvidenceRef = evidenceRef(result)
return result, nil
}
func commandFor(check Check) (string, []string) {
switch check.Kind {
case CheckGoTest:
return "go", append([]string{"test"}, check.Packages...)
case CheckGoVet:
return "go", append([]string{"vet"}, check.Packages...)
default:
return "git", []string{"diff", "--check"}
}
}
func evidenceRef(result Result) string {
hash := sha256.New()
_, _ = fmt.Fprintf(hash, "%s|%s|%t|", result.WorktreeID, result.BaseRepoRevision, result.Passed)
for _, check := range result.Checks {
_, _ = fmt.Fprintf(hash, "%s|%t|%d|", check.Kind, check.Passed, check.ExitCode)
}
return "verification:" + hex.EncodeToString(hash.Sum(nil))
}
func safeEnvironment() []string {
allowed := []string{"PATH", "SystemRoot", "TEMP", "TMP", "GOROOT", "GOPATH", "GOMODCACHE"}
result := make([]string, 0, len(allowed))
for _, name := range allowed {
if value, ok := os.LookupEnv(name); ok && value != "" {
result = append(result, name+"="+value)
}
}
return result
}
@@ -0,0 +1,70 @@
package developmentexecutor
import (
"context"
"errors"
"path/filepath"
"testing"
)
type fakeRunner struct {
calls []string
exits []int
}
func (r *fakeRunner) Run(_ context.Context, _ string, name string, args []string) int {
call := name
for _, arg := range args {
call += " " + arg
}
r.calls = append(r.calls, call)
if len(r.exits) == 0 {
return 0
}
exit := r.exits[0]
r.exits = r.exits[1:]
return exit
}
func validPlan(t *testing.T) Plan {
t.Helper()
worktree, err := filepath.Abs(".")
if err != nil {
t.Fatal(err)
}
return Plan{Worktree: worktree, WorktreeID: "sandbox-1", BaseRepoRevision: "revision-1", ProtectedMainAccess: "DENIED", ProductionAccess: "DENIED", SecretAccess: "DENIED", Checks: []Check{{Kind: CheckGoTest, Packages: []string{"./modules/work/..."}}, {Kind: CheckGoVet, Packages: []string{"./modules/work/..."}}, {Kind: CheckGitDiffCheck}}}
}
func TestExecuteRunsOnlyTypedChecksAndReturnsEvidence(t *testing.T) {
runner := &fakeRunner{}
result, err := Execute(context.Background(), validPlan(t), runner)
if err != nil || !result.Passed || len(result.Checks) != 3 || result.EvidenceRef == "" {
t.Fatalf("result=%+v err=%v", result, err)
}
if len(runner.calls) != 3 || runner.calls[0] != "go test ./modules/work/..." || runner.calls[2] != "git diff --check" {
t.Fatalf("calls=%v", runner.calls)
}
}
func TestExecuteFailureCannotBecomeVerificationSuccess(t *testing.T) {
runner := &fakeRunner{exits: []int{1, 0}}
plan := validPlan(t)
plan.Checks = []Check{{Kind: CheckGoTest, Packages: []string{"./..."}}, {Kind: CheckGitDiffCheck}}
result, err := Execute(context.Background(), plan, runner)
if err != nil || result.Passed || result.Checks[0].Passed || result.EvidenceRef == "" {
t.Fatalf("failed result was overclaimed: %+v err=%v", result, err)
}
}
func TestValidatePlanRejectsProtectedAndShellLikeInputs(t *testing.T) {
protected := validPlan(t)
protected.WorktreeID = "production"
if !errors.Is(ValidatePlan(protected), ErrProtectedWorktree) {
t.Fatal("protected worktree accepted")
}
injection := validPlan(t)
injection.Checks = []Check{{Kind: CheckGoTest, Packages: []string{"./...; echo leaked"}}}
if !errors.Is(ValidatePlan(injection), ErrInvalidPlan) {
t.Fatal("shell-like package accepted")
}
}
@@ -505,6 +505,14 @@ func (r *CapabilityRepository) loadCognitiveEvidence(ctx context.Context, value
if err == nil {
value.Performance = &performance
}
var qualification capability.ModelQualificationObservation
err = r.pool.QueryRow(ctx, `SELECT observation_id,provider_instance_id,provider_model_id,status,finish_reason,latency_millis,output_observed,quality_assessed,category,observed_at_unix_millis FROM capability.cognitive_model_qualification_observations WHERE provider_instance_id=$1 AND provider_model_id=$2 ORDER BY observed_at_unix_millis DESC LIMIT 1`, value.Provider.ID, value.Model.ProviderModelID).Scan(&qualification.ID, &qualification.ProviderInstanceID, &qualification.ProviderModelID, &qualification.Status, &qualification.FinishReason, &qualification.LatencyMillis, &qualification.OutputObserved, &qualification.QualityAssessed, &qualification.Category, &qualification.ObservedAtUnixMillis)
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
return err
}
if err == nil {
value.Qualification = &qualification
}
if profileID != "" {
var taskEvaluation capability.TaskEvaluation
err = r.pool.QueryRow(ctx, `SELECT evaluation_id,task_profile_id,binding_id,binding_revision,capability_id,result,quality_score_millis,reliability_score_millis,latency_score_millis,overall_score_millis,critical_failure_millis,benchmark_version,artifact_id,observed_at_unix_millis FROM capability.cognitive_task_evaluations WHERE task_profile_id=$1 AND binding_id=$2 AND binding_revision=$3 ORDER BY observed_at_unix_millis DESC LIMIT 1`, profileID, value.Binding.ID, value.Binding.Revision).Scan(&taskEvaluation.ID, &taskEvaluation.ProfileID, &taskEvaluation.BindingID, &taskEvaluation.BindingRevision, &taskEvaluation.CapabilityID, &taskEvaluation.Result, &taskEvaluation.QualityScoreMillis, &taskEvaluation.ReliabilityScoreMillis, &taskEvaluation.LatencyScoreMillis, &taskEvaluation.OverallScoreMillis, &taskEvaluation.CriticalFailureMillis, &taskEvaluation.BenchmarkVersion, &taskEvaluation.ArtifactID, &taskEvaluation.ObservedAtUnixMillis)
@@ -633,3 +641,8 @@ func (r *CapabilityRepository) SaveTaskEvaluation(ctx context.Context, value cap
_, err := r.pool.Exec(ctx, `INSERT INTO capability.cognitive_task_evaluations (evaluation_id,task_profile_id,binding_id,binding_revision,capability_id,result,quality_score_millis,reliability_score_millis,latency_score_millis,overall_score_millis,critical_failure_millis,benchmark_version,artifact_id,observed_at_unix_millis) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) ON CONFLICT DO NOTHING`, value.ID, value.ProfileID, value.BindingID, value.BindingRevision, value.CapabilityID, value.Result, value.QualityScoreMillis, value.ReliabilityScoreMillis, value.LatencyScoreMillis, value.OverallScoreMillis, value.CriticalFailureMillis, value.BenchmarkVersion, value.ArtifactID, value.ObservedAtUnixMillis)
return err
}
func (r *CapabilityRepository) SaveModelQualificationObservation(ctx context.Context, value capability.ModelQualificationObservation) error {
_, err := r.pool.Exec(ctx, `INSERT INTO capability.cognitive_model_qualification_observations (observation_id,provider_instance_id,provider_model_id,status,finish_reason,latency_millis,output_observed,quality_assessed,category,observed_at_unix_millis) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) ON CONFLICT DO NOTHING`, value.ID, value.ProviderInstanceID, value.ProviderModelID, value.Status, value.FinishReason, value.LatencyMillis, value.OutputObserved, value.QualityAssessed, value.Category, value.ObservedAtUnixMillis)
return err
}
+52 -1
View File
@@ -11,6 +11,14 @@ import (
type EpistemicsRepository struct{ pool *pgxpool.Pool }
type TruthRuntimeStatus struct {
BundleCount int64 `json:"bundle_count"`
ClaimsByVerification map[string]int64 `json:"claims_by_verification"`
BeliefsByState map[string]int64 `json:"beliefs_by_state"`
SourcesByFreshness map[string]int64 `json:"sources_by_freshness"`
ObservedAtUnixMillis int64 `json:"observed_at_unix_millis"`
}
func OpenEpistemics(ctx context.Context, config ContinuityConfig) (*EpistemicsRepository, error) {
pool, err := openPool(ctx, config)
if err != nil {
@@ -25,6 +33,49 @@ func (r *EpistemicsRepository) Ping(ctx context.Context) error {
return r.pool.Ping(ctx)
}
// TruthRuntimeStatus is a read-only projection of durable truth bundles.
// It does not promote claims, revise beliefs, or mutate epistemic state.
func (r *EpistemicsRepository) TruthRuntimeStatus(ctx context.Context) (TruthRuntimeStatus, error) {
status := TruthRuntimeStatus{
ClaimsByVerification: map[string]int64{},
BeliefsByState: map[string]int64{},
SourcesByFreshness: map[string]int64{},
ObservedAtUnixMillis: time.Now().UnixMilli(),
}
if err := r.pool.QueryRow(ctx, `SELECT count(*) FROM epistemics.truth_bundles`).Scan(&status.BundleCount); err != nil {
return TruthRuntimeStatus{}, err
}
queries := []struct {
query string
destination map[string]int64
}{
{`SELECT claim_verification, count(*) FROM epistemics.truth_bundles GROUP BY claim_verification`, status.ClaimsByVerification},
{`SELECT belief_state, count(*) FROM epistemics.truth_bundles GROUP BY belief_state`, status.BeliefsByState},
{`SELECT snapshot_freshness, count(*) FROM epistemics.truth_bundles GROUP BY snapshot_freshness`, status.SourcesByFreshness},
}
for _, item := range queries {
rows, err := r.pool.Query(ctx, item.query)
if err != nil {
return TruthRuntimeStatus{}, err
}
for rows.Next() {
var state string
var count int64
if err := rows.Scan(&state, &count); err != nil {
rows.Close()
return TruthRuntimeStatus{}, err
}
item.destination[state] = count
}
if err := rows.Err(); err != nil {
rows.Close()
return TruthRuntimeStatus{}, err
}
rows.Close()
}
return status, nil
}
func (r *EpistemicsRepository) SaveSystemExpectation(ctx context.Context, value epistemics.SystemExpectation) error {
_, err := r.pool.Exec(ctx, `INSERT INTO epistemics.system_expectations (
expectation_id, original_intent_id, source_utterance_id, expected_outcome, expected_effect,
@@ -99,7 +150,7 @@ func (r *EpistemicsRepository) ClaimReflectionCandidate(ctx context.Context, wor
var value epistemics.ReflectionCandidate
err = tx.QueryRow(ctx, `SELECT reflection_candidate_id,trigger,state,source_experience_id,original_intent_id,source_utterance_id,summary,evidence_refs,requires_cognition,recorded_at_unix_millis,revision,claimed_by,claim_expires_at_unix_millis
FROM epistemics.reflection_candidates
WHERE state='OPEN' AND (claim_expires_at_unix_millis=0 OR claim_expires_at_unix_millis < $1)
WHERE state='OPEN' AND requires_cognition=false AND (claim_expires_at_unix_millis=0 OR claim_expires_at_unix_millis < $1)
ORDER BY recorded_at_unix_millis,reflection_candidate_id
FOR UPDATE SKIP LOCKED LIMIT 1`, nowUnixMillis).Scan(&value.ID, &value.Trigger, &value.State, &value.SourceExperienceID, &value.OriginalIntentID, &value.SourceUtteranceID, &value.Summary, &value.EvidenceRefs, &value.RequiresCognition, &value.RecordedAtUnixMillis, &value.Revision, &value.ClaimedBy, &value.ClaimExpiresAtUnixMillis)
if err != nil {
+48
View File
@@ -32,6 +32,54 @@ func OpenWork(ctx context.Context, config ContinuityConfig) (*WorkRepository, er
}
return &WorkRepository{pool}, nil
}
// WorkGraphRuntimeStatus is a read-only operational projection owned by Work.
// It is not canonical graph state and cannot advance any lifecycle.
type WorkGraphRuntimeStatus struct {
GraphsByState map[string]int64 `json:"graphs_by_state"`
NodesByState map[string]int64 `json:"nodes_by_state"`
QueueByState map[string]int64 `json:"queue_by_state"`
ObservedAtUnixMillis int64 `json:"observed_at_unix_millis"`
}
func (r *WorkRepository) RuntimeStatus(ctx context.Context) (WorkGraphRuntimeStatus, error) {
status := WorkGraphRuntimeStatus{
GraphsByState: map[string]int64{},
NodesByState: map[string]int64{},
QueueByState: map[string]int64{},
ObservedAtUnixMillis: time.Now().UnixMilli(),
}
queries := []struct {
query string
destination map[string]int64
}{
{`SELECT state, count(*) FROM work.graphs GROUP BY state`, status.GraphsByState},
{`SELECT state, count(*) FROM work.graph_nodes GROUP BY state`, status.NodesByState},
{`SELECT state, count(*) FROM work.agent_dispatch_queue GROUP BY state`, status.QueueByState},
}
for _, item := range queries {
rows, err := r.pool.Query(ctx, item.query)
if err != nil {
return WorkGraphRuntimeStatus{}, err
}
for rows.Next() {
var state string
var count int64
if err := rows.Scan(&state, &count); err != nil {
rows.Close()
return WorkGraphRuntimeStatus{}, err
}
item.destination[state] = count
}
if err := rows.Err(); err != nil {
rows.Close()
return WorkGraphRuntimeStatus{}, err
}
rows.Close()
}
return status, nil
}
func (r *WorkRepository) Close() { r.pool.Close() }
func (r *WorkRepository) Ping(ctx context.Context) error { return r.pool.Ping(ctx) }
func (r *WorkRepository) Create(ctx context.Context, target work.TaskTarget) error {
+150
View File
@@ -0,0 +1,150 @@
package research
import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"html"
"io"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"git.cin.su/m/Assistant/contracts/epistemics"
)
var ErrSearchRejected = errors.New("web search request rejected")
var (
searchLinkPattern = regexp.MustCompile(`(?is)<a[^>]+class=["'][^"']*result__a[^"']*["'][^>]*>(.*?)</a>`)
searchSnippetPattern = regexp.MustCompile(`(?is)<a[^>]+class=["'][^"']*result__snippet[^"']*["'][^>]*>(.*?)</a>`)
bingLinkPattern = regexp.MustCompile(`(?is)<li[^>]+class=["'][^"']*b_algo[^"']*["'][^>]*>.*?<h2[^>]*><a[^>]*>(.*?)</a>`)
bingSnippetPattern = regexp.MustCompile(`(?is)<li[^>]+class=["'][^"']*b_algo[^"']*["'][^>]*>.*?<p[^>]*>(.*?)</p>`)
tagPattern = regexp.MustCompile(`(?s)<[^>]*>`)
termPattern = regexp.MustCompile(`(?i)[\p{L}\p{N}]{2,}`)
)
var searchStopTerms = map[string]struct{}{
"какая": {}, "какое": {}, "какие": {}, "какой": {}, "что": {}, "как": {}, "где": {}, "когда": {}, "почему": {}, "мне": {}, "пожалуйста": {},
"what": {}, "which": {}, "where": {}, "when": {}, "why": {}, "how": {}, "please": {}, "the": {}, "this": {},
}
// SearchConnector uses a configured HTTPS search endpoint. It does not know
// cities, weather terms, entities or model names: the complete semantic query
// comes from the interpretation layer.
type SearchConnector struct {
Client *http.Client
EndpointTemplate string
AllowedHosts map[string]struct{}
MaxBytes int64
MaxResults int
}
func (c SearchConnector) Collect(ctx context.Context, request epistemics.WebSearchRequest, nowUnixMillis int64) ([]epistemics.ResearchCandidate, error) {
if request.Plan.Validate() != nil || strings.TrimSpace(request.Query) == "" || nowUnixMillis < 1 || !strings.Contains(c.EndpointTemplate, "%s") {
return nil, ErrSearchRejected
}
endpoint, err := url.Parse(strings.Replace(c.EndpointTemplate, "%s", url.QueryEscape(strings.TrimSpace(request.Query)), 1))
if err != nil || endpoint.Scheme != "https" || endpoint.Host == "" || !c.allowedHost(strings.ToLower(endpoint.Hostname())) {
return nil, ErrSearchRejected
}
client := c.Client
if client == nil {
client = &http.Client{}
}
maxBytes := c.MaxBytes
if maxBytes <= 0 || maxBytes > 4<<20 {
maxBytes = 512 << 10
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, ErrSearchRejected
}
response, err := client.Do(req)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
return nil, ErrSearchRejected
}
body, err := io.ReadAll(io.LimitReader(response.Body, maxBytes+1))
if err != nil || len(body) == 0 || int64(len(body)) > maxBytes {
return nil, ErrSearchRejected
}
links := searchLinkPattern.FindAllSubmatch(body, c.limit())
snippets := searchSnippetPattern.FindAllSubmatch(body, c.limit())
if len(links) == 0 {
links = bingLinkPattern.FindAllSubmatch(body, c.limit())
snippets = bingSnippetPattern.FindAllSubmatch(body, c.limit())
}
if len(links) == 0 {
return nil, nil
}
digest := sha256.Sum256(body)
digestText := "sha256:" + hex.EncodeToString(digest[:])
base := hex.EncodeToString(digest[:])
sourceID := epistemics.SourceID("source:search:" + base)
snapshotID := epistemics.SourceSnapshotID("snapshot:search:" + base)
results := make([]epistemics.ResearchCandidate, 0, len(links))
for index, link := range links {
title := cleanHTML(string(link[1]))
snippet := ""
if index < len(snippets) {
snippet = cleanHTML(string(snippets[index][1]))
}
statement := strings.TrimSpace(strings.Trim(title+" — "+snippet, " —"))
if statement == "" || !searchStatementRelatesToQuery(request.Query, statement) {
continue
}
ordinal := strconv.Itoa(index)
excerptID := epistemics.SourceExcerptID("excerpt:search:" + base + ":" + ordinal)
results = append(results, epistemics.ResearchCandidate{
PropositionKey: request.PropositionKey + ":result:" + ordinal,
Statement: statement,
Source: epistemics.Source{ID: sourceID, Kind: "web-search", Locator: endpoint.String(), Title: title, Publisher: endpoint.Hostname()},
Snapshot: epistemics.SourceSnapshot{ID: snapshotID, SourceID: sourceID, ContentDigest: digestText, CapturedAtUnixMillis: nowUnixMillis, Freshness: epistemics.FreshnessCurrent},
Excerpt: epistemics.SourceExcerpt{ID: excerptID, SnapshotID: snapshotID, Text: statement, Start: 0, End: int64(len(statement))},
Evidence: epistemics.EvidenceObserved, Coverage: epistemics.CoverageComplete, Conflict: epistemics.ConflictNone,
})
}
return results, nil
}
// searchStatementRelatesToQuery is a bounded relevance gate, not a truth
// check. It prevents generic search pages from becoming apparent evidence
// when their content has no substantive relation to the requested query.
func searchStatementRelatesToQuery(query, statement string) bool {
terms := termPattern.FindAllString(strings.ToLower(query), -1)
content := strings.ToLower(statement)
usable := 0
for _, term := range terms {
if _, stop := searchStopTerms[term]; stop || len([]rune(term)) < 4 {
continue
}
usable++
runes := []rune(term)
prefixLen := 5
if len(runes) < prefixLen {
prefixLen = len(runes)
}
if strings.Contains(content, string(runes[:prefixLen])) {
return true
}
}
return usable == 0
}
func (c SearchConnector) allowedHost(host string) bool { _, ok := c.AllowedHosts[host]; return ok }
func (c SearchConnector) limit() int {
if c.MaxResults <= 0 || c.MaxResults > 8 {
return 3
}
return c.MaxResults
}
func cleanHTML(value string) string {
return strings.Join(strings.Fields(html.UnescapeString(tagPattern.ReplaceAllString(value, ""))), " ")
}
+211
View File
@@ -0,0 +1,211 @@
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"
"git.cin.su/m/Assistant/contracts/artifact"
"git.cin.su/m/Assistant/contracts/capability"
"git.cin.su/m/Assistant/contracts/cognitive"
"git.cin.su/m/Assistant/contracts/work"
capabilitydomain "git.cin.su/m/Assistant/modules/capability/domain"
)
type bridge struct {
cognitiveURL string
callbackURL string
verifierURL string
client *http.Client
}
func main() {
b := bridge{
cognitiveURL: strings.TrimRight(envOr("COGNITIVE_WORKER_URL", "http://cognitive-worker:8083"), "/"),
callbackURL: strings.TrimRight(envOr("WORK_GRAPH_RUNTIME_URL", "http://work-graph-runtime:8088"), "/"),
verifierURL: strings.TrimRight(envOr("TASK_VERIFIER_URL", "http://task-verifier:8091"), "/"),
client: &http.Client{Timeout: 10 * time.Minute},
}
mux := http.NewServeMux()
mux.HandleFunc("/healthz", healthHandler)
mux.HandleFunc("/readyz", healthHandler)
mux.HandleFunc("/v1/execute", b.executeHandler)
addr := envOr("LISTEN_ADDR", ":8092")
if err := http.ListenAndServe(addr, mux); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func (b bridge) executeHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var request work.AgentExecutionRequest
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&request); err != nil {
http.Error(w, "invalid execution request", http.StatusBadRequest)
return
}
result, err := b.execute(r.Context(), request)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(result)
}
func (b bridge) execute(ctx context.Context, request work.AgentExecutionRequest) (work.AgentExecutionResult, error) {
now := time.Now().UnixMilli()
intent := request.Intent
if intent.TaskEvaluationExecution != nil && intent.TaskEvaluationExecution.GenericMessage != "" {
return b.executeGenericCognitiveWork(ctx, request, *intent.TaskEvaluationExecution)
}
if intent.TaskEvaluationExecution == nil || intent.TaskEvaluationProfile == nil || len(intent.TaskEvaluationCases) == 0 {
return work.AgentExecutionResult{State: work.AgentExecutionWaiting, Summary: "локальный bridge не поддерживает это намерение; работа сохранена и ожидает подходящего исполнителя", ObservedAtUnixMillis: now}, nil
}
execution := intent.TaskEvaluationExecution
if execution.GraphID == "" || execution.NodeID == "" || execution.BindingRevision < 1 || execution.ActorID == "" || execution.PrincipalID == "" || execution.ScopeID == "" {
return work.AgentExecutionResult{State: work.AgentExecutionWaiting, Summary: "контекст task-evaluation неполон; работа сохранена для повторной маршрутизации", ObservedAtUnixMillis: now}, nil
}
payload := capability.TaskEvaluationExecutionRequest{Profile: *intent.TaskEvaluationProfile, CapabilityID: intent.TaskEvaluationProfile.CapabilityID, BindingID: intent.TaskEvaluationBindingID, BindingRevision: execution.BindingRevision, ActorID: execution.ActorID, PrincipalID: execution.PrincipalID, ScopeID: execution.ScopeID, Cases: intent.TaskEvaluationCases, MaxOutputBytes: 4096}
response, err := postJSON[capability.TaskEvaluationExecutionResponse](ctx, b.client, b.cognitiveURL+"/v1/task-evaluations/execute", payload)
if err != nil {
return work.AgentExecutionResult{}, err
}
if len(response.Responses) == 0 || response.ObservedAtUnixMillis < 1 {
return work.AgentExecutionResult{}, fmt.Errorf("task evaluation returned no observations")
}
caseByID := make(map[string]capability.TaskEvaluationCase, len(intent.TaskEvaluationCases))
for _, item := range intent.TaskEvaluationCases {
caseByID[item.CaseID] = item
}
observations := response.Observations
if !hasCriticalFailure(observations) {
observations = make([]capability.TaskCaseObservation, 0, len(response.Responses))
for _, item := range response.Responses {
caseValue, ok := caseByID[item.CaseID]
if !ok || caseValue.Verification.Mode == "" {
return work.AgentExecutionResult{State: work.AgentExecutionWaiting, Summary: "task-evaluation case has no bounded verifier; work remains waiting", ObservedAtUnixMillis: time.Now().UnixMilli()}, nil
}
verified, verifyErr := postJSON[capability.TaskCaseVerificationResponse](ctx, b.client, b.verifierURL+"/v1/task-evaluations/verify", capability.TaskCaseVerificationRequest{CaseID: item.CaseID, ResponseText: item.ResponseText, LatencyMicros: item.LatencyMicros, EvidenceArtifactRef: item.EvidenceArtifactRef, Verification: caseValue.Verification})
if verifyErr != nil {
return work.AgentExecutionResult{}, verifyErr
}
observations = append(observations, capability.TaskCaseObservation{CaseID: verified.CaseID, Result: verified.Result, QualityScoreMillis: verified.QualityScoreMillis, LatencyMicros: verified.LatencyMicros, CriticalFailure: verified.CriticalFailure, EvidenceArtifactRef: verified.EvidenceArtifactRef, VerificationEvidenceRef: verified.VerificationEvidenceRef, VerifierIdentity: verified.VerifierIdentity})
}
}
evaluation, err := capabilitydomain.BuildTaskEvaluation(*intent.TaskEvaluationProfile, capability.TaskEvaluationID("evaluation:"+intent.DispatchID), response.BindingID, response.BindingRevision, intent.TaskEvaluationProfile.CapabilityID, observations, artifact.ArtifactID(intent.TaskEvaluationCases[0].EvidenceArtifactRef), response.ObservedAtUnixMillis)
if err != nil {
return work.AgentExecutionResult{}, err
}
callback := work.TaskEvaluationEvidenceCallback{GraphID: execution.GraphID, Revision: execution.GraphRevision, NodeID: execution.NodeID, Evaluation: evaluation, Succeeded: true}
if _, err := postJSON[json.RawMessage](ctx, b.client, b.callbackURL+"/v1/work-graphs/task-evaluations/callback", callback); err != nil {
return work.AgentExecutionResult{}, err
}
return work.AgentExecutionResult{State: work.AgentExecutionCompleted, Summary: "local bridge executed task evaluation and persisted verifier callback", OutcomeRef: string(evaluation.ID), ArtifactRefs: []string{string(evaluation.ArtifactID)}, ObservedAtUnixMillis: time.Now().UnixMilli()}, nil
}
func (b bridge) executeGenericCognitiveWork(ctx context.Context, request work.AgentExecutionRequest, execution work.TaskEvaluationDispatchContext) (work.AgentExecutionResult, error) {
if execution.GraphID == "" || execution.NodeID == "" || execution.ActorID == "" || execution.PrincipalID == "" || execution.ScopeID == "" {
return work.AgentExecutionResult{State: work.AgentExecutionWaiting, Summary: "generic cognitive work context is incomplete; durable work remains waiting for rerouting", ObservedAtUnixMillis: time.Now().UnixMilli()}, nil
}
maxOutputBytes := execution.GenericMaxOutputBytes
if maxOutputBytes < 1 {
maxOutputBytes = 4096
}
dataPolicy := capability.DataPolicy(execution.GenericDataPolicy)
if dataPolicy == "" {
dataPolicy = capability.DataPolicyLocalOnly
}
response, err := postJSON[cognitive.GatewayAdvisoryResponse](ctx, b.client, b.cognitiveURL+"/v1/advisories", cognitive.GatewayAdvisoryRequest{
Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID("agent-work:" + request.Intent.DispatchID), ActorID: execution.ActorID,
PrincipalID: execution.PrincipalID, ScopeID: execution.ScopeID, Message: execution.GenericMessage, Locale: execution.GenericLocale,
MaxOutputBytes: maxOutputBytes, DataPolicy: dataPolicy, Meaning: cognitive.TaskMeaning{SpeechAct: "DIRECTIVE", RequestedOutcome: execution.GenericPurpose},
})
if err != nil {
return work.AgentExecutionResult{}, err
}
if response.FinishReason != cognitive.FinishCompleted || strings.TrimSpace(response.AdvisoryText) == "" {
return work.AgentExecutionResult{}, fmt.Errorf("generic cognitive work did not complete: %s", response.FinishReason)
}
candidateID := "candidate:" + request.Intent.DispatchID
artifactRef := work.EvidenceRef("agent-output:" + request.Intent.DispatchID)
verification, err := postJSON[work.GenericCandidateVerificationResponse](ctx, b.client, b.verifierURL+"/v1/work-graphs/candidates/verify", work.GenericCandidateVerificationRequest{
GraphID: execution.GraphID, GraphRevision: execution.GraphRevision, NodeID: execution.NodeID, AuthorRunID: request.Intent.RunID, CandidateID: candidateID, Summary: response.AdvisoryText, EvidenceRefs: []work.EvidenceRef{artifactRef},
})
if err != nil {
return work.AgentExecutionResult{}, err
}
if !verification.Verified || verification.VerificationEvidenceRef == "" || verification.VerifierIdentity == "" {
return work.AgentExecutionResult{State: work.AgentExecutionWaiting, Summary: "generic candidate envelope was not independently verified; work remains waiting", ObservedAtUnixMillis: time.Now().UnixMilli()}, nil
}
candidate := work.GraphResultCallback{GraphID: execution.GraphID, Revision: execution.GraphRevision, Candidate: work.ResultCandidate{
ID: candidateID, GraphID: execution.GraphID, GraphRevision: execution.GraphRevision, NodeID: execution.NodeID,
AuthorRunID: request.Intent.RunID, ArtifactRefs: []work.EvidenceRef{artifactRef, work.EvidenceRef(verification.VerificationEvidenceRef)}, EvidenceSummary: "candidate attribution and evidence envelope independently verified; semantic content remains unverified",
Summary: response.AdvisoryText,
}}
if _, err := postJSON[json.RawMessage](ctx, b.client, b.callbackURL+"/v1/work-graphs/result-callback", candidate); err != nil {
return work.AgentExecutionResult{}, err
}
return work.AgentExecutionResult{State: work.AgentExecutionCompleted, Summary: "local cognitive specialist produced a candidate for independent verification", OutcomeRef: "candidate:" + request.Intent.DispatchID, ArtifactRefs: []string{"agent-output:" + request.Intent.DispatchID}, ObservedAtUnixMillis: time.Now().UnixMilli()}, nil
}
func hasCriticalFailure(observations []capability.TaskCaseObservation) bool {
for _, observation := range observations {
if observation.CriticalFailure {
return true
}
}
return false
}
func postJSON[T any](ctx context.Context, client *http.Client, url string, payload any) (T, error) {
var result T
body, err := json.Marshal(payload)
if err != nil {
return result, err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
if err != nil {
return result, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return result, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return result, fmt.Errorf("POST %s: status %s", url, resp.Status)
}
if resp.StatusCode == http.StatusNoContent {
return result, nil
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return result, err
}
return result, nil
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"runtime":"agent-execution-bridge","status":"ok","capabilities":["task-evaluation","generic-cognitive-work"]}`))
}
func envOr(name, fallback string) string {
if value := strings.TrimSpace(os.Getenv(name)); value != "" {
return value
}
return fallback
}
+88
View File
@@ -0,0 +1,88 @@
package main
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"git.cin.su/m/Assistant/contracts/capability"
"git.cin.su/m/Assistant/contracts/cognitive"
"git.cin.su/m/Assistant/contracts/work"
)
func TestBridgeWaitsForUnsupportedIntent(t *testing.T) {
b := bridge{client: http.DefaultClient}
result, err := b.execute(context.Background(), work.AgentExecutionRequest{Intent: work.AgentDispatchIntent{DispatchID: "dispatch-unsupported"}})
if err != nil {
t.Fatal(err)
}
if result.State != work.AgentExecutionWaiting || result.ObservedAtUnixMillis < 1 {
t.Fatalf("result=%+v", result)
}
}
func TestHealthHandlerExposesBoundedCapability(t *testing.T) {
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/healthz", nil)
healthHandler(recorder, request)
if recorder.Code != http.StatusOK || recorder.Body.String() == "" {
t.Fatalf("status=%d body=%q", recorder.Code, recorder.Body.String())
}
}
func TestBridgeRunsTaskEvaluationThroughVerifierAndCallback(t *testing.T) {
profile := capability.TaskEvaluationProfile{ID: "profile-1", Revision: 1, CapabilityID: "capability-1", Stage: capability.TaskEvaluationGeneral, BenchmarkVersion: "general-v1", RequiredCaseIDs: []string{"case-1"}, QualityWeightMillis: 600, ReliabilityWeightMillis: 300, LatencyWeightMillis: 100}
verification := capability.TaskCaseVerification{Mode: capability.TaskCaseVerificationExactText, ExpectedText: "answer", MinimumQualityMillis: 900}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/v1/task-evaluations/execute":
_ = json.NewEncoder(w).Encode(capability.TaskEvaluationExecutionResponse{ProfileID: profile.ID, BindingID: "binding-1", BindingRevision: 3, Responses: []capability.TaskCaseResponse{{CaseID: "case-1", ResponseText: "answer", LatencyMicros: 2, EvidenceArtifactRef: "artifact-1"}}, ObservedAtUnixMillis: 10})
case "/v1/task-evaluations/verify":
_ = json.NewEncoder(w).Encode(capability.TaskCaseVerificationResponse{CaseID: "case-1", Result: capability.EvaluationSupported, QualityScoreMillis: 900, LatencyMicros: 2, EvidenceArtifactRef: "artifact-1", VerificationEvidenceRef: "verify-1", VerifierIdentity: "verifier-1"})
case "/v1/work-graphs/task-evaluations/callback":
w.WriteHeader(http.StatusNoContent)
default:
http.NotFound(w, r)
}
}))
defer server.Close()
b := bridge{cognitiveURL: server.URL, callbackURL: server.URL, verifierURL: server.URL, client: server.Client()}
result, err := b.execute(context.Background(), work.AgentExecutionRequest{Intent: work.AgentDispatchIntent{DispatchID: "dispatch-1", TaskEvaluationBindingID: "binding-1", TaskEvaluationProfile: &profile, TaskEvaluationCases: []capability.TaskEvaluationCase{{CaseID: "case-1", Prompt: "answer", EvidenceArtifactRef: "artifact-1", Verification: verification}}, TaskEvaluationExecution: &work.TaskEvaluationDispatchContext{GraphID: "graph-1", GraphRevision: 1, NodeID: "node-1", BindingRevision: 3, ActorID: "actor-1", PrincipalID: "principal-1", ScopeID: "scope-1"}}})
if err != nil || result.State != work.AgentExecutionCompleted || result.OutcomeRef == "" {
t.Fatalf("result=%+v err=%v", result, err)
}
}
func TestBridgeRunsGenericCognitiveWorkAsUnverifiedGraphCandidate(t *testing.T) {
var callback work.GraphResultCallback
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/v1/advisories":
var request cognitive.GatewayAdvisoryRequest
if err := json.NewDecoder(r.Body).Decode(&request); err != nil || request.Message != "find the bounded solution" {
t.Fatalf("unexpected advisory request: %+v err=%v", request, err)
}
_ = json.NewEncoder(w).Encode(cognitive.GatewayAdvisoryResponse{Version: cognitive.CreatorAdvisoryV1, RequestID: request.RequestID, AdvisoryText: "candidate answer", FinishReason: cognitive.FinishCompleted})
case "/v1/work-graphs/result-callback":
if err := json.NewDecoder(r.Body).Decode(&callback); err != nil {
t.Fatal(err)
}
w.WriteHeader(http.StatusNoContent)
case "/v1/work-graphs/candidates/verify":
_ = json.NewEncoder(w).Encode(work.GenericCandidateVerificationResponse{Verified: true, VerificationEvidenceRef: "verification:generic-1", VerifierIdentity: "deterministic-generic-envelope-verifier-v1", Summary: "envelope verified"})
default:
http.NotFound(w, r)
}
}))
defer server.Close()
b := bridge{cognitiveURL: server.URL, callbackURL: server.URL, verifierURL: server.URL, client: server.Client()}
result, err := b.execute(context.Background(), work.AgentExecutionRequest{Intent: work.AgentDispatchIntent{
DispatchID: "dispatch-generic", RunID: "run-generic",
TaskEvaluationExecution: &work.TaskEvaluationDispatchContext{GraphID: "graph-1", GraphRevision: 2, NodeID: "node-1", ActorID: "actor-1", PrincipalID: "principal-1", ScopeID: "scope-1", GenericMessage: "find the bounded solution", GenericPurpose: "investigate", GenericDataPolicy: string(capability.DataPolicyLocalOnly)},
}})
if err != nil || result.State != work.AgentExecutionCompleted || callback.Candidate.Summary != "candidate answer" || callback.Candidate.AuthorRunID != "run-generic" || len(callback.Candidate.ArtifactRefs) != 2 {
t.Fatalf("result=%+v callback=%+v err=%v", result, callback, err)
}
}
+25 -2
View File
@@ -42,7 +42,7 @@ func main() {
defer repository.Close()
var executor ports.AgentExecutor
localFallback := false
if bridgeURL, bridgeConfigured := loadProtectedBridgeURL(); bridgeConfigured {
if bridgeURL, bridgeConfigured := loadConfiguredBridgeURL(); bridgeConfigured {
executor, err = agentbridge.NewHTTPExecutor(bridgeURL, &http.Client{Timeout: *leaseTTL})
if err != nil {
fmt.Fprintln(os.Stderr, err)
@@ -56,13 +56,15 @@ func main() {
executor = newTaskEvaluationExecutor(envOr("COGNITIVE_WORKER_URL", "http://cognitive-worker:8083"), envOr("WORK_GRAPH_RUNTIME_URL", "http://work-graph-runtime:8088"), envOr("TASK_VERIFIER_URL", "http://task-verifier:8091"), &http.Client{Timeout: *leaseTTL})
fmt.Fprintln(os.Stderr, "protected agent execution bridge URL is not configured; task evaluations use the local durable evaluator, other work remains in fallback")
}
runtimeStatus := newAgentWorkerRuntimeStatus()
go func() {
if serveErr := http.ListenAndServe(*statusAddr, workerStatusHandler(executor != nil && !localFallback, localFallback)); serveErr != nil {
if serveErr := http.ListenAndServe(*statusAddr, workerStatusHandler(executor != nil && !localFallback, localFallback, runtimeStatus)); serveErr != nil {
fmt.Fprintln(os.Stderr, "agent worker status server:", serveErr)
}
}()
ctx := context.Background()
for {
runtimeStatus.cycleStarted(time.Now())
var result application.WorkerCycleResult
var cycleErr error
if executor == nil {
@@ -71,11 +73,19 @@ func main() {
result, cycleErr = application.ExecuteWorkerCycleWithExecutor(ctx, repository, executor, *workerID, work.AgentFencingToken(1), time.Now().UTC(), *staleAfter, *leaseTTL)
}
if cycleErr != nil {
runtimeStatus.cycleFailed(time.Now(), cycleErr)
fmt.Fprintln(os.Stderr, cycleErr)
if *once {
os.Exit(1)
}
}
cycleResult := "idle"
if result.Completed {
cycleResult = "completed"
} else if result.RecoveredClaims > 0 {
cycleResult = "recovered"
}
runtimeStatus.cycleFinished(time.Now(), result.RecoveredClaims, result.DispatchID, cycleResult)
if cycleErr == nil && (result.Completed || result.RecoveredClaims > 0) {
_ = json.NewEncoder(os.Stdout).Encode(result)
}
@@ -89,6 +99,19 @@ func main() {
}
}
func loadConfiguredBridgeURL() (string, bool) {
if bridgeURL, configured := loadProtectedBridgeURL(); configured {
return bridgeURL, true
}
// The URL is routing configuration, not a credential. A protected file
// remains preferred when an independently operated bridge is supplied.
value := strings.TrimSpace(os.Getenv("AGENT_EXECUTION_BRIDGE_URL"))
if value == "" || !strings.HasPrefix(value, "http://") && !strings.HasPrefix(value, "https://") {
return "", false
}
return value, true
}
func loadProtectedBridgeURL() (string, bool) {
return loadProtectedBridgeURLFrom("/run/secrets", "/run/secrets/agent_execution_bridge_url")
}
+97 -19
View File
@@ -3,22 +3,87 @@ package main
import (
"encoding/json"
"net/http"
"sync"
"time"
)
type workerStatusResponse struct {
Runtime string `json:"runtime"`
ExecutorConfigured bool `json:"executor_configured"`
Mode string `json:"mode"`
Ready bool `json:"ready"`
Reason string `json:"reason"`
LocalFallback bool `json:"local_fallback"`
LocalExecutor bool `json:"local_executor"`
Capabilities []string `json:"capabilities,omitempty"`
Runtime string `json:"runtime"`
ExecutorConfigured bool `json:"executor_configured"`
Mode string `json:"mode"`
Ready bool `json:"ready"`
Reason string `json:"reason"`
LocalFallback bool `json:"local_fallback"`
LocalExecutor bool `json:"local_executor"`
Capabilities []string `json:"capabilities,omitempty"`
StartedAt time.Time `json:"started_at,omitempty"`
LastCycleAt time.Time `json:"last_cycle_at,omitempty"`
LastCompletedAt time.Time `json:"last_completed_at,omitempty"`
LastRecoveredClaims int64 `json:"last_recovered_claims"`
TotalRecoveredClaims int64 `json:"total_recovered_claims"`
LastDispatchID string `json:"last_dispatch_id,omitempty"`
LastResult string `json:"last_result,omitempty"`
LastError string `json:"last_error,omitempty"`
}
func workerStatusHandler(executorConfigured bool, localFallback ...bool) http.Handler {
local := len(localFallback) > 0 && localFallback[0]
status := workerStatusResponse{
type agentWorkerRuntimeStatus struct {
mu sync.RWMutex
startedAt time.Time
lastCycleAt time.Time
lastCompletedAt time.Time
lastRecoveredClaims int64
totalRecoveredClaims int64
lastDispatchID string
lastResult string
lastError string
}
func newAgentWorkerRuntimeStatus() *agentWorkerRuntimeStatus {
return &agentWorkerRuntimeStatus{startedAt: time.Now().UTC()}
}
func (s *agentWorkerRuntimeStatus) cycleStarted(at time.Time) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCycleAt = at.UTC()
s.lastResult = "running"
s.lastError = ""
}
func (s *agentWorkerRuntimeStatus) cycleFinished(at time.Time, recoveredClaims int64, dispatchID string, result string) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCompletedAt = at.UTC()
s.lastRecoveredClaims = recoveredClaims
s.totalRecoveredClaims += recoveredClaims
s.lastDispatchID = dispatchID
s.lastResult = result
s.lastError = ""
}
func (s *agentWorkerRuntimeStatus) cycleFailed(at time.Time, err error) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCompletedAt = at.UTC()
s.lastResult = "failed"
s.lastError = err.Error()
}
func workerStatusHandler(executorConfigured bool, localFallback ...interface{}) http.Handler {
local := false
var runtime *agentWorkerRuntimeStatus
for _, argument := range localFallback {
switch value := argument.(type) {
case bool:
local = value
case *agentWorkerRuntimeStatus:
runtime = value
}
}
if runtime == nil {
runtime = newAgentWorkerRuntimeStatus()
}
baseStatus := workerStatusResponse{
Runtime: "agent-worker",
ExecutorConfigured: executorConfigured,
Mode: "recovery-only",
@@ -28,14 +93,16 @@ func workerStatusHandler(executorConfigured bool, localFallback ...bool) http.Ha
LocalExecutor: local,
}
if local {
status.Mode = "local-executor"
status.Reason = "bounded local task-evaluation executor is active; unsupported work remains durably waiting"
status.Capabilities = []string{"task-evaluation"}
baseStatus.Mode = "local-executor"
baseStatus.Ready = true
baseStatus.Reason = "bounded local task-evaluation capability is ready; unsupported work remains durably waiting"
baseStatus.Capabilities = []string{"task-evaluation"}
}
if executorConfigured {
status.Mode = "execution"
status.Ready = true
status.Reason = "typed agent execution bridge configured"
baseStatus.Mode = "execution"
baseStatus.Ready = true
baseStatus.Reason = "typed agent execution bridge configured"
baseStatus.Capabilities = []string{"task-evaluation", "generic-cognitive-work"}
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet || (r.URL.Path != "/healthz" && r.URL.Path != "/readyz" && r.URL.Path != "/status") {
@@ -43,9 +110,20 @@ func workerStatusHandler(executorConfigured bool, localFallback ...bool) http.Ha
return
}
w.Header().Set("Content-Type", "application/json")
if r.URL.Path == "/readyz" && !status.Ready {
if r.URL.Path == "/readyz" && !baseStatus.Ready {
w.WriteHeader(http.StatusServiceUnavailable)
}
_ = json.NewEncoder(w).Encode(status)
response := baseStatus
runtime.mu.RLock()
response.StartedAt = runtime.startedAt
response.LastCycleAt = runtime.lastCycleAt
response.LastCompletedAt = runtime.lastCompletedAt
response.LastRecoveredClaims = runtime.lastRecoveredClaims
response.TotalRecoveredClaims = runtime.totalRecoveredClaims
response.LastDispatchID = runtime.lastDispatchID
response.LastResult = runtime.lastResult
response.LastError = runtime.lastError
runtime.mu.RUnlock()
_ = json.NewEncoder(w).Encode(response)
})
}
+39 -4
View File
@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"testing"
"time"
)
func TestLoadProtectedBridgeURL(t *testing.T) {
@@ -38,14 +39,14 @@ func TestWorkerStatusReportsRecoveryOnlyWithoutExecutor(t *testing.T) {
}
}
func TestWorkerStatusReportsLocalExecutor(t *testing.T) {
func TestWorkerStatusReportsLocalExecutorCapabilityReady(t *testing.T) {
response := httptest.NewRecorder()
workerStatusHandler(false, true).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/status", nil))
var value workerStatusResponse
if err := json.NewDecoder(response.Body).Decode(&value); err != nil {
t.Fatal(err)
}
if response.Code != http.StatusOK || value.Ready || value.Mode != "local-executor" || !value.LocalFallback || !value.LocalExecutor {
if response.Code != http.StatusOK || !value.Ready || value.Mode != "local-executor" || !value.LocalFallback || !value.LocalExecutor {
t.Fatalf("unexpected status: code=%d value=%+v", response.Code, value)
}
if len(value.Capabilities) != 1 || value.Capabilities[0] != "task-evaluation" {
@@ -60,20 +61,54 @@ func TestWorkerStatusDoesNotCallLocalFallbackAnExternalExecutor(t *testing.T) {
if err := json.NewDecoder(response.Body).Decode(&value); err != nil {
t.Fatal(err)
}
if value.ExecutorConfigured || value.Ready || !value.LocalFallback || !value.LocalExecutor || value.Mode != "local-executor" {
if value.ExecutorConfigured || !value.Ready || !value.LocalFallback || !value.LocalExecutor || value.Mode != "local-executor" {
t.Fatalf("local fallback was presented as external execution: %+v", value)
}
}
func TestWorkerReadyzRequiresExecutor(t *testing.T) {
func TestWorkerReadyzRequiresAnAdvertisedCapability(t *testing.T) {
response := httptest.NewRecorder()
workerStatusHandler(false).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/readyz", nil))
if response.Code != http.StatusServiceUnavailable {
t.Fatalf("readyz status=%d", response.Code)
}
response = httptest.NewRecorder()
workerStatusHandler(false, true).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/readyz", nil))
if response.Code != http.StatusOK {
t.Fatalf("local capability readyz status=%d", response.Code)
}
response = httptest.NewRecorder()
workerStatusHandler(true).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/readyz", nil))
if response.Code != http.StatusOK {
t.Fatalf("configured readyz status=%d", response.Code)
}
}
func TestWorkerStatusReportsRecoveryCycle(t *testing.T) {
runtime := newAgentWorkerRuntimeStatus()
runtime.cycleStarted(time.Date(2026, 8, 23, 10, 0, 0, 0, time.UTC))
runtime.cycleFinished(time.Date(2026, 8, 23, 10, 0, 1, 0, time.UTC), 3, "dispatch-1", "recovered")
response := httptest.NewRecorder()
workerStatusHandler(false, true, runtime).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/status", nil))
var value workerStatusResponse
if err := json.NewDecoder(response.Body).Decode(&value); err != nil {
t.Fatal(err)
}
if value.LastRecoveredClaims != 3 || value.TotalRecoveredClaims != 3 || value.LastDispatchID != "dispatch-1" || value.LastResult != "recovered" {
t.Fatalf("recovery evidence missing: %+v", value)
}
}
func TestWorkerStatusReportsCycleError(t *testing.T) {
runtime := newAgentWorkerRuntimeStatus()
runtime.cycleFailed(time.Date(2026, 8, 23, 10, 0, 1, 0, time.UTC), os.ErrClosed)
response := httptest.NewRecorder()
workerStatusHandler(false, runtime).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/status", nil))
var value workerStatusResponse
if err := json.NewDecoder(response.Body).Decode(&value); err != nil {
t.Fatal(err)
}
if value.LastResult != "failed" || value.LastError == "" {
t.Fatalf("cycle error missing: %+v", value)
}
}
@@ -50,6 +50,17 @@ func (e taskEvaluationExecutor) Execute(ctx context.Context, request work.AgentE
if len(response.Responses) == 0 || response.ObservedAtUnixMillis < 1 {
return work.AgentExecutionResult{}, fmt.Errorf("task evaluation returned no observations")
}
if len(response.Observations) == len(intent.TaskEvaluationCases) && taskEvaluationHasCriticalFailure(response.Observations) {
evaluation, err := capabilitydomain.BuildTaskEvaluation(*intent.TaskEvaluationProfile, capability.TaskEvaluationID("evaluation:"+intent.DispatchID), response.BindingID, response.BindingRevision, intent.TaskEvaluationProfile.CapabilityID, response.Observations, artifact.ArtifactID(intent.TaskEvaluationCases[0].EvidenceArtifactRef), response.ObservedAtUnixMillis)
if err != nil {
return work.AgentExecutionResult{}, err
}
callback := work.TaskEvaluationEvidenceCallback{GraphID: execution.GraphID, Revision: execution.GraphRevision, NodeID: execution.NodeID, Evaluation: evaluation, Succeeded: true}
if _, err := postJSON[json.RawMessage](ctx, e.client, e.callbackURL+"/v1/work-graphs/task-evaluations/callback", callback); err != nil {
return work.AgentExecutionResult{}, err
}
return work.AgentExecutionResult{State: work.AgentExecutionCompleted, Summary: "task evaluation recorded as UNKNOWN after bounded provider recovery", OutcomeRef: string(evaluation.ID), ArtifactRefs: []string{string(evaluation.ArtifactID)}, ObservedAtUnixMillis: time.Now().UnixMilli()}, nil
}
if len(intent.TaskEvaluationCases) == 0 {
return work.AgentExecutionResult{}, fmt.Errorf("task evaluation cases are missing")
}
@@ -80,6 +91,15 @@ func (e taskEvaluationExecutor) Execute(ctx context.Context, request work.AgentE
return work.AgentExecutionResult{State: work.AgentExecutionCompleted, Summary: "task evaluation executed, aggregated and accepted by Work Graph callback", OutcomeRef: string(evaluation.ID), ArtifactRefs: []string{string(evaluation.ArtifactID)}, ObservedAtUnixMillis: time.Now().UnixMilli()}, nil
}
func taskEvaluationHasCriticalFailure(observations []capability.TaskCaseObservation) bool {
for _, observation := range observations {
if observation.CriticalFailure {
return true
}
}
return false
}
func postJSON[T any](ctx context.Context, client *http.Client, url string, payload any) (T, error) {
var result T
body, err := json.Marshal(payload)
@@ -41,3 +41,32 @@ func TestTaskEvaluationExecutorInvokesAggregatesAndCallbacks(t *testing.T) {
t.Fatalf("result=%+v err=%v", result, err)
}
}
func TestTaskEvaluationExecutorPersistsUnknownAfterProviderFailure(t *testing.T) {
profile := capability.TaskEvaluationProfile{ID: "profile-unknown", Revision: 1, CapabilityID: "capability-1", Stage: capability.TaskEvaluationGeneral, BenchmarkVersion: "unknown-v1", RequiredCaseIDs: []string{"case-1"}, QualityWeightMillis: 600, ReliabilityWeightMillis: 300, LatencyWeightMillis: 100}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/task-evaluations/execute" {
_ = json.NewEncoder(w).Encode(capability.TaskEvaluationExecutionResponse{ProfileID: profile.ID, BindingID: "binding-1", BindingRevision: 3, Observations: []capability.TaskCaseObservation{{CaseID: "case-1", Result: capability.EvaluationUnknown, CriticalFailure: true, EvidenceArtifactRef: "artifact-1"}}, Responses: []capability.TaskCaseResponse{{CaseID: "case-1", EvidenceArtifactRef: "artifact-1"}}, ObservedAtUnixMillis: 10})
return
}
if r.URL.Path == "/v1/work-graphs/task-evaluations/callback" {
var callback work.TaskEvaluationEvidenceCallback
if err := json.NewDecoder(r.Body).Decode(&callback); err != nil || callback.Evaluation.Result != capability.EvaluationUnknown || !callback.Succeeded {
http.Error(w, "invalid unknown callback", http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusNoContent)
return
}
if r.URL.Path == "/v1/task-evaluations/verify" {
t.Errorf("verifier must not be called for a provider failure")
}
http.NotFound(w, r)
}))
defer server.Close()
executor := newTaskEvaluationExecutor(server.URL, server.URL, server.URL, server.Client())
result, err := executor.Execute(context.Background(), work.AgentExecutionRequest{Intent: work.AgentDispatchIntent{DispatchID: "dispatch-unknown", TaskEvaluationBindingID: "binding-1", TaskEvaluationProfile: &profile, TaskEvaluationCases: []capability.TaskEvaluationCase{{CaseID: "case-1", Prompt: "answer", EvidenceArtifactRef: "artifact-1"}}, TaskEvaluationExecution: &work.TaskEvaluationDispatchContext{GraphID: "graph-1", GraphRevision: 1, NodeID: "node-1", BindingRevision: 3, ActorID: "actor-1", PrincipalID: "principal-1", ScopeID: "scope-1"}}})
if err != nil || result.State != work.AgentExecutionCompleted || result.OutcomeRef == "" {
t.Fatalf("result=%+v err=%v", result, err)
}
}
+10 -1
View File
@@ -298,12 +298,21 @@ func fallbackEligible(reason cognitive.FinishReason) bool {
}
func (g cognitiveGateway) provider(selected capability.ResolvedCognitiveBinding) advisoryProvider {
var byStableID advisoryProvider
for _, registration := range g.providers[selected.Provider.Kind] {
if registration.EndpointRef == selected.Provider.EndpointRef && registration.CredentialRef == selected.Provider.CredentialRef {
return registration.Provider
}
// Capability revisions can legitimately retain an older credential
// reference while the protected runtime has rotated or reloaded the
// current credential. ProviderInstanceID is the stable owner identity;
// use it after exact metadata matching so stale non-secret metadata does
// not force an unnecessary builtin fallback.
if byStableID == nil && registration.ProviderInstanceID == selected.Provider.ID && registration.Kind == selected.Provider.Kind {
byStableID = registration.Provider
}
}
return nil
return byStableID
}
func writeGatewayResponse(w http.ResponseWriter, response cognitive.GatewayAdvisoryResponse) {
@@ -88,6 +88,19 @@ func TestActiveRegistryBindingReachesProviderAdapter(t *testing.T) {
}
}
func TestProviderUsesStableInstanceWhenCredentialMetadataIsStale(t *testing.T) {
provider := &stubAdvisoryProvider{}
gateway := cognitiveGateway{providers: map[capability.ProviderKind][]providerRegistration{
"provider-kind-1": {{ProviderInstanceID: "provider-1", Kind: "provider-kind-1", EndpointRef: "current-endpoint", CredentialRef: "current-credential", Provider: provider}},
}}
selected := gatewaySelection()
selected.Provider.EndpointRef = "old-endpoint"
selected.Provider.CredentialRef = ""
if got := gateway.provider(selected); got != provider {
t.Fatalf("stable provider identity was not used for stale metadata")
}
}
func TestCanaryFlagIsForwardedToCapabilityResolver(t *testing.T) {
registry := &registryStub{err: errors.New("stop after resolve")}
gateway := cognitiveGateway{resolver: registry, providers: map[capability.ProviderKind][]providerRegistration{}, now: time.Now, allowCanary: true}
+2 -2
View File
@@ -163,8 +163,8 @@ func interpretationPrompt(request cognitive.GatewayInterpretationRequest) string
Allowed speech acts: DIRECTIVE, ASSERTION, DECISION, PREFERENCE, QUESTION, HYPOTHESIS, CORRECTION, REQUEST_TO_INVESTIGATE, CONFIRMATION, REJECTION, CANCELLATION, CONVERSATIONAL, UNKNOWN.
Allowed applicability: APPLICABLE, LOW_CONFIDENCE, MIXED_INTENT, NEEDS_BROADER_INTERPRETATION, CLARIFICATION_REQUIRED.
Allowed clarification: NOT_REQUIRED or REQUIRED.
Capability hints may only contain known capability kinds like COGNITIVE_REASONING, MODIFY_CODE, RUN_TESTS, SEND_EMAIL, SCHEDULE_REMINDER, VERIFY_FACT, INSPECT_PROJECT, READ_DATABASE, QUERY_LOGS.
Interpret the human utterance semantically. Do not claim action execution. Do not treat punctuation alone as decisive. If uncertain, set applicability accordingly.
Capability hints may only contain known capability kinds like COGNITIVE_REASONING, WEB_RESEARCH, MODIFY_CODE, RUN_TESTS, SEND_EMAIL, SCHEDULE_REMINDER, VERIFY_FACT, INSPECT_PROJECT, READ_DATABASE, QUERY_LOGS.
Interpret the human utterance semantically. Do not claim action execution. A question asking for a current, external or otherwise source-dependent fact should receive WEB_RESEARCH; do not answer that fact from model memory. A request to discuss hypotheses or options is different and may receive COGNITIVE_REASONING. Do not treat punctuation alone as decisive. If uncertain, set applicability accordingly.
Message: %q
Deterministic primary: %s
Deterministic secondary: %s
+3 -2
View File
@@ -97,7 +97,8 @@ func main() {
if gpuErr != nil {
log.Printf("optional GPU provider disabled: %v", gpuErr)
} else if gpuConfigured {
gpuAdapter, adapterErr := models.NewOpenAICompatibleLocalAdapterWithAPIKey(gpuURL, gpuKey, nil)
gpuTimeout := durationEnv("LOCAL_GPU_PROVIDER_TIMEOUT", 60*time.Second)
gpuAdapter, adapterErr := models.NewOpenAICompatibleLocalAdapterWithAPIKeyAndTimeout(gpuURL, gpuKey, nil, gpuTimeout)
if adapterErr != nil {
log.Printf("optional GPU provider disabled: %v", adapterErr)
} else {
@@ -169,7 +170,7 @@ func main() {
mux.HandleFunc("POST /v1/task-evaluations/execute", gateway.taskEvaluationHandler)
discovery := discoveryService{resolver: resolver, providers: discoveries, owner: identity.PrincipalID(envOr("COGNITIVE_OWNER_PRINCIPAL_ID", "domovoy-creator-principal")), now: time.Now}
mux.HandleFunc("POST /v1/provider-discovery/{provider}", discovery.handler)
mux.Handle("POST /v1/provider-discovery/{provider}/qualify", modelQualificationHandler(discoveries, string(envOr("COGNITIVE_OWNER_PRINCIPAL_ID", "domovoy-creator-principal"))))
mux.Handle("POST /v1/provider-discovery/{provider}/qualify", modelQualificationHandler(discoveries, string(envOr("COGNITIVE_OWNER_PRINCIPAL_ID", "domovoy-creator-principal")), httpQualificationObserver{resolver: resolver}))
mux.HandleFunc("GET /v1/provider-discovery/status", discovery.statusHandler)
refreshProviderDiscoveries(resolver, discovery)
log.Printf("cognitive-worker listening on %s", addr)
+16
View File
@@ -4,6 +4,7 @@ import (
"os"
"path/filepath"
"testing"
"time"
)
func TestLifeboatProviderIsOptionalAndLocalAdapterBacked(t *testing.T) {
@@ -19,6 +20,21 @@ func TestLifeboatProviderIsOptionalAndLocalAdapterBacked(t *testing.T) {
}
}
func TestGPUProviderTimeoutUsesBoundedConfigurableDefault(t *testing.T) {
t.Setenv("LOCAL_GPU_PROVIDER_TIMEOUT", "")
if got := durationEnv("LOCAL_GPU_PROVIDER_TIMEOUT", 60*time.Second); got != 60*time.Second {
t.Fatalf("default GPU timeout=%s", got)
}
t.Setenv("LOCAL_GPU_PROVIDER_TIMEOUT", "31s")
if got := durationEnv("LOCAL_GPU_PROVIDER_TIMEOUT", 60*time.Second); got != 31*time.Second {
t.Fatalf("configured GPU timeout=%s", got)
}
t.Setenv("LOCAL_GPU_PROVIDER_TIMEOUT", "invalid")
if got := durationEnv("LOCAL_GPU_PROVIDER_TIMEOUT", 60*time.Second); got != 60*time.Second {
t.Fatalf("invalid GPU timeout=%s", got)
}
}
func TestLoadLocalCPUProviderAPIKeyUsesProtectedFileReference(t *testing.T) {
t.Setenv("LOCAL_CPU_PROVIDER_API_KEY", "")
_ = os.Unsetenv("LOCAL_CPU_PROVIDER_API_KEY")
+52 -2
View File
@@ -14,8 +14,22 @@ import (
"git.cin.su/m/Assistant/contracts/capability"
"git.cin.su/m/Assistant/contracts/cognitive"
"git.cin.su/m/Assistant/contracts/identity"
"git.cin.su/m/Assistant/platform/ids"
)
type qualificationObserver interface {
ObserveQualification(context.Context, capability.ModelQualificationObservation) error
}
type httpQualificationObserver struct{ resolver *httpBindingResolver }
func (o httpQualificationObserver) ObserveQualification(ctx context.Context, value capability.ModelQualificationObservation) error {
if o.resolver == nil {
return fmt.Errorf("qualification observer resolver is nil")
}
return o.resolver.post(ctx, "/v1/cognitive-registry/model-qualification-observations", value, nil)
}
type modelQualificationRequest struct {
Prompt string `json:"prompt"`
MaxOutputBytes int `json:"max_output_bytes"`
@@ -39,7 +53,7 @@ type modelQualificationResponse struct {
Results []modelQualificationResult `json:"results"`
}
func modelQualificationHandler(providers map[string]providerRegistration, owner string) http.Handler {
func modelQualificationHandler(providers map[string]providerRegistration, owner string, observers ...qualificationObserver) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
name := r.PathValue("provider")
registration, ok := providers[name]
@@ -76,6 +90,12 @@ func modelQualificationHandler(providers map[string]providerRegistration, owner
modelsFound = modelsFound[:16]
}
results := qualifyModels(r.Context(), registration, owner, request.Prompt, request.MaxOutputBytes, modelsFound)
if len(observers) > 0 && observers[0] != nil {
if err := persistQualificationResults(r.Context(), observers[0], registration, results); err != nil {
http.Error(w, "persist model qualification observation", http.StatusBadGateway)
return
}
}
w.Header().Set("Content-Type", "application/json")
_ = jsonEncode(w, modelQualificationResponse{Provider: registration.ProviderInstanceID, ObservedAtUnixMillis: time.Now().UnixMilli(), Results: results})
})
@@ -89,11 +109,12 @@ func qualifyModels(ctx context.Context, registration providerRegistration, owner
go func(index int, model models.DiscoveredModel) {
defer wg.Done()
started := time.Now()
request := cognitive.AdvisoryRequest{Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID(fmt.Sprintf("qualification:%s:%d", model.ProviderModelID, started.UnixNano())), ActorID: identity.ActorID("qualification-runtime"), PrincipalID: identity.PrincipalID(owner), ScopeID: authority.ScopeID("qualification-scope"), Message: prompt, MaxOutputBytes: maxOutputBytes, ProviderBindingID: "qualification-binding-v1", ProviderBindingRevision: 1, ProviderInstanceID: registration.ProviderInstanceID, ModelDescriptorID: capability.ModelDescriptorID("qualification-" + string(model.ProviderModelID)), ProviderModelID: model.ProviderModelID}
request := cognitive.AdvisoryRequest{Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID(fmt.Sprintf("qualification:%s:%d", model.ProviderModelID, started.UnixNano())), ActorID: identity.ActorID("qualification-runtime"), PrincipalID: identity.PrincipalID(owner), ScopeID: authority.ScopeID("qualification-scope"), Message: prompt, Locale: "en-US", MaxOutputBytes: maxOutputBytes, ProviderBindingID: "qualification-binding-v1", ProviderBindingRevision: 1, ProviderInstanceID: registration.ProviderInstanceID, ModelDescriptorID: capability.ModelDescriptorID("qualification-" + string(model.ProviderModelID)), ProviderModelID: model.ProviderModelID}
response, err := registration.Provider.Advise(ctx, request)
result := modelQualificationResult{ProviderModelID: model.ProviderModelID, LatencyMillis: time.Since(started).Milliseconds(), QualityAssessed: false, Category: capability.ModelRatingUnrated}
if err != nil {
result.Status = "UNAVAILABLE"
result.FinishReason = cognitive.FinishProviderUnavailable
result.Error = err.Error()
results[index] = result
return
@@ -112,6 +133,35 @@ func qualifyModels(ctx context.Context, registration providerRegistration, owner
return results
}
func persistQualificationResults(ctx context.Context, observer qualificationObserver, registration providerRegistration, results []modelQualificationResult) error {
for _, result := range results {
observationID, err := ids.New()
if err != nil {
return err
}
status := capability.ModelQualificationRespondedWithoutRating
switch result.Status {
case "REACHABLE":
status = capability.ModelQualificationReachable
case "UNAVAILABLE":
status = capability.ModelQualificationUnavailable
}
observation := capability.ModelQualificationObservation{
ID: capability.ModelQualificationObservationID(observationID), ProviderInstanceID: registration.ProviderInstanceID, ProviderModelID: result.ProviderModelID,
Status: status, FinishReason: string(result.FinishReason), LatencyMillis: result.LatencyMillis,
OutputObserved: result.OutputObserved, QualityAssessed: false, Category: capability.ModelRatingUnrated,
ObservedAtUnixMillis: time.Now().UnixMilli(),
}
if err := observation.Validate(); err != nil {
return err
}
if err := observer.ObserveQualification(ctx, observation); err != nil {
return err
}
}
return nil
}
func jsonEncode(w http.ResponseWriter, value any) error {
return json.NewEncoder(w).Encode(value)
}
@@ -21,6 +21,9 @@ func (qualificationProvider) Discover(context.Context) ([]models.DiscoveredModel
return []models.DiscoveredModel{{ProviderModelID: "model-a"}, {ProviderModelID: "model-b"}}, models.ProviderProbe{Health: capability.HealthAvailable}
}
func (qualificationProvider) Advise(_ context.Context, request cognitive.AdvisoryRequest) (cognitive.AdvisoryResponse, error) {
if err := models.ValidateAdvisoryRequest(request); err != nil {
return cognitive.AdvisoryResponse{}, err
}
return cognitive.AdvisoryResponse{Version: request.Version, RequestID: request.RequestID, AdvisoryText: "reachable", FinishReason: cognitive.FinishCompleted}, nil
}
@@ -54,3 +57,29 @@ func TestModelQualificationRejectsUnboundedOutput(t *testing.T) {
t.Fatalf("unbounded qualification status=%d", response.Code)
}
}
type qualificationObserverStub struct {
observations []capability.ModelQualificationObservation
}
func (s *qualificationObserverStub) ObserveQualification(_ context.Context, value capability.ModelQualificationObservation) error {
s.observations = append(s.observations, value)
return nil
}
func TestModelQualificationPersistsReachabilityWithoutQuality(t *testing.T) {
provider := qualificationProvider{}
observer := &qualificationObserverStub{}
mux := http.NewServeMux()
mux.Handle("POST /v1/provider-discovery/{provider}/qualify", modelQualificationHandler(map[string]providerRegistration{"cpu": {ProviderInstanceID: "cpu", Provider: provider, Discovery: provider}}, "owner", observer))
request := httptest.NewRequest(http.MethodPost, "/v1/provider-discovery/cpu/qualify", strings.NewReader(`{"prompt":"ready","max_output_bytes":128,"model_ids":["model-a"]}`))
request.Header.Set("Content-Type", "application/json")
response := httptest.NewRecorder()
mux.ServeHTTP(response, request)
if response.Code != http.StatusOK || len(observer.observations) != 1 {
t.Fatalf("qualification persistence status=%d observations=%d body=%s", response.Code, len(observer.observations), response.Body.String())
}
if err := observer.observations[0].Validate(); err != nil || observer.observations[0].Status != capability.ModelQualificationReachable {
t.Fatalf("invalid qualification observation: %+v err=%v", observer.observations[0], err)
}
}
+74 -31
View File
@@ -1,9 +1,11 @@
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"sync"
"time"
@@ -34,20 +36,13 @@ func (g cognitiveGateway) taskEvaluationHandler(w http.ResponseWriter, r *http.R
http.Error(w, "external egress acknowledgement required", http.StatusBadRequest)
return
}
provider := g.provider(selected)
if provider == nil {
http.Error(w, "cognitive provider unavailable", http.StatusServiceUnavailable)
return
}
caseSet := make(map[string]struct{}, len(request.Profile.RequiredCaseIDs))
seenCases := make(map[string]struct{}, len(request.Cases))
for _, id := range request.Profile.RequiredCaseIDs {
caseSet[id] = struct{}{}
}
observations := make([]capability.TaskCaseObservation, len(request.Cases))
responses := make([]capability.TaskCaseResponse, len(request.Cases))
var wg sync.WaitGroup
seenCases := make(map[string]struct{}, len(request.Cases))
for i, item := range request.Cases {
for _, item := range request.Cases {
if item.CaseID == "" || item.Prompt == "" || item.EvidenceArtifactRef == "" {
http.Error(w, "invalid task evaluation case", http.StatusBadRequest)
return
@@ -61,35 +56,83 @@ func (g cognitiveGateway) taskEvaluationHandler(w http.ResponseWriter, r *http.R
return
}
seenCases[item.CaseID] = struct{}{}
wg.Add(1)
go func(index int, item capability.TaskEvaluationCase) {
defer wg.Done()
started := time.Now()
invocation := cognitive.AdvisoryRequest{Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID(fmt.Sprintf("task-evaluation:%s:%s", request.Profile.ID, item.CaseID)), ActorID: request.ActorID, PrincipalID: request.PrincipalID, ScopeID: request.ScopeID, Message: item.Prompt, MaxOutputBytes: int(request.MaxOutputBytes), ProviderBindingID: selected.Binding.ID, ProviderBindingRevision: selected.Binding.Revision, ProviderInstanceID: selected.Provider.ID, ModelDescriptorID: selected.Model.ID, ProviderModelID: selected.Model.ProviderModelID}
response, invokeErr := g.invokeAdmitted(r.Context(), string(invocation.RequestID), provider, invocation, int(request.MaxOutputBytes))
observation := capability.TaskCaseObservation{CaseID: item.CaseID, LatencyMicros: time.Since(started).Microseconds(), EvidenceArtifactRef: item.EvidenceArtifactRef}
responses[index] = capability.TaskCaseResponse{CaseID: item.CaseID, ResponseText: response.AdvisoryText, LatencyMicros: observation.LatencyMicros, EvidenceArtifactRef: item.EvidenceArtifactRef}
if invokeErr == nil && response.FinishReason == cognitive.FinishCompleted && response.AdvisoryText != "" {
// Invocation success proves reachability only. Without an
// independent verifier and answer evidence, semantic support and
// quality must remain UNKNOWN rather than becoming false evidence.
observation.Result = capability.EvaluationUnknown
observation.QualityScoreMillis = 0
} else {
observation.Result = capability.EvaluationUnknown
observation.CriticalFailure = invokeErr != nil
}
observations[index] = observation
}(i, item)
}
if len(seenCases) != len(caseSet) {
http.Error(w, "task evaluation case set is incomplete", http.StatusBadRequest)
return
}
wg.Wait()
// One bounded fan-out wave uses one binding snapshot. If that candidate is
// unavailable, only this binding is excluded and the existing resolver
// chooses the next eligible candidate. Provider output remains untrusted
// until the independent verifier accepts it downstream.
requirement := capability.CognitiveRequirement{CapabilityID: request.CapabilityID, OwnerPrincipalID: request.PrincipalID, DataPolicy: selected.Binding.DataPolicy, RequiredModalities: []capability.ModelModality{capability.ModalityText}, RequiredCapabilities: []capability.ModelCapability{capability.CapabilityAdvisory}, AllowCanary: g.allowCanary, MaxOutputBytes: request.MaxOutputBytes}
var observations []capability.TaskCaseObservation
var responses []capability.TaskCaseResponse
for attempt := 0; attempt < 3; attempt++ {
provider := g.provider(selected)
if provider == nil {
observations, responses = taskEvaluationUnavailable(request.Cases)
} else {
observations, responses = g.executeTaskEvaluationCases(r.Context(), request, selected, provider)
}
if taskEvaluationResponsesUsable(responses) {
break
}
requirement.ExcludedBindingIDs = append(requirement.ExcludedBindingIDs, selected.Binding.ID)
fallback, resolveErr := g.resolver.Resolve(r.Context(), requirement)
fallbackProvider := g.provider(fallback)
if resolveErr != nil || fallbackProvider == nil || fallback.Binding.ID == selected.Binding.ID || (fallback.Binding.DataPolicy != capability.DataPolicyLocalOnly && !request.EgressAcknowledged) {
break
}
selected = fallback
}
writeTaskEvaluationJSON(w, capability.TaskEvaluationExecutionResponse{ProfileID: request.Profile.ID, BindingID: selected.Binding.ID, BindingRevision: selected.Binding.Revision, Observations: observations, Responses: responses, ObservedAtUnixMillis: time.Now().UnixMilli()})
}
func (g cognitiveGateway) executeTaskEvaluationCases(ctx context.Context, request capability.TaskEvaluationExecutionRequest, selected capability.ResolvedCognitiveBinding, provider advisoryProvider) ([]capability.TaskCaseObservation, []capability.TaskCaseResponse) {
observations := make([]capability.TaskCaseObservation, len(request.Cases))
responses := make([]capability.TaskCaseResponse, len(request.Cases))
var wg sync.WaitGroup
for i, item := range request.Cases {
wg.Add(1)
go func(index int, item capability.TaskEvaluationCase) {
defer wg.Done()
started := time.Now()
invocation := cognitive.AdvisoryRequest{Version: cognitive.CreatorAdvisoryV1, RequestID: cognitive.RequestID(fmt.Sprintf("task-evaluation:%s:%s", request.Profile.ID, item.CaseID)), ActorID: request.ActorID, PrincipalID: request.PrincipalID, ScopeID: request.ScopeID, Message: item.Prompt, MaxOutputBytes: int(request.MaxOutputBytes), ProviderBindingID: selected.Binding.ID, ProviderBindingRevision: selected.Binding.Revision, ProviderInstanceID: selected.Provider.ID, ModelDescriptorID: selected.Model.ID, ProviderModelID: selected.Model.ProviderModelID}
response, invokeErr := g.invokeAdmitted(ctx, string(invocation.RequestID), provider, invocation, int(request.MaxOutputBytes))
latency := time.Since(started).Microseconds()
observations[index] = capability.TaskCaseObservation{CaseID: item.CaseID, Result: capability.EvaluationUnknown, CriticalFailure: invokeErr != nil || response.FinishReason != cognitive.FinishCompleted || strings.TrimSpace(response.AdvisoryText) == "", EvidenceArtifactRef: item.EvidenceArtifactRef, LatencyMicros: latency}
responses[index] = capability.TaskCaseResponse{CaseID: item.CaseID, ResponseText: response.AdvisoryText, LatencyMicros: latency, EvidenceArtifactRef: item.EvidenceArtifactRef}
}(i, item)
}
wg.Wait()
return observations, responses
}
func taskEvaluationUnavailable(cases []capability.TaskEvaluationCase) ([]capability.TaskCaseObservation, []capability.TaskCaseResponse) {
observations := make([]capability.TaskCaseObservation, len(cases))
responses := make([]capability.TaskCaseResponse, len(cases))
for i, item := range cases {
observations[i] = capability.TaskCaseObservation{CaseID: item.CaseID, Result: capability.EvaluationUnknown, CriticalFailure: true, EvidenceArtifactRef: item.EvidenceArtifactRef}
responses[i] = capability.TaskCaseResponse{CaseID: item.CaseID, EvidenceArtifactRef: item.EvidenceArtifactRef}
}
return observations, responses
}
func taskEvaluationResponsesUsable(responses []capability.TaskCaseResponse) bool {
if len(responses) == 0 {
return false
}
for _, response := range responses {
if strings.TrimSpace(response.ResponseText) == "" {
return false
}
}
return true
}
func writeTaskEvaluationJSON(w http.ResponseWriter, value any) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(value)
@@ -56,3 +56,90 @@ func TestTaskEvaluationHandlerInvokesEveryBoundedCase(t *testing.T) {
}
}
}
type taskEvaluationFailoverRegistry struct {
primary, fallback capability.ResolvedCognitiveBinding
resolveCalls int
}
func (s *taskEvaluationFailoverRegistry) ResolveCandidate(context.Context, capability.CapabilityBindingID, int64) (capability.ResolvedCognitiveBinding, error) {
return s.primary, nil
}
func (s *taskEvaluationFailoverRegistry) Resolve(_ context.Context, requirement capability.CognitiveRequirement) (capability.ResolvedCognitiveBinding, error) {
s.resolveCalls++
if len(requirement.ExcludedBindingIDs) == 0 {
return s.primary, nil
}
return s.fallback, nil
}
func (s *taskEvaluationFailoverRegistry) Observe(context.Context, capability.CognitiveUsageObservation) error {
return nil
}
func (s *taskEvaluationFailoverRegistry) RegisterDiscovery(context.Context, capability.CognitiveDiscoverySnapshot) (capability.CognitiveDiscoveryRegistration, error) {
return capability.CognitiveDiscoveryRegistration{}, nil
}
func TestTaskEvaluationExcludesUnavailableCandidateAndUsesFallback(t *testing.T) {
primary := gatewaySelection()
fallback := primary
fallback.Binding.ID = "binding-fallback"
fallback.Binding.Revision = 7
fallback.Binding.ProviderInstanceID = "provider-fallback"
fallback.Provider.ID = "provider-fallback"
fallback.Provider.Kind = "provider-fallback-kind"
fallback.Provider.EndpointRef = "fallback-endpoint"
fallback.Provider.CredentialRef = "fallback-credential"
fallback.Model.ProviderInstanceID = "provider-fallback"
registry := &taskEvaluationFailoverRegistry{primary: primary, fallback: fallback}
primaryProvider := &stubAdvisoryProvider{response: cognitive.AdvisoryResponse{Version: cognitive.CreatorAdvisoryV1, FinishReason: cognitive.FinishProviderUnavailable}}
fallbackProvider := &stubAdvisoryProvider{response: cognitive.AdvisoryResponse{Version: cognitive.CreatorAdvisoryV1, FinishReason: cognitive.FinishCompleted, AdvisoryText: "fallback response"}}
gateway := cognitiveGateway{resolver: registry, providers: map[capability.ProviderKind][]providerRegistration{
primary.Provider.Kind: {{ProviderInstanceID: primary.Provider.ID, Kind: primary.Provider.Kind, EndpointRef: primary.Provider.EndpointRef, CredentialRef: primary.Provider.CredentialRef, Provider: primaryProvider}},
fallback.Provider.Kind: {{ProviderInstanceID: fallback.Provider.ID, Kind: fallback.Provider.Kind, EndpointRef: fallback.Provider.EndpointRef, CredentialRef: fallback.Provider.CredentialRef, Provider: fallbackProvider}},
}, now: func() time.Time { return time.UnixMilli(1000) }}
profile := capability.TaskEvaluationProfile{ID: "profile-failover", Revision: 1, CapabilityID: primary.Binding.CapabilityID, Stage: capability.TaskEvaluationGeneral, BenchmarkVersion: "failover-v1", RequiredCaseIDs: []string{"case-a"}, QualityWeightMillis: 600, ReliabilityWeightMillis: 300, LatencyWeightMillis: 100}
request := capability.TaskEvaluationExecutionRequest{Profile: profile, CapabilityID: profile.CapabilityID, BindingID: primary.Binding.ID, BindingRevision: primary.Binding.Revision, ActorID: identity.ActorID("actor-1"), PrincipalID: primary.Binding.OwnerPrincipalID, ScopeID: authority.ScopeID("scope-1"), MaxOutputBytes: 512, EgressAcknowledged: true, Cases: []capability.TaskEvaluationCase{{CaseID: "case-a", Prompt: "one", EvidenceArtifactRef: "artifact:a"}}}
payload, _ := json.Marshal(request)
recorder := httptest.NewRecorder()
gateway.taskEvaluationHandler(recorder, httptest.NewRequest(http.MethodPost, "/v1/task-evaluations/execute", bytes.NewReader(payload)))
if recorder.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
var response capability.TaskEvaluationExecutionResponse
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
t.Fatal(err)
}
if response.BindingID != fallback.Binding.ID || !primaryProvider.called || !fallbackProvider.called || registry.resolveCalls != 1 {
t.Fatalf("failover binding=%s primary_called=%t fallback_called=%t resolve_calls=%d", response.BindingID, primaryProvider.called, fallbackProvider.called, registry.resolveCalls)
}
if response.Observations[0].CriticalFailure || response.Responses[0].ResponseText != "fallback response" {
t.Fatalf("fallback response was not usable: %+v %+v", response.Observations, response.Responses)
}
}
func TestTaskEvaluationUnavailableResponseIsMarkedCriticalUnknown(t *testing.T) {
selected := gatewaySelection()
registry := &taskEvaluationFailoverRegistry{primary: selected, fallback: selected}
provider := &stubAdvisoryProvider{response: cognitive.AdvisoryResponse{Version: cognitive.CreatorAdvisoryV1, FinishReason: cognitive.FinishTimeout}}
gateway := cognitiveGateway{resolver: registry, providers: map[capability.ProviderKind][]providerRegistration{
selected.Provider.Kind: {{ProviderInstanceID: selected.Provider.ID, Kind: selected.Provider.Kind, EndpointRef: selected.Provider.EndpointRef, CredentialRef: selected.Provider.CredentialRef, Provider: provider}},
}, now: func() time.Time { return time.UnixMilli(1000) }}
profile := capability.TaskEvaluationProfile{ID: "profile-unknown", Revision: 1, CapabilityID: selected.Binding.CapabilityID, Stage: capability.TaskEvaluationGeneral, BenchmarkVersion: "unknown-v1", RequiredCaseIDs: []string{"case-a"}, QualityWeightMillis: 600, ReliabilityWeightMillis: 300, LatencyWeightMillis: 100}
request := capability.TaskEvaluationExecutionRequest{Profile: profile, CapabilityID: profile.CapabilityID, BindingID: selected.Binding.ID, BindingRevision: selected.Binding.Revision, ActorID: identity.ActorID("actor-1"), PrincipalID: selected.Binding.OwnerPrincipalID, ScopeID: authority.ScopeID("scope-1"), MaxOutputBytes: 512, EgressAcknowledged: true, Cases: []capability.TaskEvaluationCase{{CaseID: "case-a", Prompt: "one", EvidenceArtifactRef: "artifact:a"}}}
payload, _ := json.Marshal(request)
recorder := httptest.NewRecorder()
gateway.taskEvaluationHandler(recorder, httptest.NewRequest(http.MethodPost, "/v1/task-evaluations/execute", bytes.NewReader(payload)))
if recorder.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
var response capability.TaskEvaluationExecutionResponse
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
t.Fatal(err)
}
if len(response.Observations) != 1 || response.Observations[0].Result != capability.EvaluationUnknown || !response.Observations[0].CriticalFailure {
t.Fatalf("unavailable response was overclaimed: %+v", response.Observations)
}
}
@@ -61,13 +61,6 @@ func (c cognitiveInterpretationClient) Interpret(ctx context.Context, request co
}
func shouldRequestBroaderInterpretation(result interactiondomain.SemanticResult) bool {
// The built-in advisory channel is the default conversational path. A
// simple question must not first wait for the slower, strict JSON
// interpretation route: that route exists for ambiguity and work routing,
// not as a prerequisite for talking to Domovoy.
if isSimpleLocalQuestion(result) {
return false
}
switch result.Interpretation.Applicability {
case interaction.SemanticNeedsBroaderInterpret, interaction.SemanticLowConfidence, interaction.SemanticMixedIntent:
return true
@@ -7,7 +7,7 @@ import (
interactiondomain "git.cin.su/m/Assistant/modules/interaction/domain"
)
func TestSimpleQuestionUsesBuiltInAdvisoryWithoutStrictInterpretation(t *testing.T) {
func TestSimpleQuestionUsesStrictInterpretationForSemanticRouting(t *testing.T) {
result := interactiondomain.SemanticResult{
Interpretation: interaction.UtteranceInterpretation{
SpeechAct: interaction.SpeechActQuestion,
@@ -16,8 +16,8 @@ func TestSimpleQuestionUsesBuiltInAdvisoryWithoutStrictInterpretation(t *testing
},
CapabilityHints: []string{"COGNITIVE_REASONING", "VERIFY_FACT"},
}
if shouldRequestBroaderInterpretation(result) {
t.Fatal("simple question must go directly to the built-in advisory path")
if !shouldRequestBroaderInterpretation(result) {
t.Fatal("simple question must reach semantic interpretation before any answer")
}
}
+97
View File
@@ -1,14 +1,111 @@
package main
import (
"context"
"errors"
"strings"
"time"
"git.cin.su/m/Assistant/adapters/developmentexecutor"
"git.cin.su/m/Assistant/contracts/work"
workdomain "git.cin.su/m/Assistant/modules/work/domain"
"git.cin.su/m/Assistant/modules/work/ports"
)
var errDevelopmentGateCreate = errors.New("development gate must start at evidence collected with revision one")
var errDevelopmentGateNotIsolated = errors.New("development gate is not isolated")
var errDevelopmentVerifierNotIndependent = errors.New("development verifier is not independent")
var errDevelopmentWorktreeMismatch = errors.New("verification worktree does not match gate")
var errDevelopmentChecksFailed = errors.New("development verification checks failed")
var errDevelopmentEvidenceRequired = errors.New("development transition evidence is required")
var errDevelopmentEvidenceStage = errors.New("development transition evidence does not match stage")
type developmentGateVerificationRequest struct {
GateID string
ExpectedRevision int64
VerifierRunID work.AgentRunID
Plan developmentexecutor.Plan
}
type developmentGateEvidenceRequest struct {
GateID string
ExpectedRevision int64
NextStage work.DevelopmentStage
EvidenceRef string
}
func recordDevelopmentGateEvidence(ctx context.Context, store ports.DevelopmentGateStore, request developmentGateEvidenceRequest, now time.Time) (work.DevelopmentGate, error) {
gate, err := store.LoadDevelopmentGate(ctx, request.GateID)
if err != nil {
return work.DevelopmentGate{}, err
}
if request.ExpectedRevision < 1 || gate.Revision != request.ExpectedRevision {
return gate, errDevelopmentGateNotIsolated
}
if strings.TrimSpace(request.EvidenceRef) == "" {
return gate, errDevelopmentEvidenceRequired
}
switch request.NextStage {
case work.DevelopmentCanary:
gate.CanaryEvidence = strings.TrimSpace(request.EvidenceRef)
case work.DevelopmentPromoted:
gate.PromotionRef = strings.TrimSpace(request.EvidenceRef)
case work.DevelopmentRolledBack:
gate.RollbackRef = strings.TrimSpace(request.EvidenceRef)
default:
return gate, errDevelopmentEvidenceStage
}
advanced, err := advanceDevelopmentGate(gate, request.NextStage, now)
if err != nil {
return gate, err
}
if err := store.SaveDevelopmentGate(ctx, advanced, request.ExpectedRevision); err != nil {
return gate, err
}
return advanced, nil
}
func verifyDevelopmentGate(ctx context.Context, store ports.DevelopmentGateStore, request developmentGateVerificationRequest, now time.Time, runner developmentexecutor.CommandRunner) (work.DevelopmentGate, developmentexecutor.Result, error) {
gate, err := store.LoadDevelopmentGate(ctx, request.GateID)
if err != nil {
return work.DevelopmentGate{}, developmentexecutor.Result{}, err
}
if request.ExpectedRevision < 1 || gate.Revision != request.ExpectedRevision || gate.Stage != work.DevelopmentIsolated {
return gate, developmentexecutor.Result{}, errDevelopmentGateNotIsolated
}
if request.VerifierRunID == "" || sameRun(request.VerifierRunID, gate.ProducerRunRefs) {
return gate, developmentexecutor.Result{}, errDevelopmentVerifierNotIndependent
}
if request.Plan.WorktreeID != gate.WorktreeRef {
return gate, developmentexecutor.Result{}, errDevelopmentWorktreeMismatch
}
result, err := developmentexecutor.Execute(ctx, request.Plan, runner)
if err != nil {
return gate, result, err
}
if !result.Passed {
return gate, result, errDevelopmentChecksFailed
}
gate.VerifierRunRefs = append(append([]work.AgentRunID(nil), gate.VerifierRunRefs...), request.VerifierRunID)
gate.VerificationEvidence = result.EvidenceRef
advanced, err := advanceDevelopmentGate(gate, work.DevelopmentVerified, now)
if err != nil {
return gate, result, err
}
if err := store.SaveDevelopmentGate(ctx, advanced, request.ExpectedRevision); err != nil {
return gate, result, err
}
return advanced, result, nil
}
func sameRun(value work.AgentRunID, runs []work.AgentRunID) bool {
for _, run := range runs {
if run == value {
return true
}
}
return false
}
func prepareDevelopmentGate(value work.DevelopmentGate, now time.Time) (work.DevelopmentGate, error) {
if value.Stage != work.DevelopmentEvidenceCollected || value.Revision != 1 {
+141
View File
@@ -1,12 +1,153 @@
package main
import (
"context"
"path/filepath"
"testing"
"time"
"git.cin.su/m/Assistant/adapters/developmentexecutor"
"git.cin.su/m/Assistant/contracts/work"
)
type developmentGateStoreStub struct {
gate work.DevelopmentGate
saved bool
expected int64
}
func (s *developmentGateStoreStub) LoadDevelopmentGate(context.Context, string) (work.DevelopmentGate, error) {
return s.gate, nil
}
func (s *developmentGateStoreStub) SaveDevelopmentGate(_ context.Context, gate work.DevelopmentGate, expected int64) error {
s.gate = gate
s.saved = true
s.expected = expected
return nil
}
type developmentCommandRunnerStub struct {
exitCodes []int
commands int
}
func (r *developmentCommandRunnerStub) Run(context.Context, string, string, []string) int {
code := 0
if r.commands < len(r.exitCodes) {
code = r.exitCodes[r.commands]
}
r.commands++
return code
}
func validDevelopmentVerificationPlan(t *testing.T, worktreeID string) developmentexecutor.Plan {
t.Helper()
worktree, err := filepath.Abs(".")
if err != nil {
t.Fatal(err)
}
return developmentexecutor.Plan{
Worktree: worktree,
WorktreeID: worktreeID,
BaseRepoRevision: "base-revision",
ProtectedMainAccess: "DENIED",
ProductionAccess: "DENIED",
SecretAccess: "DENIED",
Checks: []developmentexecutor.Check{{Kind: developmentexecutor.CheckGitDiffCheck}},
}
}
func isolatedDevelopmentGate() work.DevelopmentGate {
return work.DevelopmentGate{
ID: "gate-1",
Revision: 4,
Stage: work.DevelopmentIsolated,
EvidenceRefs: []string{"evidence-1"},
ProposalRef: "proposal-1",
TaskPacketRef: "task-1",
WorktreeRef: "sandbox-1",
ProducerRunRefs: []work.AgentRunID{"producer-1"},
VerifierRunRefs: nil,
VerificationEvidence: "",
}
}
func TestVerifyDevelopmentGatePersistsVerifiedWithIndependentEvidence(t *testing.T) {
store := &developmentGateStoreStub{gate: isolatedDevelopmentGate()}
runner := &developmentCommandRunnerStub{exitCodes: []int{0}}
gate, result, err := verifyDevelopmentGate(context.Background(), store, developmentGateVerificationRequest{
GateID: "gate-1",
ExpectedRevision: 4,
VerifierRunID: "verifier-1",
Plan: validDevelopmentVerificationPlan(t, "sandbox-1"),
}, time.UnixMilli(20), runner)
if err != nil {
t.Fatal(err)
}
if !result.Passed || gate.Stage != work.DevelopmentVerified || gate.Revision != 5 {
t.Fatalf("verification did not advance gate: gate=%#v result=%#v", gate, result)
}
if !store.saved || store.expected != 4 || gate.VerificationEvidence == "" {
t.Fatalf("verification was not durably persisted: store=%#v gate=%#v", store, gate)
}
}
func TestVerifyDevelopmentGateLeavesIsolatedOnFailedChecks(t *testing.T) {
store := &developmentGateStoreStub{gate: isolatedDevelopmentGate()}
gate, result, err := verifyDevelopmentGate(context.Background(), store, developmentGateVerificationRequest{
GateID: "gate-1",
ExpectedRevision: 4,
VerifierRunID: "verifier-1",
Plan: validDevelopmentVerificationPlan(t, "sandbox-1"),
}, time.UnixMilli(20), &developmentCommandRunnerStub{exitCodes: []int{1}})
if err != errDevelopmentChecksFailed || result.Passed || gate.Stage != work.DevelopmentIsolated || store.saved {
t.Fatalf("failed checks changed gate unexpectedly: gate=%#v result=%#v err=%v store=%#v", gate, result, err, store)
}
}
func TestVerifyDevelopmentGateRejectsProducerAsVerifier(t *testing.T) {
store := &developmentGateStoreStub{gate: isolatedDevelopmentGate()}
_, _, err := verifyDevelopmentGate(context.Background(), store, developmentGateVerificationRequest{
GateID: "gate-1",
ExpectedRevision: 4,
VerifierRunID: "producer-1",
Plan: validDevelopmentVerificationPlan(t, "sandbox-1"),
}, time.UnixMilli(20), &developmentCommandRunnerStub{exitCodes: []int{0}})
if err != errDevelopmentVerifierNotIndependent {
t.Fatalf("producer was accepted as verifier: %v", err)
}
}
func TestRecordDevelopmentGateEvidenceAdvancesCanaryAndPromotion(t *testing.T) {
store := &developmentGateStoreStub{gate: work.DevelopmentGate{
ID: "gate-1", Revision: 6, Stage: work.DevelopmentVerified,
EvidenceRefs: []string{"evidence-1"}, ProposalRef: "proposal-1", TaskPacketRef: "task-1", WorktreeRef: "sandbox-1",
ProducerRunRefs: []work.AgentRunID{"producer-1"}, VerifierRunRefs: []work.AgentRunID{"verifier-1"}, VerificationEvidence: "verification-1",
}}
gate, err := recordDevelopmentGateEvidence(context.Background(), store, developmentGateEvidenceRequest{GateID: "gate-1", ExpectedRevision: 6, NextStage: work.DevelopmentCanary, EvidenceRef: "canary-1"}, time.UnixMilli(30))
if err != nil || gate.Stage != work.DevelopmentCanary || gate.Revision != 7 || gate.CanaryEvidence != "canary-1" {
t.Fatalf("canary transition failed: gate=%#v err=%v", gate, err)
}
gate, err = recordDevelopmentGateEvidence(context.Background(), store, developmentGateEvidenceRequest{GateID: "gate-1", ExpectedRevision: 7, NextStage: work.DevelopmentPromoted, EvidenceRef: "promotion-1"}, time.UnixMilli(31))
if err != nil || gate.Stage != work.DevelopmentPromoted || gate.Revision != 8 || gate.PromotionRef != "promotion-1" {
t.Fatalf("promotion transition failed: gate=%#v err=%v", gate, err)
}
}
func TestRecordDevelopmentGateEvidenceSupportsCanaryRollbackAndRejectsMissingEvidence(t *testing.T) {
store := &developmentGateStoreStub{gate: work.DevelopmentGate{ID: "gate-1", Revision: 2, Stage: work.DevelopmentCanary, EvidenceRefs: []string{"evidence-1"}, CanaryEvidence: "canary-1"}}
gate, err := recordDevelopmentGateEvidence(context.Background(), store, developmentGateEvidenceRequest{GateID: "gate-1", ExpectedRevision: 2, NextStage: work.DevelopmentRolledBack, EvidenceRef: "rollback-1"}, time.UnixMilli(40))
if err != nil || gate.Stage != work.DevelopmentRolledBack || gate.RollbackRef != "rollback-1" {
t.Fatalf("rollback transition failed: gate=%#v err=%v", gate, err)
}
store = &developmentGateStoreStub{gate: work.DevelopmentGate{ID: "gate-2", Revision: 2, Stage: work.DevelopmentCanary, EvidenceRefs: []string{"evidence-1"}, CanaryEvidence: "canary-1"}}
gate, err = recordDevelopmentGateEvidence(context.Background(), store, developmentGateEvidenceRequest{GateID: "gate-2", ExpectedRevision: 2, NextStage: work.DevelopmentRolledBack}, time.UnixMilli(40))
if err != errDevelopmentEvidenceRequired || gate.Stage != work.DevelopmentCanary || store.saved {
t.Fatalf("missing rollback evidence changed gate: gate=%#v err=%v store=%#v", gate, err, store)
}
}
func TestDevelopmentGateControllerRequiresSequentialVerifiedEvidence(t *testing.T) {
now := time.UnixMilli(10)
gate, err := prepareDevelopmentGate(work.DevelopmentGate{ID: "gate-1", Revision: 1, Stage: work.DevelopmentEvidenceCollected, EvidenceRefs: []string{"evidence-1"}}, now)
+104
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"git.cin.su/m/Assistant/adapters/developmentexecutor"
"git.cin.su/m/Assistant/adapters/postgres"
"git.cin.su/m/Assistant/contracts/authority"
"git.cin.su/m/Assistant/contracts/capability"
@@ -21,6 +22,7 @@ import (
interactiondomain "git.cin.su/m/Assistant/modules/interaction/domain"
situationdomain "git.cin.su/m/Assistant/modules/situation/domain"
workdomain "git.cin.su/m/Assistant/modules/work/domain"
"git.cin.su/m/Assistant/platform/ids"
platformsecrets "git.cin.su/m/Assistant/platform/secrets"
"git.cin.su/m/Assistant/runtime/health"
"github.com/jackc/pgx/v5"
@@ -311,6 +313,56 @@ func main() {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(gate)
})
mux.HandleFunc("POST /v1/creator/development/gates/{id}/verify", func(w http.ResponseWriter, r *http.Request) {
if _, ok := authenticateCreator(w, r); !ok {
return
}
var request struct {
ExpectedRevision int64 `json:"expected_revision"`
VerifierRunID work.AgentRunID `json:"verifier_run_id"`
Plan developmentexecutor.Plan `json:"plan"`
}
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&request); err != nil || request.ExpectedRevision < 1 || request.VerifierRunID == "" {
http.Error(w, "invalid development verification request", http.StatusBadRequest)
return
}
gate, result, err := verifyDevelopmentGate(r.Context(), workRepository, developmentGateVerificationRequest{GateID: r.PathValue("id"), ExpectedRevision: request.ExpectedRevision, VerifierRunID: request.VerifierRunID, Plan: request.Plan}, time.Now(), developmentexecutor.OSCommandRunner{})
if err != nil {
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"gate": gate, "verification": result})
})
registerDevelopmentGateEvidenceRoute := func(path string, next work.DevelopmentStage, fieldName string) {
mux.HandleFunc("POST /v1/creator/development/gates/{id}/"+path, func(w http.ResponseWriter, r *http.Request) {
if _, ok := authenticateCreator(w, r); !ok {
return
}
var request struct {
ExpectedRevision int64 `json:"expected_revision"`
EvidenceRef string `json:"evidence_ref"`
}
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&request); err != nil || request.ExpectedRevision < 1 || request.EvidenceRef == "" {
http.Error(w, "invalid development transition evidence", http.StatusBadRequest)
return
}
gate, err := recordDevelopmentGateEvidence(r.Context(), workRepository, developmentGateEvidenceRequest{GateID: r.PathValue("id"), ExpectedRevision: request.ExpectedRevision, NextStage: next, EvidenceRef: request.EvidenceRef}, time.Now())
if err != nil {
http.Error(w, "development "+fieldName+" transition rejected", http.StatusUnprocessableEntity)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(gate)
})
}
registerDevelopmentGateEvidenceRoute("canary", work.DevelopmentCanary, "canary")
registerDevelopmentGateEvidenceRoute("promote", work.DevelopmentPromoted, "promotion")
registerDevelopmentGateEvidenceRoute("rollback", work.DevelopmentRolledBack, "rollback")
mux.HandleFunc("GET /v1/creator/focus", func(w http.ResponseWriter, r *http.Request) {
session, ok := authenticateCreator(w, r)
if !ok {
@@ -570,6 +622,17 @@ func main() {
planningReply = humanCognitiveChannelReply(semantic.InformationRequestKind, semantic.Interpretation.SpeechAct, health, healthErr)
needsCognition = false
}
researchBacked := false
if hasCapabilityHint(semantic.CapabilityHints, "WEB_RESEARCH") && semantic.Interpretation.Clarification != interaction.ClarificationRequired {
if planID, idErr := ids.New(); idErr == nil {
result, researchErr := researchClient.SearchWeb(r.Context(), epistemics.WebSearchRequest{
Plan: epistemics.ResearchPlan{ID: epistemics.ResearchPlanID("plan:" + planID), Question: utterance.Content, SourceClasses: []string{"web-search"}, FreshnessPolicy: "current"},
PropositionKey: "question:" + string(utterance.ID), Query: utterance.Content,
})
planningReply, researchBacked = renderResearchReply(result, researchErr)
needsCognition = false
}
}
if helpPath.Reply != "" {
planningReply = helpPath.Reply
needsCognition = false
@@ -667,6 +730,7 @@ func main() {
Clarification: semantic.Interpretation.Clarification,
WorkPlanned: workPlanned,
PlanningReply: planningReply,
ResearchBacked: researchBacked,
CapabilityStatus: capabilityStatus,
CapabilityKind: capabilityKind,
CapabilityHints: toCapabilityKinds(semantic.CapabilityHints),
@@ -1058,6 +1122,20 @@ func main() {
}
w.WriteHeader(http.StatusCreated)
})
mux.HandleFunc("POST /v1/cognitive-registry/model-qualification-observations", func(w http.ResponseWriter, r *http.Request) {
var value capability.ModelQualificationObservation
decoder := json.NewDecoder(io.LimitReader(r.Body, 16<<10))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&value); err != nil || value.Validate() != nil {
http.Error(w, "invalid model qualification observation", http.StatusBadRequest)
return
}
if err := capabilityRepository.SaveModelQualificationObservation(r.Context(), value); err != nil {
http.Error(w, "save model qualification observation", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
})
mux.HandleFunc("POST /v1/creator/cognitive-canary", func(w http.ResponseWriter, r *http.Request) {
session, ok := authenticateCreator(w, r)
if !ok {
@@ -1362,6 +1440,32 @@ func envInt(name string, fallback int) int {
return value
}
func hasCapabilityHint(hints []string, wanted string) bool {
for _, hint := range hints {
if hint == wanted {
return true
}
}
return false
}
func renderResearchReply(result epistemics.ResearchResult, researchErr error) (string, bool) {
if researchErr != nil {
return "Я не смог получить проверяемые данные из research-источника, поэтому не буду придумывать ответ.", false
}
lines := make([]string, 0, len(result.Findings))
for _, finding := range result.Findings {
if finding.State != epistemics.FindingSupported || strings.TrimSpace(finding.Excerpt.Text) == "" || strings.TrimSpace(finding.Source.Locator) == "" {
continue
}
lines = append(lines, "- "+finding.Excerpt.Text+"\n Источник: "+finding.Source.Locator)
}
if len(lines) == 0 {
return "Я не получил достаточно согласованных данных из проверяемых источников, поэтому не буду выдавать предположение как факт.", false
}
return "Нашёл точные выдержки из подключённого источника. Я не заменяю их догадкой:\n" + strings.Join(lines, "\n"), true
}
func humanCognitiveModelInventory(candidates []capability.ResolvedCognitiveBinding, inventoryErr error) string {
if inventoryErr != nil {
return "Я не смогла прочитать реестр подключённых моделей. Встроенный канал Домового остаётся доступен; настройки можно проверить повторно после восстановления реестра. Ключи и пароли не выводятся в чат."
+25
View File
@@ -51,3 +51,28 @@ func (c researchRuntimeClient) EvaluateWeb(ctx context.Context, request epistemi
}
return result, nil
}
func (c researchRuntimeClient) SearchWeb(ctx context.Context, request epistemics.WebSearchRequest) (epistemics.ResearchResult, error) {
payload, err := json.Marshal(request)
if err != nil {
return epistemics.ResearchResult{}, err
}
httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/v1/research/web/search", bytes.NewReader(payload))
if err != nil {
return epistemics.ResearchResult{}, err
}
httpRequest.Header.Set("Content-Type", "application/json")
response, err := c.client.Do(httpRequest)
if err != nil {
return epistemics.ResearchResult{}, err
}
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
return epistemics.ResearchResult{}, fmt.Errorf("research search status %d", response.StatusCode)
}
var result epistemics.ResearchResult
if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
return epistemics.ResearchResult{}, err
}
return result, nil
}
+38
View File
@@ -151,6 +151,44 @@ func TestChatMessageUsesCoreAndLocalOnlyCognitiveGateway(t *testing.T) {
}
}
func TestChatMessageDoesNotExposeUnverifiedQuestionAsFact(t *testing.T) {
core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/v1/creator/sessions/current":
_ = json.NewEncoder(w).Encode(identity.CreatorSession{ActorID: "actor-1", PrincipalID: "principal-1"})
case r.Method == http.MethodPost && r.URL.Path == "/v1/creator/chat/messages":
w.WriteHeader(http.StatusCreated)
_ = json.NewEncoder(w).Encode(interaction.ChatMessageResponse{ConversationID: "conversation-1", UtteranceID: "utterance-1", SpeechAct: interaction.SpeechActQuestion, NeedsCognition: true, FallbackText: "небезопасная модельная догадка"})
default:
t.Fatalf("unexpected core route %s %s", r.Method, r.URL.Path)
}
}))
defer core.Close()
cognitiveRuntime := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatalf("unverified question must not reach advisory: %s %s", r.Method, r.URL.Path)
}))
defer cognitiveRuntime.Close()
handler, err := newHandler(core.URL, "http://continuity", "http://commit", core.Client(), cognitiveRuntime.URL)
if err != nil {
t.Fatal(err)
}
request := httptest.NewRequest(http.MethodPost, "/api/chat/messages", strings.NewReader(`{"message":"Какая погода?"}`))
request.Header.Set("Authorization", "Bearer session-token")
request.Header.Set("Content-Type", "application/json")
recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("expected status 200, got %d body=%s", recorder.Code, recorder.Body.String())
}
var response browserChatResponse
if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil {
t.Fatal(err)
}
if response.Cognition != "verification_required" || strings.Contains(response.ReplyText, "небезопасная") || !strings.Contains(response.ReplyText, "не буду придумывать") {
t.Fatalf("unexpected truth-first response: %#v", response)
}
}
func TestChatMessageReturnsHumanCapabilityMetadata(t *testing.T) {
core := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
+53
View File
@@ -0,0 +1,53 @@
package main
import (
"strings"
"testing"
)
func TestCreatorConsoleResponsiveGuardrails(t *testing.T) {
html, err := staticFiles.ReadFile("static/index.html")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(html), `<meta name="viewport" content="width=device-width, initial-scale=1">`) {
t.Fatal("creator console is missing responsive viewport metadata")
}
css, err := staticFiles.ReadFile("static/assets/console.css")
if err != nil {
t.Fatal(err)
}
stylesheet := string(css)
for _, guard := range []string{
"@media (max-width: 680px)",
".console { display: block; }",
".diagnostic-list { grid-template-columns: 1fr; }",
".agent-details { grid-template-columns: 1fr; gap: 14px; }",
".fleet-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }",
".section-nav { display: flex;",
"overflow-x: auto",
} {
if !strings.Contains(stylesheet, guard) {
t.Fatalf("responsive guardrail missing: %s", guard)
}
}
}
func TestChatResponsiveGuardrailsConstrainMobileGridItem(t *testing.T) {
css, err := staticFiles.ReadFile("static/assets/chat.css")
if err != nil {
t.Fatal(err)
}
stylesheet := string(css)
for _, guard := range []string{
".chat-view { width: 100%; min-width: 0;",
"@media (max-width: 640px)",
".chat-layout { height: auto; display: block; }",
".chat-header > .operator-link, .build-meta { display: none; }",
".chat-header .cognitive-console-link { flex: 0 1 110px;",
} {
if !strings.Contains(stylesheet, guard) {
t.Fatalf("chat responsive guardrail missing: %s", guard)
}
}
}
+11 -1
View File
@@ -209,6 +209,7 @@ type browserChatResponse struct {
CapabilityKind string `json:"capability_kind"`
CapabilityHints []capability.CapabilityKind `json:"capability_hints,omitempty"`
RequestedOutcome string `json:"requested_outcome,omitempty"`
ResearchBacked bool `json:"research_backed"`
InterpretationConfidenceMillis int64 `json:"interpretation_confidence_millis,omitempty"`
DelegationID string `json:"delegation_id"`
DelegationState string `json:"delegation_state"`
@@ -467,7 +468,11 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) {
saved.NeedsCognition = false
}
}
if saved.NeedsCognition && !saved.WorkPlanned && saved.Clarification != interaction.ClarificationRequired {
if saved.SpeechAct == interaction.SpeechActQuestion && saved.InformationRequestKind == "" && !saved.ResearchBacked && !saved.WorkPlanned && saved.Clarification != interaction.ClarificationRequired && !asksAttention(browserRequest.Message) {
reply = truthfulVerificationRequiredReply()
cognition = "verification_required"
saved.NeedsCognition = false
} else if saved.NeedsCognition && !saved.WorkPlanned && saved.Clarification != interaction.ClarificationRequired {
cognition = "unavailable"
requestID, idErr := ids.New()
if idErr == nil {
@@ -501,6 +506,7 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) {
CapabilityKind: string(saved.CapabilityKind),
CapabilityHints: saved.CapabilityHints,
RequestedOutcome: saved.RequestedOutcome,
ResearchBacked: saved.ResearchBacked,
InterpretationConfidenceMillis: saved.InterpretationConfidenceMillis,
DelegationID: saved.DelegationID,
DelegationState: saved.DelegationState,
@@ -508,6 +514,10 @@ func (g *gateway) handleChatMessage(w http.ResponseWriter, r *http.Request) {
})
}
func truthfulVerificationRequiredReply() string {
return "Я не могу сейчас подтвердить этот факт по проверенному источнику, поэтому не буду придумывать ответ. Для проверки используйте официальный сайт соответствующей службы или надёжный сервис с текущими данными; когда подключён research-маршрут, Домовой сможет проверить это сам."
}
func localChatContext(saved interaction.ChatMessageResponse) []cognitive.ContextExcerpt {
if strings.TrimSpace(saved.ActiveProjectName) == "" {
return []cognitive.ContextExcerpt{}
+5 -4
View File
@@ -29,7 +29,7 @@ textarea { resize: vertical; min-height: 84px; max-height: 240px; padding: 14px;
.login-form button, .composer button { border: 0; color: #11150f; background: var(--signal); font-weight: 700; }
.login-form button { width: 100%; height: 50px; margin-top: 18px; }
#login-error { min-height: 20px; color: var(--bad); font-size: 13px; }
.chat-view { height: 100vh; min-height: 0; display: grid; grid-template-rows: 64px minmax(0, 1fr); overflow: hidden; }
.chat-view { width: 100%; min-width: 0; height: 100vh; min-height: 0; display: grid; grid-template-rows: 64px minmax(0, 1fr); overflow: hidden; }
.chat-header { min-width: 0; display: flex; align-items: center; gap: 14px; padding: 0 22px; border-bottom: 1px solid var(--line); background: rgba(16, 23, 20, .98); }
.chat-header a { color: var(--ink); text-decoration: none; font: 400 24px/1 var(--serif); }
#active-focus { color: var(--good); }
@@ -107,9 +107,10 @@ textarea { resize: vertical; min-height: 84px; max-height: 240px; padding: 14px;
.conversation-starters { align-items: stretch; }
.conversation-starters span { width: 100%; }
.chat-header { gap: 10px; padding: 0 12px; }
.chat-header .cognitive-status { display: inline-flex; max-width: 150px; overflow: hidden; text-overflow: ellipsis; }
.build-meta { font-size: 8px; letter-spacing: .04em; }
.build-meta time { font-size: 7px; }
.chat-header .cognitive-status { display: inline-flex; flex: 1 1 110px; min-width: 0; max-width: 110px; overflow: hidden; text-overflow: ellipsis; }
.chat-header .cognitive-console-link { flex: 0 1 110px; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.chat-header > .operator-link, .build-meta { display: none; }
.chat-header > #logout { flex: 0 0 38px; }
.chat-layout { height: auto; display: block; }
.chat-side, .chat-main { min-height: auto; max-height: none; overflow: visible; }
.chat-left, .chat-right { border: 0; border-bottom: 1px solid var(--line); }
+17
View File
@@ -4,6 +4,8 @@ import (
"context"
"flag"
"fmt"
"log"
"net/http"
"os"
"time"
@@ -29,9 +31,21 @@ func main() {
panic(err)
}
defer repository.Close()
status := newWorkerStatus(*workerID, *once)
listenAddr := os.Getenv("REFLECTION_LISTEN_ADDR")
if listenAddr == "" {
listenAddr = ":8092"
}
go func() {
if err := http.ListenAndServe(listenAddr, status.handler()); err != nil {
log.Printf("reflection status server stopped: %v", err)
}
}()
for {
now := time.Now().UnixMilli()
status.cycleStarted(time.Now())
if _, err := repository.RecoverReflectionClaims(ctx, now); err != nil {
status.cycleFailed(time.Now(), err)
panic(err)
}
candidate, claimErr := repository.ClaimReflectionCandidate(ctx, *workerID, now, lease.Milliseconds())
@@ -39,16 +53,19 @@ func main() {
// This worker only closes the durable evaluation loop. Any cognitive
// proposal remains a candidate and must be admitted by a separate governed flow.
if err := repository.ResolveReflectionCandidate(ctx, candidate.ID, *workerID, time.Now().UnixMilli()); err != nil {
status.cycleFailed(time.Now(), err)
panic(err)
}
fmt.Printf("resolved reflection candidate %s\n", candidate.ID)
} else if *once {
fmt.Println("no reflection candidate")
}
status.cycleFinished(time.Now(), map[bool]string{true: "candidate-resolved", false: "idle"}[claimErr == nil])
if *once {
return
}
if err := repository.WaitForReflectionCandidate(ctx, *pollInterval); err != nil {
status.cycleFailed(time.Now(), err)
panic(err)
}
}
+74
View File
@@ -0,0 +1,74 @@
package main
import (
"encoding/json"
"net/http"
"sync"
"time"
"git.cin.su/m/Assistant/runtime/health"
)
type workerStatus struct {
mu sync.RWMutex
workerID string
once bool
startedAt time.Time
lastCycleAt time.Time
lastCompleted time.Time
lastResult string
lastError string
}
func newWorkerStatus(workerID string, once bool) *workerStatus {
return &workerStatus{workerID: workerID, once: once, startedAt: time.Now().UTC()}
}
func (s *workerStatus) cycleStarted(at time.Time) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCycleAt = at.UTC()
s.lastResult = "running"
s.lastError = ""
}
func (s *workerStatus) cycleFinished(at time.Time, result string) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCompleted = at.UTC()
s.lastResult = result
s.lastError = ""
}
func (s *workerStatus) cycleFailed(at time.Time, err error) {
s.mu.Lock()
defer s.mu.Unlock()
s.lastCompleted = at.UTC()
s.lastResult = "failed"
s.lastError = err.Error()
}
func (s *workerStatus) handler() http.Handler {
mux := http.NewServeMux()
mux.Handle("/healthz", health.Handler{Runtime: "reflection-worker", SemanticGeneration: 1, ArchitectureGeneration: 1})
mux.HandleFunc("/status", func(w http.ResponseWriter, _ *http.Request) {
s.mu.RLock()
response := struct {
Runtime string `json:"runtime"`
WorkerID string `json:"worker_id"`
Mode string `json:"mode"`
StartedAt time.Time `json:"started_at"`
LastCycleAt time.Time `json:"last_cycle_at,omitempty"`
LastCompleted time.Time `json:"last_completed_at,omitempty"`
LastResult string `json:"last_result"`
LastError string `json:"last_error,omitempty"`
}{Runtime: "reflection-worker", WorkerID: s.workerID, Mode: "durable-reflection-loop", StartedAt: s.startedAt, LastCycleAt: s.lastCycleAt, LastCompleted: s.lastCompleted, LastResult: s.lastResult, LastError: s.lastError}
if s.once {
response.Mode = "durable-reflection-once"
}
s.mu.RUnlock()
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(response)
})
return mux
}
+35
View File
@@ -0,0 +1,35 @@
package main
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func TestWorkerStatusReportsCycle(t *testing.T) {
status := newWorkerStatus("test-worker", false)
status.cycleStarted(time.Date(2026, 8, 23, 10, 0, 0, 0, time.UTC))
status.cycleFinished(time.Date(2026, 8, 23, 10, 0, 1, 0, time.UTC), "idle")
recorder := httptest.NewRecorder()
status.handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/status", nil))
if recorder.Code != http.StatusOK {
t.Fatalf("status code = %d, want 200", recorder.Code)
}
body := recorder.Body.String()
for _, expected := range []string{"\"runtime\":\"reflection-worker\"", "\"worker_id\":\"test-worker\"", "\"last_result\":\"idle\""} {
if !strings.Contains(body, expected) {
t.Fatalf("status body missing %q: %s", expected, body)
}
}
}
func TestWorkerHealthIsAvailable(t *testing.T) {
recorder := httptest.NewRecorder()
newWorkerStatus("test-worker", true).handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/healthz", nil))
if recorder.Code != http.StatusOK {
t.Fatalf("health status code = %d, want 200", recorder.Code)
}
}
+21
View File
@@ -34,6 +34,7 @@ func main() {
mux := http.NewServeMux()
mux.Handle("/healthz", health.Handler{Runtime: "research-runtime", SemanticGeneration: 1, ArchitectureGeneration: 1})
mux.Handle("/readyz", health.ReadinessHandler{Runtime: "research-runtime", Probes: []health.Probe{{Name: "epistemics-postgres", Check: func(ctx context.Context) error { return repository.Ping(ctx) }}}})
mux.Handle("/v1/research/status", truthRuntimeStatusHandler(repository))
mux.HandleFunc("POST /v1/research/evaluate", func(w http.ResponseWriter, r *http.Request) {
var request epistemics.ResearchEvaluationRequest
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 2<<20)).Decode(&request); err != nil {
@@ -88,6 +89,26 @@ func main() {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(result)
})
searchConnector := researchadapter.SearchConnector{EndpointTemplate: os.Getenv("RESEARCH_SEARCH_URL_TEMPLATE"), AllowedHosts: allowedHosts(os.Getenv("RESEARCH_SEARCH_ALLOWED_HOSTS")), Client: &http.Client{Timeout: 8 * time.Second}, MaxResults: 3}
mux.HandleFunc("POST /v1/research/web/search", func(w http.ResponseWriter, r *http.Request) {
var request epistemics.WebSearchRequest
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10)).Decode(&request); err != nil {
http.Error(w, "invalid web search request", http.StatusBadRequest)
return
}
candidates, err := searchConnector.Collect(r.Context(), request, time.Now().UnixMilli())
if err != nil {
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
result, err := application.EvaluateAndPersist(r.Context(), epistemics.ResearchEvaluationRequest{Plan: request.Plan, Candidates: candidates}, repository, time.Now().UnixMilli())
if err != nil {
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(result)
})
addr := os.Getenv("LISTEN_ADDR")
if addr == "" {
addr = ":8087"
+47
View File
@@ -0,0 +1,47 @@
package main
import (
"context"
"encoding/json"
"net/http"
"git.cin.su/m/Assistant/adapters/postgres"
)
type truthRuntimeStatusReader interface {
TruthRuntimeStatus(context.Context) (postgres.TruthRuntimeStatus, error)
}
type truthRuntimeStatusResponse struct {
Runtime string `json:"runtime"`
Status string `json:"status"`
BundleCount int64 `json:"bundle_count"`
ClaimsByVerification map[string]int64 `json:"claims_by_verification"`
BeliefsByState map[string]int64 `json:"beliefs_by_state"`
SourcesByFreshness map[string]int64 `json:"sources_by_freshness"`
ObservedAtUnixMillis int64 `json:"observed_at_unix_millis"`
}
func truthRuntimeStatusHandler(reader truthRuntimeStatusReader) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet || r.URL.Path != "/v1/research/status" {
http.NotFound(w, r)
return
}
status, err := reader.TruthRuntimeStatus(r.Context())
if err != nil {
http.Error(w, "research status unavailable", http.StatusServiceUnavailable)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(truthRuntimeStatusResponse{
Runtime: "research-runtime",
Status: "ready",
BundleCount: status.BundleCount,
ClaimsByVerification: status.ClaimsByVerification,
BeliefsByState: status.BeliefsByState,
SourcesByFreshness: status.SourcesByFreshness,
ObservedAtUnixMillis: status.ObservedAtUnixMillis,
})
})
}
+48
View File
@@ -0,0 +1,48 @@
package main
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"git.cin.su/m/Assistant/adapters/postgres"
)
type fakeTruthRuntimeStatusReader struct {
status postgres.TruthRuntimeStatus
err error
}
func (f fakeTruthRuntimeStatusReader) TruthRuntimeStatus(context.Context) (postgres.TruthRuntimeStatus, error) {
return f.status, f.err
}
func TestTruthRuntimeStatusReportsEvidenceCounts(t *testing.T) {
response := httptest.NewRecorder()
truthRuntimeStatusHandler(fakeTruthRuntimeStatusReader{status: postgres.TruthRuntimeStatus{
BundleCount: 4,
ClaimsByVerification: map[string]int64{"UNVERIFIED": 3, "VERIFIED": 1},
BeliefsByState: map[string]int64{"PROVISIONAL": 4},
SourcesByFreshness: map[string]int64{"FRESH": 4},
ObservedAtUnixMillis: 1700000000000,
}}).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/v1/research/status", nil))
if response.Code != http.StatusOK {
t.Fatalf("status code = %d", response.Code)
}
for _, expected := range []string{"\"status\":\"ready\"", "\"bundle_count\":4", "\"VERIFIED\":1", "\"PROVISIONAL\":4", "1700000000000"} {
if !strings.Contains(response.Body.String(), expected) {
t.Fatalf("response missing %q: %s", expected, response.Body.String())
}
}
}
func TestTruthRuntimeStatusFailsClosedOnDatabaseError(t *testing.T) {
response := httptest.NewRecorder()
truthRuntimeStatusHandler(fakeTruthRuntimeStatusReader{err: errors.New("database unavailable")}).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/v1/research/status", nil))
if response.Code != http.StatusServiceUnavailable {
t.Fatalf("status code = %d", response.Code)
}
}
+24
View File
@@ -10,9 +10,11 @@ import (
"strings"
"git.cin.su/m/Assistant/contracts/capability"
"git.cin.su/m/Assistant/contracts/work"
)
const verifierIdentity = "deterministic-task-verifier-v1"
const genericVerifierIdentity = "deterministic-generic-envelope-verifier-v1"
func main() {
mux := http.NewServeMux()
@@ -21,6 +23,7 @@ func main() {
_, _ = w.Write([]byte(`{"runtime":"task-verifier","status":"ok"}`))
})
mux.HandleFunc("POST /v1/task-evaluations/verify", verifyHandler)
mux.HandleFunc("POST /v1/work-graphs/candidates/verify", genericCandidateVerifyHandler)
addr := ":8091"
if value := strings.TrimSpace(os.Getenv("LISTEN_ADDR")); value != "" {
addr = value
@@ -30,6 +33,27 @@ func main() {
}
}
func genericCandidateVerifyHandler(w http.ResponseWriter, r *http.Request) {
var request work.GenericCandidateVerificationRequest
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&request); err != nil || request.GraphID == "" || request.GraphRevision < 1 || request.NodeID == "" || request.AuthorRunID == "" || request.CandidateID == "" || strings.TrimSpace(request.Summary) == "" || len(request.EvidenceRefs) == 0 {
http.Error(w, "invalid generic candidate envelope", http.StatusBadRequest)
return
}
payload := fmt.Sprintf("%s|%s|%d|%s|%s|%s|%s", genericVerifierIdentity, request.GraphID, request.GraphRevision, request.NodeID, request.AuthorRunID, request.CandidateID, strings.Join(evidenceRefStrings(request.EvidenceRefs), "|"))
digest := sha256.Sum256([]byte(payload))
writeJSON(w, work.GenericCandidateVerificationResponse{Verified: true, VerificationEvidenceRef: "verification:" + hex.EncodeToString(digest[:]), VerifierIdentity: genericVerifierIdentity, Summary: "candidate attribution and evidence envelope verified; semantic content remains unverified"})
}
func evidenceRefStrings(values []work.EvidenceRef) []string {
result := make([]string, len(values))
for i, value := range values {
result[i] = string(value)
}
return result
}
func verifyHandler(w http.ResponseWriter, r *http.Request) {
var request capability.TaskCaseVerificationRequest
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10))
+12
View File
@@ -5,9 +5,11 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"git.cin.su/m/Assistant/contracts/capability"
"git.cin.su/m/Assistant/contracts/work"
)
func TestVerifyExactTextProducesIndependentEvidence(t *testing.T) {
@@ -39,3 +41,13 @@ func TestVerifyJSONFieldEquals(t *testing.T) {
t.Fatalf("unexpected JSON verification response: code=%d body=%s value=%+v", recorder.Code, recorder.Body.String(), response)
}
}
func TestVerifyGenericCandidateEnvelopeDoesNotClaimTruth(t *testing.T) {
payload, _ := json.Marshal(work.GenericCandidateVerificationRequest{GraphID: "graph-1", GraphRevision: 2, NodeID: "node-1", AuthorRunID: "run-1", CandidateID: "candidate-1", Summary: "model proposal", EvidenceRefs: []work.EvidenceRef{"agent-output:1"}})
recorder := httptest.NewRecorder()
genericCandidateVerifyHandler(recorder, httptest.NewRequest(http.MethodPost, "/v1/work-graphs/candidates/verify", bytes.NewReader(payload)))
var response work.GenericCandidateVerificationResponse
if recorder.Code != http.StatusOK || json.NewDecoder(recorder.Body).Decode(&response) != nil || !response.Verified || response.VerifierIdentity == "" || response.VerificationEvidenceRef == "" || !strings.Contains(response.Summary, "semantic content remains unverified") {
t.Fatalf("unexpected generic verification response: code=%d body=%s value=%+v", recorder.Code, recorder.Body.String(), response)
}
}
+17
View File
@@ -35,6 +35,7 @@ func main() {
mux := http.NewServeMux()
mux.Handle("/healthz", health.Handler{Runtime: "work-graph-runtime", SemanticGeneration: 1, ArchitectureGeneration: 1})
mux.Handle("/readyz", health.ReadinessHandler{Runtime: "work-graph-runtime", Probes: []health.Probe{{Name: "work-postgres", Check: func(ctx context.Context) error { return repository.Ping(ctx) }}}})
mux.Handle("/v1/work-graphs/status", workGraphStatusHandler(repository))
mux.HandleFunc("POST /v1/work-graphs/schedule", func(w http.ResponseWriter, r *http.Request) {
var request application.GraphScheduleRequest
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 4<<20)).Decode(&request); err != nil {
@@ -43,6 +44,7 @@ func main() {
}
graph, ready, err := application.AdmitAndSchedule(r.Context(), repository, request)
if err != nil {
log.Printf("work graph schedule rejected: %v", err)
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
@@ -56,6 +58,21 @@ func main() {
}
graph, ready, err := application.AdmitAndScheduleTaskEvaluation(r.Context(), repository, request)
if err != nil {
log.Printf("task evaluation graph schedule rejected: %v", err)
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
writeJSON(w, map[string]any{"graph": graph, "scheduled_nodes": ready})
})
mux.HandleFunc("POST /v1/work-graphs/task-evaluations/additional/schedule", func(w http.ResponseWriter, r *http.Request) {
var request application.TaskEvaluationExpansionScheduleRequest
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 4<<20)).Decode(&request); err != nil {
http.Error(w, "invalid additional task evaluation request", http.StatusBadRequest)
return
}
graph, ready, err := application.ScheduleAdditionalTaskEvaluation(r.Context(), repository, request)
if err != nil {
log.Printf("additional task evaluation schedule rejected: %v", err)
http.Error(w, err.Error(), http.StatusUnprocessableEntity)
return
}
+45
View File
@@ -0,0 +1,45 @@
package main
import (
"context"
"encoding/json"
"net/http"
"git.cin.su/m/Assistant/adapters/postgres"
)
type workGraphStatusReader interface {
RuntimeStatus(context.Context) (postgres.WorkGraphRuntimeStatus, error)
}
type workGraphStatusResponse struct {
Runtime string `json:"runtime"`
Status string `json:"status"`
GraphsByState map[string]int64 `json:"graphs_by_state"`
NodesByState map[string]int64 `json:"nodes_by_state"`
QueueByState map[string]int64 `json:"queue_by_state"`
ObservedAtUnixMillis int64 `json:"observed_at_unix_millis"`
}
func workGraphStatusHandler(reader workGraphStatusReader) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet || r.URL.Path != "/v1/work-graphs/status" {
http.NotFound(w, r)
return
}
status, err := reader.RuntimeStatus(r.Context())
if err != nil {
http.Error(w, "work graph status unavailable", http.StatusServiceUnavailable)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(workGraphStatusResponse{
Runtime: "work-graph-runtime",
Status: "ready",
GraphsByState: status.GraphsByState,
NodesByState: status.NodesByState,
QueueByState: status.QueueByState,
ObservedAtUnixMillis: status.ObservedAtUnixMillis,
})
})
}
+56
View File
@@ -0,0 +1,56 @@
package main
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"
"git.cin.su/m/Assistant/adapters/postgres"
)
type fakeWorkGraphStatusReader struct {
status postgres.WorkGraphRuntimeStatus
err error
}
func (f fakeWorkGraphStatusReader) RuntimeStatus(context.Context) (postgres.WorkGraphRuntimeStatus, error) {
return f.status, f.err
}
func TestWorkGraphStatusReportsDurableCounts(t *testing.T) {
response := httptest.NewRecorder()
workGraphStatusHandler(fakeWorkGraphStatusReader{status: postgres.WorkGraphRuntimeStatus{
GraphsByState: map[string]int64{"RUNNING": 2},
NodesByState: map[string]int64{"LEASED": 3, "SUCCEEDED": 1},
QueueByState: map[string]int64{"QUEUED": 3},
ObservedAtUnixMillis: 1700000000000,
}}).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/v1/work-graphs/status", nil))
if response.Code != http.StatusOK {
t.Fatalf("status code = %d", response.Code)
}
body := response.Body.String()
for _, expected := range []string{"\"status\":\"ready\"", "\"RUNNING\":2", "\"LEASED\":3", "\"QUEUED\":3", "1700000000000"} {
if !contains(body, expected) {
t.Fatalf("response missing %q: %s", expected, body)
}
}
}
func TestWorkGraphStatusFailsClosedOnDatabaseError(t *testing.T) {
response := httptest.NewRecorder()
workGraphStatusHandler(fakeWorkGraphStatusReader{err: errors.New("database unavailable")}).ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/v1/work-graphs/status", nil))
if response.Code != http.StatusServiceUnavailable {
t.Fatalf("status code = %d", response.Code)
}
}
func contains(value, expected string) bool {
for i := 0; i+len(expected) <= len(value); i++ {
if value[i:i+len(expected)] == expected {
return true
}
}
return false
}
@@ -172,6 +172,7 @@ type ResolvedCognitiveBinding struct {
Evaluation *EvaluatedCapability
Performance *PerformanceEvidence
TaskEvaluation *TaskEvaluation
Qualification *ModelQualificationObservation
Rating *ModelRating
}
@@ -0,0 +1,47 @@
package capability
import (
"errors"
"git.cin.su/m/Assistant/platform/ids"
)
type ModelQualificationObservationID ids.ID
type ModelQualificationStatus string
const (
ModelQualificationReachable ModelQualificationStatus = "REACHABLE"
ModelQualificationRespondedWithoutRating ModelQualificationStatus = "RESPONDED_WITHOUT_QUALIFICATION"
ModelQualificationUnavailable ModelQualificationStatus = "UNAVAILABLE"
)
// ModelQualificationObservation is operational evidence only. It records
// reachability and latency, never quality, activation, routing priority or
// provider credentials.
type ModelQualificationObservation struct {
ID ModelQualificationObservationID
ProviderInstanceID ProviderInstanceID
ProviderModelID ProviderModelID
Status ModelQualificationStatus
FinishReason string
LatencyMillis int64
OutputObserved bool
QualityAssessed bool
Category ModelRatingCategory
ObservedAtUnixMillis int64
}
func (o ModelQualificationObservation) Validate() error {
if o.ID == "" || o.ProviderInstanceID == "" || o.ProviderModelID == "" || o.FinishReason == "" || o.LatencyMillis < 0 || o.ObservedAtUnixMillis < 1 {
return errors.New("model qualification observation identity and timing are incomplete")
}
switch o.Status {
case ModelQualificationReachable, ModelQualificationRespondedWithoutRating, ModelQualificationUnavailable:
default:
return errors.New("model qualification observation status is unsupported")
}
if o.Category != ModelRatingUnrated || o.QualityAssessed {
return errors.New("model qualification observation cannot assert quality")
}
return nil
}
+11
View File
@@ -96,6 +96,8 @@ type ResearchFinding struct {
Statement string
Claim Claim
Citations []Citation
Source Source
Excerpt SourceExcerpt
State ResearchFindingState
Reason string
}
@@ -131,6 +133,15 @@ type WebResearchRequest struct {
Statement string
}
// WebSearchRequest asks the research runtime to discover bounded source
// evidence for a human question. The provider is configured by the runtime;
// the caller supplies meaning, not a provider URL or model choice.
type WebSearchRequest struct {
Plan ResearchPlan
PropositionKey string
Query string
}
// ResolveCitation creates evidence only when all identifiers form one exact
// source chain. It deliberately does not fetch, infer or rewrite source text.
func ResolveCitation(source Source, snapshot SourceSnapshot, excerpt SourceExcerpt, evidence EvidenceKind, coverage CoverageState, conflict ConflictState, resolvedAtUnixMillis int64) (Citation, error) {
+1
View File
@@ -173,6 +173,7 @@ type ChatMessageResponse struct {
Clarification ClarificationState
WorkPlanned bool
PlanningReply string
ResearchBacked bool
CapabilityStatus capability.CapabilityResolutionStatus
CapabilityKind capability.CapabilityKind
CapabilityHints []capability.CapabilityKind
+20
View File
@@ -113,6 +113,26 @@ type ResultCandidate struct {
Summary string `json:"summary"`
}
// GenericCandidateVerificationRequest asks the independent deterministic
// verifier to validate attribution and evidence-envelope integrity. It does
// not assert that the candidate's semantic content is true.
type GenericCandidateVerificationRequest struct {
GraphID string `json:"graph_id"`
GraphRevision int64 `json:"graph_revision"`
NodeID GraphNodeID `json:"node_id"`
AuthorRunID AgentRunID `json:"author_run_id"`
CandidateID string `json:"candidate_id"`
Summary string `json:"summary"`
EvidenceRefs []EvidenceRef `json:"evidence_refs"`
}
type GenericCandidateVerificationResponse struct {
Verified bool `json:"verified"`
VerificationEvidenceRef string `json:"verification_evidence_ref"`
VerifierIdentity string `json:"verifier_identity"`
Summary string `json:"summary"`
}
// GraphResultCallback carries one agent's attributable candidate to Work.
// Recording a candidate never makes the node successful; the node enters
// VERIFYING until an independent synthesis/verifier accepts it.
+8
View File
@@ -83,4 +83,12 @@ type TaskEvaluationDispatchContext struct {
ActorID identity.ActorID `json:"actor_id"`
PrincipalID identity.PrincipalID `json:"principal_id"`
ScopeID authority.ScopeID `json:"scope_id"`
// GenericMessage turns the existing durable dispatch context into a
// discriminated local cognitive-work payload without adding a second
// persistence column. It is ignored by task-evaluation executors.
GenericMessage string `json:"generic_message,omitempty"`
GenericPurpose string `json:"generic_purpose,omitempty"`
GenericLocale string `json:"generic_locale,omitempty"`
GenericDataPolicy string `json:"generic_data_policy,omitempty"`
GenericMaxOutputBytes int `json:"generic_max_output_bytes,omitempty"`
}
@@ -0,0 +1,24 @@
BEGIN;
SET ROLE capability_role;
CREATE TABLE IF NOT EXISTS capability.cognitive_model_qualification_observations (
observation_id TEXT PRIMARY KEY,
provider_instance_id TEXT NOT NULL,
provider_model_id TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('REACHABLE','RESPONDED_WITHOUT_QUALIFICATION','UNAVAILABLE')),
finish_reason TEXT NOT NULL,
latency_millis BIGINT NOT NULL CHECK (latency_millis >= 0),
output_observed BOOLEAN NOT NULL,
quality_assessed BOOLEAN NOT NULL DEFAULT FALSE CHECK (quality_assessed = FALSE),
category TEXT NOT NULL CHECK (category = 'UNRATED'),
observed_at_unix_millis BIGINT NOT NULL CHECK (observed_at_unix_millis > 0)
);
CREATE INDEX IF NOT EXISTS cognitive_model_qualification_lookup
ON capability.cognitive_model_qualification_observations(provider_instance_id, provider_model_id, observed_at_unix_millis DESC);
GRANT SELECT, INSERT ON capability.cognitive_model_qualification_observations TO capability_role;
RESET ROLE;
COMMIT;
+23
View File
@@ -119,12 +119,32 @@ services:
WORK_POSTGRES_PASSWORD_FILE: /run/secrets/work_db_password
SECRET_BACKEND_ROOT: /run/secrets
TASK_VERIFIER_URL: http://task-verifier:8091
AGENT_EXECUTION_BRIDGE_URL: http://agent-execution-bridge:8092
depends_on:
postgres: { condition: service_healthy }
task-verifier: { condition: service_healthy }
agent-execution-bridge: { condition: service_healthy }
volumes:
- assistant_core_secrets:/run/secrets:ro
agent-execution-bridge:
build: { context: .., dockerfile: deploy/images/runtime.Dockerfile, args: { TARGET: agent-execution-bridge, SOURCE_REVISION: "${ASSISTANT_SOURCE_REVISION:-unknown}" } }
restart: unless-stopped
environment:
LISTEN_ADDR: :8092
COGNITIVE_WORKER_URL: http://cognitive-worker:8083
WORK_GRAPH_RUNTIME_URL: http://work-graph-runtime:8088
TASK_VERIFIER_URL: http://task-verifier:8091
depends_on:
cognitive-worker: { condition: service_started }
task-verifier: { condition: service_healthy }
work-graph-runtime: { condition: service_started }
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8092/healthz >/dev/null"]
interval: 10s
timeout: 3s
retries: 10
task-verifier:
build: { context: .., dockerfile: deploy/images/runtime.Dockerfile, args: { TARGET: task-verifier, SOURCE_REVISION: "${ASSISTANT_SOURCE_REVISION:-unknown}" } }
restart: unless-stopped
@@ -175,6 +195,8 @@ services:
LISTEN_ADDR: :8087
RESEARCH_SOURCE_ROOT: /var/lib/research-sources
RESEARCH_HTTP_ALLOWED_HOSTS: assistant.cin.su,git.cin.su
RESEARCH_SEARCH_URL_TEMPLATE: ${RESEARCH_SEARCH_URL_TEMPLATE:-https://www.bing.com/search?q=%s}
RESEARCH_SEARCH_ALLOWED_HOSTS: ${RESEARCH_SEARCH_ALLOWED_HOSTS:-www.bing.com}
POSTGRES_HOST: postgres
POSTGRES_PORT: "5432"
POSTGRES_DATABASE: ${POSTGRES_DB:-assistant}
@@ -234,6 +256,7 @@ services:
LOCAL_MEDIUM_PROVIDER_URL: ${LOCAL_MEDIUM_PROVIDER_URL:-http://llm-local-api:8080/v1}
LOCAL_MEDIUM_PROVIDER_API_KEY_FILE: ${LOCAL_MEDIUM_PROVIDER_API_KEY_FILE:-}
LOCAL_MEDIUM_PROVIDER_CREDENTIAL_REF: ${LOCAL_MEDIUM_PROVIDER_CREDENTIAL_REF:-credential://domovoy/local-medium/provider-api-key/v1}
LOCAL_GPU_PROVIDER_TIMEOUT: ${LOCAL_GPU_PROVIDER_TIMEOUT:-60s}
LOCAL_LIFEBOAT_PROVIDER_URL: http://cognitive-lifeboat:8086/v1
COGNITIVE_OWNER_PRINCIPAL_ID: domovoy-creator-principal
COGNITIVE_ALLOW_CANARY: "true"
+2 -2
View File
@@ -1,5 +1,5 @@
{
"task_file": "dev/tasks/DEV-000235-model-qualification-probe.json",
"task_id": "DEV-000235",
"task_file": "dev/tasks/DEV-000291-mobile-acceptance-evidence.json",
"task_id": "DEV-000291",
"task_revision": 1
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000236",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"risk_tier": "E",
"base_repo_revision": "b20611590a075e3574efddbdba6e1ace312d11a8",
"intent": "Make the bounded model qualification invocation satisfy the canonical advisory contract and keep a regression test for the required locale.",
"requirement_ids": ["model qualification smoke", "advisory contract"],
"allowed_change_scope": {"modules": ["cognitive-worker"], "paths": ["cmd/cognitive-worker/model_qualification.go", "cmd/cognitive-worker/model_qualification_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000236-qualification-locale-fix.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "LLM-local/**", "modules/work/secrets/**", "contracts/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["Creator advisory request validation"], "may_change": []},
"semantic_owner": "Cognitive worker qualification observation",
"canonical_state": ["none; qualification remains an operational observation"],
"state_transitions": ["invalid advisory invocation -> valid bounded invocation"],
"side_effects_and_privileges": ["read-only bounded provider invocation; no promotion or routing mutation"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["one bounded probe failure remains isolated"],
"assumptions": ["provider credentials remain protected inside cognitive-worker"],
"acceptance_scenarios": ["live qualification invocation passes advisory validation", "quality remains UNRATED"],
"required_tests": ["go test ./cmd/cognitive-worker/...", "go vet ./cmd/cognitive-worker/...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000237",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "be01b6b883c2ce6e9bb47bc851505fe222c46d14",
"risk_tier": "E",
"intent": "Record current authenticated quick acceptance for the five creator-console sections without mutating production state.",
"requirement_ids": ["WP-800", "WP-804", "authenticated UI quick acceptance"],
"allowed_change_scope": {"modules": ["creator-web", "evidence"], "paths": ["docs/evidence/authenticated-ui-section-smoke-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000237-ui-authenticated-section-smoke.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "LLM-local/**", "modules/work/secrets/**", "contracts/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["authenticated creator-console UI"], "may_change": []},
"semantic_owner": "Creator-web acceptance evidence",
"canonical_state": ["evidence only; no runtime state mutation"],
"state_transitions": ["unverified current section -> observed authenticated quick acceptance"],
"side_effects_and_privileges": ["read-only browser inspection; no mutation"],
"persistence_and_migrations": ["evidence artifact only"],
"failure_retry_restart": ["recheck is idempotent and does not change product state"],
"assumptions": ["the authenticated browser session belongs to the authorized creator"],
"acceptance_scenarios": ["five admin sections load authenticated", "no visible section error is present", "full mobile/accessibility/chaos acceptance remains explicitly unclaimed"],
"required_tests": ["go test ./cmd/creator-web/...", "go vet ./cmd/creator-web/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000238",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "0609561dfefb6dce302f56d50bb56bd5a222fb64",
"risk_tier": "E",
"intent": "Record a bounded live proof that the advisory gateway keeps a local lifeboat path when the normal model route cannot be used.",
"requirement_ids": ["Agent Execution local recovery", "model fleet fallback", "false completion prevention"],
"allowed_change_scope": {"modules": ["evidence"], "paths": ["docs/evidence/local-fallback-gateway-live-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000238-local-fallback-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "LLM-local/**", "modules/work/secrets/**", "contracts/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["creator advisory gateway", "durable cognitive usage observation"], "may_change": []},
"semantic_owner": "Evidence only",
"canonical_state": ["no runtime state mutation; read-only evidence capture"],
"state_transitions": ["normal route unavailable or not eligible -> builtin local lifeboat response"],
"side_effects_and_privileges": ["bounded local advisory request; no external egress and no secret exposure"],
"persistence_and_migrations": ["existing cognitive usage observation only"],
"failure_retry_restart": ["live request is bounded and idempotent in effect"],
"assumptions": ["the production database observation is the authoritative route attribution"],
"acceptance_scenarios": ["local response is COMPLETED", "usage evidence identifies builtin-local-lifeboat-v1", "no claim of external bridge or multi-model failover"],
"required_tests": ["go test ./cmd/core-runtime/...", "go vet ./cmd/core-runtime/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000239",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "6061eb5c13e46cec1e60fc104b022394f0d94dee",
"risk_tier": "E",
"intent": "Resolve a registered provider by its stable instance identity when a persisted Capability revision contains stale non-secret endpoint credential metadata, while preserving exact metadata matching first.",
"requirement_ids": ["model fleet/failover", "credential rotation compatibility", "local fallback attribution"],
"allowed_change_scope": {"modules": ["cognitive-worker"], "paths": ["cmd/cognitive-worker/gateway.go", "cmd/cognitive-worker/gateway_registry_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000239-stable-provider-resolution.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "LLM-local/**", "modules/work/secrets/**", "contracts/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["Capability ResolvedCognitiveBinding", "protected provider registration"], "may_change": ["internal provider lookup fallback order"]},
"semantic_owner": "Cognitive worker provider adapter boundary",
"canonical_state": ["no new durable state"],
"state_transitions": ["exact provider metadata match -> stable provider identity match -> no provider"],
"side_effects_and_privileges": ["provider lookup only; secret values remain in protected runtime"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["missing provider remains explicit and uses existing builtin fallback"],
"assumptions": ["ProviderInstanceID is the stable Capability-owned provider identity"],
"acceptance_scenarios": ["stale credential metadata still reaches current registered adapter", "exact metadata match retains priority", "no provider remains nil"],
"required_tests": ["go test ./cmd/cognitive-worker/...", "go vet ./cmd/cognitive-worker/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000240",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "383d4c6e4fd53bb0c34ed358e0d1be6a416da6cc",
"risk_tier": "E",
"intent": "Record live durable evidence of CPU model failure followed by successful local Lifeboat failover after stable provider resolution was deployed.",
"requirement_ids": ["model fleet/failover", "durable usage evidence", "false completion prevention"],
"allowed_change_scope": {"modules": ["evidence"], "paths": ["docs/evidence/cpu-lifeboat-failover-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000240-cpu-lifeboat-failover-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "LLM-local/**", "modules/work/secrets/**", "contracts/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["cognitive advisory gateway", "cognitive usage observations"], "may_change": []},
"semantic_owner": "Evidence only",
"canonical_state": ["no runtime state mutation; evidence captures existing durable observations"],
"state_transitions": ["CPU MODEL_UNAVAILABLE -> builtin local Lifeboat COMPLETED"],
"side_effects_and_privileges": ["bounded LOCAL_ONLY advisory request; no external egress"],
"persistence_and_migrations": ["existing usage observation persistence only"],
"failure_retry_restart": ["the fallback is selected within the bounded gateway request"],
"assumptions": ["provider_instance_id and result_class in capability usage observations are authoritative route attribution"],
"acceptance_scenarios": ["primary failure is durably observed", "fallback completion is durably observed", "HTTP gateway returns COMPLETED", "public health remains OK"],
"required_tests": ["go test ./cmd/cognitive-worker/...", "go vet ./cmd/cognitive-worker/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000241",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "ca0d0020739d8f70e2a3ac04820e32794072ab30",
"risk_tier": "E",
"intent": "Record bounded medium and GPU qualification observations without treating hardware presence or reachability as quality or promotion evidence.",
"requirement_ids": ["model fleet inventory", "provider-neutral qualification", "hardware-neutral routing"],
"allowed_change_scope": {"modules": ["evidence"], "paths": ["docs/evidence/fleet-qualification-2026-08-23-v1.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000241-fleet-qualification-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "LLM-local/**", "modules/work/secrets/**", "contracts/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["provider discovery status", "bounded qualification response"], "may_change": []},
"semantic_owner": "Evidence only",
"canonical_state": ["no runtime state mutation"],
"state_transitions": ["discovered model -> bounded reachability observation -> UNRATED result"],
"side_effects_and_privileges": ["bounded provider invocation; no promotion, activation or routing mutation"],
"persistence_and_migrations": ["evidence artifact only"],
"failure_retry_restart": ["one model timeout does not cancel or misclassify other tier evidence"],
"assumptions": ["provider discovery IDs are the exact IDs returned by the live worker"],
"acceptance_scenarios": ["medium reachability records latency", "GPU timeout is explicit", "both results remain UNRATED", "GPU does not receive an implicit priority"],
"required_tests": ["go test ./cmd/cognitive-worker/...", "go vet ./cmd/cognitive-worker/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000242",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "e4ba1cb5fabf930396b2aa9f95d3b4b205e6319d",
"risk_tier": "E",
"intent": "Persist provider-neutral model qualification observations through the Capability owner while keeping quality unrated until independent task evaluation.",
"requirement_ids": ["durable qualification evidence", "model fleet/failover", "quality separated from reachability"],
"allowed_change_scope": {"modules": ["capability", "core-runtime", "cognitive-worker", "database"], "paths": ["adapters/postgres/capability_repository.go", "cmd/cognitive-worker/main.go", "cmd/cognitive-worker/model_qualification.go", "cmd/cognitive-worker/model_qualification_test.go", "cmd/core-runtime/main.go", "contracts/capability/model_qualification.go", "database/migrations/045_capability_model_qualification_observations.sql", "modules/capability/ports/repository.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000242-durable-qualification-observations.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "deploy/**", "LLM-local/**", "modules/work/secrets/**", "contracts/work/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 1},
"contracts": {"consume": ["bounded qualification result", "Capability repository boundary"], "may_change": ["typed model qualification observation contract", "internal core persistence route"]},
"semantic_owner": "Capability owns qualification evidence; cognitive-worker produces observations",
"canonical_state": ["cognitive_model_qualification_observations"],
"state_transitions": ["probe -> REACHABLE/RESPONDED_WITHOUT_QUALIFICATION/UNAVAILABLE; category remains UNRATED"],
"side_effects_and_privileges": ["bounded provider call and capability-owned INSERT; no promotion, activation or secret persistence"],
"persistence_and_migrations": ["migration 045 creates capability-owned observation table and index"],
"failure_retry_restart": ["observation persistence failure returns bounded gateway error; duplicate IDs are idempotent"],
"assumptions": ["independent task evaluation remains the only quality/promotion evidence"],
"acceptance_scenarios": ["qualification route persists one observation per probed model", "invalid quality/category is rejected", "prompt/output/error text is not persisted", "existing qualification tests remain valid"],
"required_tests": ["go test ./cmd/cognitive-worker/... ./cmd/core-runtime/... ./modules/capability/... ./adapters/postgres/...", "go vet ./cmd/cognitive-worker/... ./cmd/core-runtime/... ./modules/capability/... ./adapters/postgres/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "REQUIRES_MIGRATION_DECISION"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000243",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "47327cc6d853da9084c96e3bf48502266706ca57",
"risk_tier": "E",
"intent": "Make Capability selection consume the latest durable qualification observation for fresh unavailability exclusion and transparent latency rating, without granting quality or activation.",
"requirement_ids": ["qualification selection loop", "hardware-neutral routing", "quality separated from reachability"],
"allowed_change_scope": {"modules": ["capability", "adapters/postgres"], "paths": ["adapters/postgres/capability_repository.go", "contracts/capability/cognitive_registry.go", "modules/capability/domain/cognitive_registry.go", "modules/capability/domain/model_rating_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000243-qualification-selection-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "LLM-local/**", "modules/work/secrets/**", "contracts/work/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["durable ModelQualificationObservation"], "may_change": ["resolved binding evidence projection"]},
"semantic_owner": "Capability selection and rating",
"canonical_state": ["qualification observation remains evidence; no activation mutation"],
"state_transitions": ["fresh UNAVAILABLE observation -> candidate excluded", "stale UNAVAILABLE observation -> discovery state remains authoritative", "REACHABLE observation -> latency evidence only"],
"side_effects_and_privileges": ["read-only projection and deterministic selection"],
"persistence_and_migrations": ["uses migration 045 from DEV-000242"],
"failure_retry_restart": ["missing observation preserves existing behavior"],
"assumptions": ["provider/model observed timestamps define freshness boundary"],
"acceptance_scenarios": ["fresh unavailable candidate is not selected", "stale unavailable candidate is not permanently poisoned", "quality remains task-evaluation controlled"],
"required_tests": ["go test ./modules/capability/... ./adapters/postgres/...", "go vet ./modules/capability/... ./adapters/postgres/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,26 @@
{
"task_id": "DEV-000244",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "c81e2269e157c1a41b567d79d93bbbe49dd4b134",
"risk_tier": "E",
"intent": "Expose bounded health and status evidence for the durable reflection worker without changing reflection semantics or authority.",
"requirement_ids": ["learning reflection loop", "runtime observability", "fast operational verification"],
"allowed_change_scope": {
"modules": ["epistemics"],
"paths": ["cmd/reflection-worker/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000244-reflection-worker-observability.json"]
},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "LLM-local/**", "modules/work/secrets/**", "contracts/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing reflection repository port", "runtime health conventions"], "may_change": ["worker operational HTTP surface"]},
"semantic_owner": "Epistemics reflection worker runtime",
"canonical_state": ["none; status is derived operational evidence"],
"state_transitions": ["cycle start/complete/failure are reported without changing candidate lifecycle rules"],
"side_effects_and_privileges": ["read-only health/status endpoint; existing repository writes unchanged"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["status resets on process restart; durable candidate state remains authoritative"],
"assumptions": ["internal production network is sufficient for worker status checks"],
"acceptance_scenarios": ["health endpoint returns 200", "status exposes loop mode and latest cycle", "reflection loop still resolves candidates"],
"required_tests": ["go test ./cmd/reflection-worker", "go vet ./cmd/reflection-worker", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000245",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "ea90bf0fabea43b735dcb901a2da2733db216369",
"risk_tier": "E",
"intent": "Expose live durable agent-worker cycle and recovery evidence through the existing internal status surface without changing dispatch, lease, executor, or canonical semantics.",
"requirement_ids": ["Agent Execution runtime", "recovery observability", "fast operational verification"],
"allowed_change_scope": {"modules": ["work"], "paths": ["cmd/agent-worker/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000245-agent-worker-recovery-status.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/secrets/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing WorkerCycleResult", "existing worker status HTTP surface"], "may_change": ["derived worker status response fields"]},
"semantic_owner": "Work agent-worker runtime observability",
"canonical_state": ["none; status is derived operational evidence"],
"state_transitions": ["cycle start -> completed/failed status only"],
"side_effects_and_privileges": ["read-only internal status endpoint; no new privilege"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["status is process-local evidence and resets on restart; durable recovery remains repository-owned"],
"assumptions": ["existing status address is reachable from creator-web and internal diagnostics"],
"acceptance_scenarios": ["status reports cycle completion", "status reports recovered claims", "cycle error is visible without exposing secrets", "existing executor mode remains accurate"],
"required_tests": ["go test ./cmd/agent-worker", "go vet ./cmd/agent-worker", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000246",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "355ba7db5621a11b2c4e0827d9301ab7fdb2d419",
"risk_tier": "E",
"intent": "Expose read-only durable Work Graph runtime counts for operational verification without changing graph scheduling, fan-out/fan-in, verifier, or replan semantics.",
"requirement_ids": ["Multi-Agent Work Graph", "fan-out/fan-in observability", "fast operational verification"],
"allowed_change_scope": {"modules": ["work"], "paths": ["adapters/postgres/work_repository.go", "cmd/work-graph-runtime/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000246-work-graph-runtime-status.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/secrets/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["work-owned graph and dispatch queue tables", "existing runtime health conventions"], "may_change": ["internal read-only status response"]},
"semantic_owner": "Work Graph runtime observability",
"canonical_state": ["none; counts are derived projections"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["read-only aggregate queries only"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["status fails closed with 503 when work database is unavailable"],
"assumptions": ["status is consumed by internal diagnostics and can be queried from the production network"],
"acceptance_scenarios": ["readyz remains database-backed", "status returns graph state and queue counts", "status does not mutate canonical state"],
"required_tests": ["go test ./cmd/work-graph-runtime ./adapters/postgres", "go vet ./cmd/work-graph-runtime ./adapters/postgres", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000247",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "e70be0103a03c31f7ecea851fd1aeabcb7abcc55",
"risk_tier": "E",
"intent": "Expose read-only Truth/Research evidence counts from durable truth bundles without changing epistemic evaluation or belief semantics.",
"requirement_ids": ["Truth/Research runtime", "Observation-Claim-Belief evidence visibility", "fast operational verification"],
"allowed_change_scope": {"modules": ["epistemics"], "paths": ["adapters/postgres/epistemics_repository.go", "cmd/research-runtime/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000247-research-runtime-status.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/secrets/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["epistemics.truth_bundles", "existing research health conventions"], "may_change": ["internal read-only status response"]},
"semantic_owner": "Epistemics Truth/Research runtime observability",
"canonical_state": ["none; counts are derived projections"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["read-only aggregate queries only"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["status fails closed with 503 when epistemics database is unavailable"],
"assumptions": ["truth bundle migration is present in production"],
"acceptance_scenarios": ["readyz remains database-backed", "status reports bundle, claim verification, belief and freshness counts", "status does not mutate epistemic state"],
"required_tests": ["go test ./cmd/research-runtime ./adapters/postgres", "go vet ./cmd/research-runtime ./adapters/postgres", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000248",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "cdc72e17be46c843737eabcd654cb69ae32d6268",
"risk_tier": "E",
"intent": "Diagnose and repair the live Work Graph task-evaluation scheduling path exposed by the production canary, preserving durable fan-out and callback semantics.",
"requirement_ids": ["live task evaluation", "model quality qualification", "Work Graph integration"],
"allowed_change_scope": {"modules": ["work", "capability"], "paths": ["cmd/work-graph-runtime/**", "adapters/postgres/work_repository.go", "modules/work/application/**", "modules/work/domain/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000248-live-task-evaluation-scheduling.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/secrets/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing TaskEvaluationGraphRequest", "existing AgentDispatchIntent", "existing Work Graph repository"], "may_change": ["diagnostic logging and local integration behavior"]},
"semantic_owner": "Work Graph task-evaluation scheduling",
"canonical_state": ["existing graph, dispatch and evaluation records only"],
"state_transitions": ["schedule -> queue -> evaluator -> verifier -> evidence callback"],
"side_effects_and_privileges": ["existing work/capability owner writes; no new privileges"],
"persistence_and_migrations": ["none unless current schema mismatch is proven"],
"failure_retry_restart": ["failed scheduling leaves no partial graph or dispatch mutation; durable retries remain existing worker behavior"],
"assumptions": ["production canary payload is provider-neutral and contains no secret"],
"acceptance_scenarios": ["live general evaluation graph schedules", "worker executes and verifier accepts one case", "TaskEvaluation persists", "graph callback reaches terminal state"],
"required_tests": ["go test ./cmd/work-graph-runtime ./modules/work/application ./modules/work/domain ./adapters/postgres", "go vet ./cmd/work-graph-runtime ./modules/work/application ./modules/work/domain ./adapters/postgres", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke", "live task-evaluation canary"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000249",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "f90ed7af35f9f19c72039812272640ae1d63635b",
"risk_tier": "E",
"intent": "Make task-evaluation execution use the existing cognitive model resolution and local fallback path without recording unsupported output as qualification evidence.",
"requirement_ids": ["model fleet/failover", "live task evaluation", "local recovery"],
"allowed_change_scope": {"modules": ["capability", "work"], "paths": ["cmd/cognitive-worker/**", "cmd/agent-worker/**", "modules/capability/domain/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000249-task-evaluation-model-failover.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/**", "modules/work/secrets/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing CognitiveRequirement", "existing ResolvedCognitiveBinding", "existing TaskEvaluationExecutionRequest/Response"], "may_change": []},
"semantic_owner": "capability cognitive execution adapter",
"canonical_state": ["existing cognitive usage observations and task evaluation records only"],
"state_transitions": ["candidate invocation -> bounded fallback candidate -> verified evaluation or UNKNOWN"],
"side_effects_and_privileges": ["existing cognitive provider calls and capability observations; no new privileges"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["provider failure excludes the failed binding for the current bounded execution; all candidates unavailable returns UNKNOWN without qualification"],
"assumptions": ["fallback resolution remains governed by the existing capability resolver and data-policy checks"],
"acceptance_scenarios": ["task evaluation invokes active candidate", "provider-unavailable candidate is excluded and local candidate is attempted", "empty/non-completed provider response never becomes supported evidence", "all candidates unavailable produces durable UNKNOWN evaluation and terminal graph callback"],
"required_tests": ["go test ./cmd/cognitive-worker ./modules/capability/domain", "go vet ./cmd/cognitive-worker ./modules/capability/domain", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke", "live task-evaluation canary"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000250",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "2b8cea5aa8f43f597b2af7d92c2eaa305d68256e",
"risk_tier": "E",
"intent": "Preserve the scheduled Work Graph revision in task-evaluation dispatch context so verifier callbacks complete the current immutable graph revision and reach terminal state.",
"requirement_ids": ["live task evaluation", "Work Graph integration", "durable callback lineage"],
"allowed_change_scope": {"modules": ["work"], "paths": ["modules/work/application/**", "modules/work/domain/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000250-task-evaluation-terminal-callback.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/cognitive-worker/**", "cmd/agent-worker/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing WorkGraph scheduler", "existing TaskEvaluationDispatchContext", "existing callback contract"], "may_change": []},
"semantic_owner": "Work Graph task-evaluation scheduling",
"canonical_state": ["existing immutable graph revisions and node states only"],
"state_transitions": ["scheduled revision -> evaluator callback -> completed revision -> terminal graph"],
"side_effects_and_privileges": ["existing work and capability owner writes; no new privileges"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["callback uses the revision that contains the leased node; duplicate callback remains protected by existing immutable revision/idempotency behavior"],
"assumptions": ["scheduler increments the draft graph revision exactly once before enqueueing dispatches"],
"acceptance_scenarios": ["live task-evaluation callback updates scheduled revision", "unknown evaluation reaches graph terminal state", "successful verified evaluation reaches graph terminal state", "callback lineage remains serializable"],
"required_tests": ["go test ./modules/work/application ./modules/work/domain", "go vet ./modules/work/application ./modules/work/domain", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke", "live task-evaluation canary"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000251",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "c1cde67bcfff8e0e80553ce0562f9841f6c00b30",
"risk_tier": "E",
"intent": "Prevent the reflection worker from resolving cognition-required candidates before a governed reflection path evaluates them.",
"requirement_ids": ["learning reflection loop", "durable candidate routing", "no false resolution"],
"allowed_change_scope": {"modules": ["epistemics"], "paths": ["adapters/postgres/epistemics_repository.go", "cmd/reflection-worker/**", "modules/epistemics/domain/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000251-reflection-cognition-routing.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/**", "cmd/cognitive-worker/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing ReflectionCandidate state and requires_cognition field", "existing Epistemics repository"], "may_change": []},
"semantic_owner": "Epistemics reflection candidate lifecycle",
"canonical_state": ["existing epistemics.reflection_candidates rows only"],
"state_transitions": ["OPEN deterministic -> EVALUATING -> RESOLVED", "OPEN cognition-required remains OPEN until governed evaluator"],
"side_effects_and_privileges": ["existing epistemics owner updates only; no cognition call or proposal promotion"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["cognition-required candidates remain durable and claimable only by a future cognition evaluator; stale deterministic claims recover"],
"assumptions": ["reflection worker is the deterministic close loop and must not impersonate a cognitive evaluator"],
"acceptance_scenarios": ["worker resolves deterministic candidate", "worker does not claim cognition-required candidate", "OPEN cognition candidate survives restart", "status remains healthy when only cognition candidates are pending"],
"required_tests": ["go test ./cmd/reflection-worker ./adapters/postgres ./modules/epistemics/domain", "go vet ./cmd/reflection-worker ./adapters/postgres ./modules/epistemics/domain", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke", "live reflection-worker status"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000252",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "c925089fd2304213758d2721a0592ca73d765047",
"risk_tier": "E",
"intent": "Add a local governed verification executor that runs only typed, bounded checks inside an isolated worktree and returns evidence without source, secret, production or promotion authority.",
"requirement_ids": ["governed self-development", "isolated verification", "independent test evidence"],
"allowed_change_scope": {"modules": ["governed-development"], "paths": ["adapters/developmentexecutor/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000252-local-governed-verification-executor.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/**", "cmd/core-runtime/**", "cmd/agent-worker/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing DevelopmentScaffold semantics", "typed local check plan"], "may_change": []},
"semantic_owner": "governed development verification adapter",
"canonical_state": ["none; executor returns evidence to a future gate owner"],
"state_transitions": ["typed plan -> validated sandbox checks -> immutable result summary"],
"side_effects_and_privileges": ["read-only source/test execution in supplied isolated worktree; no secrets, production, protected main or promotion access"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["each check has an explicit result; failed or cancelled checks never become verification success; rerun creates a new result"],
"assumptions": ["worktree isolation and gate persistence are supplied by the surrounding governed control plane"],
"acceptance_scenarios": ["allowed go test/vet and git diff check plans validate", "shell injection and unsupported commands reject", "protected or production worktree rejects", "failed check returns non-success evidence", "result contains no command output or secret material"],
"required_tests": ["go test ./adapters/developmentexecutor", "go vet ./adapters/developmentexecutor", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000253",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "f2e520e31e27563a17f66c73ff68b35bc93963a0",
"risk_tier": "E",
"intent": "Connect the local bounded verification executor to the authenticated durable DevelopmentGate ISOLATED-to-VERIFIED transition with optimistic revision and independent verifier checks.",
"requirement_ids": ["governed self-development", "independent verification", "durable gate progression"],
"allowed_change_scope": {"modules": ["governed-development"], "paths": ["cmd/core-runtime/development_gate.go", "cmd/core-runtime/main.go", "cmd/core-runtime/development_gate_test.go", "adapters/developmentexecutor/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000253-development-gate-verifier-integration.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/**", "cmd/agent-worker/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing authenticated Creator session", "existing DevelopmentGate CAS store", "local developmentexecutor"], "may_change": ["internal authenticated verifier route" ]},
"semantic_owner": "Work-owned DevelopmentGate controller",
"canonical_state": ["existing work.development_gates row only"],
"state_transitions": ["ISOLATED -> VERIFIED only after bounded checks pass and verifier run differs from producer runs"],
"side_effects_and_privileges": ["read-only checks in isolated worktree; one existing Work-owner CAS update; no source mutation, secrets, production or promotion authority"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["stale gate revision conflicts; failed checks leave gate ISOLATED; rerun uses a new verifier run/evidence reference"],
"assumptions": ["worktree has already been created by the governed isolation step"],
"acceptance_scenarios": ["unauthenticated verifier request denied", "non-ISOLATED gate denied", "same producer/verifier run denied", "failed check leaves gate unchanged", "passing checks persist VERIFIED with evidence and CAS"],
"required_tests": ["go test ./cmd/core-runtime ./adapters/developmentexecutor", "go vet ./cmd/core-runtime ./adapters/developmentexecutor", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent", "CI", "production health smoke", "authenticated gate verifier live smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000254",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "3bfe76278b7e44965cbb0e3b4ac2911ad4a4bbae",
"risk_tier": "E",
"intent": "Expose typed authenticated transitions for durable governed self-development canary, promotion and rollback evidence with optimistic revision checks.",
"requirement_ids": ["governed self-development", "durable canary evidence", "promotion and rollback lineage"],
"allowed_change_scope": {"modules": ["governed-development"], "paths": ["cmd/core-runtime/development_gate.go", "cmd/core-runtime/main.go", "cmd/core-runtime/development_gate_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000254-development-gate-canary-promotion-rollback.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/work/**", "cmd/agent-worker/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing authenticated Creator session", "existing DevelopmentGate CAS store", "existing governed stage machine"], "may_change": ["internal authenticated canary/promotion/rollback routes"]},
"semantic_owner": "Work-owned DevelopmentGate controller",
"canonical_state": ["existing work.development_gates row only"],
"state_transitions": ["VERIFIED -> CANARY", "CANARY -> PROMOTED", "CANARY -> ROLLED_BACK"],
"side_effects_and_privileges": ["one existing Work-owner CAS update per transition; no source mutation, secrets, external mutation or deployment authority"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["stale revisions conflict; missing evidence is rejected; terminal stages cannot be reopened; retry uses a new reference and current revision"],
"assumptions": ["canary execution and deployment evidence are produced by their owning executor; this task records only typed evidence and advances the durable gate"],
"acceptance_scenarios": ["unauthenticated transition denied", "missing evidence denied", "stale revision denied", "canary evidence advances VERIFIED to CANARY", "promotion evidence advances CANARY to PROMOTED", "rollback evidence advances CANARY to ROLLED_BACK", "terminal gates cannot be reopened"],
"required_tests": ["go test ./cmd/core-runtime", "go vet ./cmd/core-runtime", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "independent verifier", "CI", "production health smoke", "authenticated transition smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000255",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "12ab124411771fbed16e1f1935b97b1d0700a7b0",
"risk_tier": "E",
"intent": "Refresh the audit status projection and evidence matrix from current production and authenticated UI observations without changing runtime behavior.",
"requirement_ids": ["task lineage and status synchronization", "auditable production evidence"],
"allowed_change_scope": {"modules": ["governance-documentation"], "paths": ["docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-23-v4.json", "docs/evidence/authenticated-ui-acceptance-2026-08-23-v3.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000255-refresh-live-audit-status-projection.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["current production health and revision evidence", "current authenticated Creator UI observation"], "may_change": ["documentation evidence projection only"]},
"semantic_owner": "Governance evidence projection",
"canonical_state": ["none; evidence files are derived audit artifacts"],
"state_transitions": ["supersede stale status projection with timestamped current evidence"],
"side_effects_and_privileges": ["documentation-only; no runtime, database, secret or external mutation"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["retain prior evidence as historical; newer evidence supersedes it without rewriting history"],
"assumptions": ["mobile viewport cannot be emulated by the current in-app browser control surface, so mobile remains explicitly unverified"],
"acceptance_scenarios": ["status names current deployed revision", "model counts match current authenticated UI", "external bridge and mobile limitations remain explicit", "no secret values are recorded"],
"required_tests": ["go test ./cmd/core-runtime", "go vet ./cmd/core-runtime", "independent evidence review", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000256",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "3d97b348f74741b9aca2aa8d77f2e66b9f5760c2",
"risk_tier": "E",
"intent": "Expose observed qualification latency and evidence strength in provider-neutral model ratings without inferring quality, activation or authority.",
"requirement_ids": ["model fleet/failover", "provider-neutral rating", "quality must remain unrated without task evaluation"],
"allowed_change_scope": {"modules": ["capability"], "paths": ["modules/capability/domain/cognitive_registry.go", "modules/capability/domain/model_rating_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000256-model-rating-reachability-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing ModelQualificationObservation", "existing provider-neutral ModelRating"], "may_change": ["derived rating values for reachability-only evidence"]},
"semantic_owner": "Capability rating derivation",
"canonical_state": ["none; ModelRating is derived from existing evidence"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["pure deterministic derivation only"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["missing or unavailable qualification remains UNRATED with no quality claim"],
"assumptions": ["reachability and latency are operational evidence, not task quality"],
"acceptance_scenarios": ["reachable qualification exposes latency/evidence scores", "quality category remains UNRATED without supported task evaluation", "provider kind and hardware do not affect rating", "unavailable qualification does not create positive rating"],
"required_tests": ["go test ./modules/capability/domain", "go vet ./modules/capability/domain", "independent evidence review", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000257",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "8868258afd4e58edfa9c71e3fbf79793cd32397b",
"risk_tier": "E",
"intent": "Record a bounded operations acceptance smoke for authenticated fleet and agent-worker status surfaces and unauthenticated protection.",
"requirement_ids": ["UI/operations acceptance", "truthful degraded state", "authenticated read-only status"],
"allowed_change_scope": {"modules": ["governance-documentation"], "paths": ["docs/evidence/operations-acceptance-2026-08-23-v1.json", "docs/PROJECT_STATUS.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000257-operations-acceptance-fast-smoke.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["authenticated Creator UI", "fleet readiness read API", "agent-worker status read API"], "may_change": ["evidence projection only"]},
"semantic_owner": "Governance evidence projection",
"canonical_state": ["none"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["read-only API/UI checks; no mutation"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["failed checks remain explicit and do not alter runtime state"],
"assumptions": ["deep UI, long-running delegation and mobile viewport acceptance remain separate tests"],
"acceptance_scenarios": ["unauthenticated fleet status returns 401", "unauthenticated agent status returns 401", "authenticated fleet refresh displays selected tier and degraded reason", "authenticated agent refresh displays local fallback and bridge-unavailable reason"],
"required_tests": ["go test ./cmd/creator-web", "go vet ./cmd/creator-web", "independent evidence review", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "CI", "production health smoke", "authenticated operations smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000258",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "5e8286695f0da3f3819cdc17ced6525237512e6e",
"risk_tier": "E",
"intent": "Add a fast static responsive regression guard for the authenticated Creator console while retaining the requirement for real mobile visual acceptance.",
"requirement_ids": ["UI responsive safety", "authenticated console acceptance"],
"allowed_change_scope": {"modules": ["creator-web"], "paths": ["cmd/creator-web/responsive_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000258-responsive-console-regression-guard.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/core-runtime/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["embedded Creator console HTML/CSS"], "may_change": ["tests only"]},
"semantic_owner": "Creator Web presentation tests",
"canonical_state": ["none"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["test-only static inspection"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["test fails when responsive guardrails disappear"],
"assumptions": ["static guard is not a substitute for browser visual acceptance; real mobile viewport evidence remains required when browser capability exists"],
"acceptance_scenarios": ["viewport metadata exists", "mobile media block exists", "key panels collapse to safe single-column layouts", "only section navigation owns horizontal scrolling"],
"required_tests": ["go test ./cmd/creator-web", "go vet ./cmd/creator-web", "independent evidence review", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000259",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "d4b541612647acd826477c8cbea2bc9976049f03",
"risk_tier": "E",
"intent": "Refresh the audit status projection after the responsive regression guard and current production deployment.",
"requirement_ids": ["task lineage and status synchronization", "auditable UI acceptance state"],
"allowed_change_scope": {"modules": ["governance-documentation"], "paths": ["docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-23-v5.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000259-refresh-post-responsive-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["current production health/revision", "responsive regression test evidence", "authenticated operations smoke"], "may_change": ["status projection only"]},
"semantic_owner": "Governance evidence projection",
"canonical_state": ["none"],
"state_transitions": ["supersede stale projection without rewriting historical evidence"],
"side_effects_and_privileges": ["documentation-only"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["historical evidence remains immutable; current projection can be refreshed"],
"assumptions": ["real mobile viewport visual evidence remains unavailable"],
"acceptance_scenarios": ["status names DEV-000258 as latest completed", "matrix names d4b54161 production revision", "responsive static guard is recorded", "mobile visual limitation remains explicit"],
"required_tests": ["go test ./cmd/creator-web", "go vet ./cmd/creator-web", "independent evidence review", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000260",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "a9dbc3b",
"risk_tier": "E",
"intent": "Record deep authenticated UI and operations acceptance without changing runtime behavior.",
"requirement_ids": ["authenticated deep acceptance evidence", "truthful operational status"],
"allowed_change_scope": {"modules": ["governance-documentation"], "paths": ["docs/evidence/deep-authenticated-acceptance-2026-08-23-v1.json", "docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-23-v5.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000260-deep-authenticated-acceptance.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["authenticated browser UI", "current production fleet/agent/learning status"], "may_change": ["evidence projection only"]},
"semantic_owner": "Governance evidence projection",
"canonical_state": ["none"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["documentation-only; browser acceptance used no mutation"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["evidence remains immutable; status projection can be refreshed"],
"assumptions": ["mobile viewport capability remains unavailable", "no delegation ID was present in the authenticated session"],
"acceptance_scenarios": ["authenticated chat reply received", "diagnostics 7/7 after refresh", "fleet and agent refresh completed", "learning refresh completed", "action history visible", "no secrets exposed"],
"required_tests": ["go test ./cmd/creator-web ./cmd/devctl", "go vet ./cmd/creator-web", "go run ./cmd/devctl preflight -task dev/tasks/DEV-000260-deep-authenticated-acceptance.json", "go run ./cmd/devctl check-diff -task dev/tasks/DEV-000260-deep-authenticated-acceptance.json", "independent evidence review", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000261",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "d2dd38b",
"risk_tier": "E",
"intent": "Connect unresolved model-evaluation evidence to a bounded additional Work Graph and durable dispatch schedule.",
"requirement_ids": ["model fleet/failover", "general-first qualification", "adaptive additional tests", "durable parallel dispatch"],
"allowed_change_scope": {"modules": ["work", "capability-evaluation"], "paths": ["modules/work/application/graph_runtime.go", "modules/work/application/task_evaluation_dispatch_test.go", "cmd/work-graph-runtime/main.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000261-durable-additional-task-evaluation-scheduling.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/core-runtime/**", "cmd/creator-web/**", "cmd/agent-worker/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["TaskEvaluationExpansionRequest", "UnresolvedTaskEvidence", "Work Graph admission and dispatch ports"], "may_change": ["application orchestration function only"]},
"semantic_owner": "Work application schedules; Capability domain decides qualification expansion",
"canonical_state": ["work graph and dispatch queue remain durable owners"],
"state_transitions": ["completed unresolved general evaluation -> bounded additional graph -> leased/queued specialist dispatch"],
"side_effects_and_privileges": ["durable graph/dispatch persistence through existing ports; no provider or external mutation"],
"persistence_and_migrations": ["none; existing graph and dispatch persistence is reused"],
"failure_retry_restart": ["existing graph admission and worker lease/recovery semantics remain authoritative; qualified candidate suppresses expansion"],
"assumptions": ["caller supplies fresh dispatch identities and bounded additional benchmark version", "independent verifier remains required before qualification promotion"],
"acceptance_scenarios": ["unresolved general result creates exactly two bounded additional cases", "additional graph preserves parent evidence lineage", "fresh dispatch intent is queued through existing scheduler", "qualified candidate creates no additional graph"],
"required_tests": ["go test ./modules/work/application ./modules/work/domain ./modules/capability/domain", "go vet ./modules/work/application ./modules/work/domain ./modules/capability/domain", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "independent evidence review", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000262",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "b789878",
"risk_tier": "E",
"intent": "Record production evidence for the deployed adaptive additional task-evaluation scheduling hand-off.",
"requirement_ids": ["auditable model qualification lifecycle", "production runtime evidence"],
"allowed_change_scope": {"modules": ["governance-documentation"], "paths": ["docs/evidence/additional-evaluation-runtime-live-2026-08-23-v1.json", "docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-23-v5.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000262-additional-evaluation-live-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["production work-graph health/readiness and route response"], "may_change": ["status projection only"]},
"semantic_owner": "Governance evidence projection",
"canonical_state": ["none"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["documentation-only; live probe used rejected empty request"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["historical evidence remains immutable; status projection can be refreshed"],
"assumptions": ["route validation is not proof of a successful graph mutation"],
"acceptance_scenarios": ["production work-graph health is OK", "readiness reports PostgreSQL available", "additional scheduling route rejects incomplete payload with 422", "no secret value appears"],
"required_tests": ["go test ./cmd/creator-web", "go vet ./cmd/creator-web", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "independent evidence review", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000263",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "1f20685",
"risk_tier": "E",
"intent": "Record the current Domovoy-owned GPU qualification result without promoting unreachable models.",
"requirement_ids": ["model fleet/failover", "quality separated from reachability", "hardware-neutral routing"],
"allowed_change_scope": {"modules": ["governance-documentation"], "paths": ["docs/evidence/gpu-qualification-runtime-2026-08-23-v2.json", "docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-23-v5.json", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000263-gpu-qualification-runtime-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["Domovoy cognitive-worker qualification response", "provider-neutral model rating"], "may_change": ["status projection only"]},
"semantic_owner": "Governance evidence projection",
"canonical_state": ["none"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["read-only bounded model qualification; no activation or promotion"],
"persistence_and_migrations": ["qualification observations are persisted by existing Capability owner route; this task adds only evidence projection"],
"failure_retry_restart": ["timeout remains UNRATED and fallback remains active"],
"assumptions": ["GPU endpoint and protected credential were resolved inside cognitive-worker"],
"acceptance_scenarios": ["four exact discovered model IDs recorded", "all timeouts remain UNRATED", "GPU does not become preferred", "no secret value appears"],
"required_tests": ["go test ./cmd/creator-web", "go vet ./cmd/creator-web", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "independent evidence review", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000264",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "c8dc9ed",
"risk_tier": "E",
"intent": "Report agent-worker readiness for the bounded local capability while keeping unsupported work durably waiting and external bridge state explicit.",
"requirement_ids": ["Agent Execution runtime and recovery", "local-first fallback", "truthful operational readiness"],
"allowed_change_scope": {"modules": ["agent-worker"], "paths": ["cmd/agent-worker/status.go", "cmd/agent-worker/status_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000264-capability-aware-agent-worker-readiness.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["agent-worker status response", "local task-evaluation capability"], "may_change": ["readiness semantics only"]},
"semantic_owner": "Agent worker operational status",
"canonical_state": ["none"],
"state_transitions": ["local capability unavailable -> recovery-only; local task-evaluation capability available -> capability-ready; external bridge configured -> execution-ready"],
"side_effects_and_privileges": ["status/readiness response only"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["unsupported work remains WAITING; stale claims remain recoverable"],
"assumptions": ["ready means at least one advertised capability can accept work, not that every arbitrary agent operation is supported"],
"acceptance_scenarios": ["recovery-only remains not ready", "local task-evaluation reports ready without claiming external bridge", "configured bridge remains ready", "local capability and unsupported-work distinction is explicit"],
"required_tests": ["go test ./cmd/agent-worker/...", "go vet ./cmd/agent-worker/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "independent evidence review", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000265",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "102a57a",
"risk_tier": "E",
"intent": "Record live capability-aware agent-worker readiness after the local executor rollout.",
"requirement_ids": ["Agent Execution runtime and recovery", "truthful operational status"],
"allowed_change_scope": {"modules": ["governance-documentation"], "paths": ["docs/evidence/agent-worker-capability-readiness-2026-08-24-v1.json", "docs/PROJECT_STATUS.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000265-agent-worker-capability-readiness-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["live agent-worker status and readyz", "public Assistant health"], "may_change": ["status projection only"]},
"semantic_owner": "Governance evidence projection",
"canonical_state": ["none"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["documentation-only; read-only runtime probes"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["unsupported work remains durable waiting; recovery counters remain observable"],
"assumptions": ["capability-ready semantics are narrower than generic agent execution readiness"],
"acceptance_scenarios": ["local task-evaluation capability reports READY", "external bridge remains false", "readyz is READY", "public health is OK", "no secret value appears"],
"required_tests": ["go test ./cmd/creator-web", "go vet ./cmd/creator-web", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "independent evidence review", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,26 @@
{
"task_id": "DEV-000266",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "c4f3f02",
"risk_tier": "E",
"intent": "Provide an independently running local Agent Execution bridge for the bounded task-evaluation capability, while keeping unsupported generic work durably waiting.",
"requirement_ids": ["WP-301", "Agent Execution runtime and recovery"],
"allowed_change_scope": {
"modules": ["agent-worker", "agentbridge"],
"paths": ["adapters/agentbridge/**", "cmd/agent-worker/**", "cmd/agent-execution-bridge/**", "deploy/docker-compose.prod.yml", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000266-local-agent-execution-bridge.json", "docs/evidence/**", "docs/PROJECT_STATUS.md"]
},
"forbidden_change_scope": {"paths": ["architecture/**", "database/**", "security/**", "contracts/authority/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 1, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["AgentExecutionRequest", "AgentExecutionResult", "TaskEvaluationExecutionResponse", "TaskCaseVerificationResponse", "TaskEvaluationEvidenceCallback"], "may_change": []},
"semantic_owner": "work dispatches; capability owns evaluation evidence",
"canonical_state": ["existing work lease/callback state", "existing capability evaluation evidence"],
"state_transitions": ["durable dispatch -> bridge request -> bounded evaluation/verifier/callback -> completed or explicit waiting"],
"side_effects_and_privileges": ["outbound HTTP only to cognitive-worker, task-verifier and work-graph-runtime; no database access; no credentials forwarded"],
"persistence_and_migrations": ["reuse existing owners; no migration"],
"failure_retry_restart": ["bridge or dependency failure returns an executor error; worker retains durable retry/recovery evidence", "unsupported intent returns WAITING and is never completed"],
"assumptions": ["local bridge is a bounded fallback, not a universal Codex executor", "generic work remains pending until a separately qualified capability is installed"],
"acceptance_scenarios": ["bridge receives typed request", "task-evaluation reaches cognitive worker, verifier and graph callback", "unsupported generic intent returns WAITING", "worker can use bridge-backed execution without changing lease/recovery semantics"],
"required_tests": ["unit", "runtime HTTP", "architecture", "go test", "go vet", "preflight", "check-diff", "check-architecture", "independent", "docker compose config", "git diff --check"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000267",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "c459cd5",
"risk_tier": "E",
"intent": "Record fresh Domovoy-owned GPU qualification and authenticated UI viewport evidence without promoting models or changing runtime behavior.",
"requirement_ids": ["model fleet/failover", "UI/operations acceptance"],
"allowed_change_scope": {"modules": ["governance-documentation"], "paths": ["docs/evidence/gpu-qualification-runtime-2026-08-24-v1.json", "docs/evidence/audit-completion-matrix-2026-08-24-v7.json", "docs/PROJECT_STATUS.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000267-refresh-gpu-and-ui-gap-evidence.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**", "cmd/**", "modules/**", "adapters/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["Domovoy cognitive-worker GPU qualification response", "authenticated browser viewport observation"], "may_change": ["status projection only"]},
"semantic_owner": "Governance evidence projection",
"canonical_state": ["none"],
"state_transitions": ["none"],
"side_effects_and_privileges": ["read-only bounded qualification and browser observation; no activation, promotion or mutation"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["timeout remains UNRATED and CPU/LIFEBOAT fallback remains active", "missing mobile viewport remains an explicit acceptance gap"],
"assumptions": ["GPU credential remains resolved inside cognitive-worker", "browser session is already authenticated and no secrets are inspected"],
"acceptance_scenarios": ["four exact GPU model IDs recorded", "all timeout results remain UNRATED", "GPU does not become preferred", "desktop viewport and mobile limitation recorded", "no secret value appears"],
"required_tests": ["go test ./cmd/creator-web", "go vet ./cmd/creator-web", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "independent evidence review", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_SPEC_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000268",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "3ca7ffa",
"risk_tier": "E",
"intent": "Make the GPU cognitive provider invocation deadline configurable and bounded so large local models are not rejected by an accidental ten-second cold-start limit.",
"requirement_ids": ["model fleet/failover", "quality separated from reachability", "hardware-neutral routing"],
"allowed_change_scope": {"modules": ["cognitive-worker", "deployment-governance"], "paths": ["cmd/cognitive-worker/**", "deploy/docker-compose.prod.yml", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000268-configurable-gpu-qualification-timeout.json", "docs/evidence/**", "docs/PROJECT_STATUS.md"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 1, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing OpenAI-compatible local provider adapter", "existing qualification route"], "may_change": ["deployment timeout configuration only"]},
"semantic_owner": "cognitive-worker invokes provider; capability remains qualification and promotion owner",
"canonical_state": ["existing qualification observations"],
"state_transitions": ["provider timeout window changes; no qualification or activation transition is inferred"],
"side_effects_and_privileges": ["bounded outbound GPU provider request through existing credential boundary"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["timeout remains an unrated/unavailable observation; CPU/LIFEBOAT fallback remains authoritative"],
"assumptions": ["60 seconds is a bounded cold-start window and qualification models still run in parallel", "no model is promoted automatically"],
"acceptance_scenarios": ["GPU adapter uses configured bounded timeout", "default is 60 seconds", "qualification remains unrated on timeout", "routing does not infer intelligence from GPU"],
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl/independent", "docker compose config", "git diff --check", "production health smoke", "one Domovoy-owned GPU qualification probe"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000270",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "29dad40",
"risk_tier": "E",
"intent": "Record the approved local-first operating scope: one local conversational model, Qwen3-8B or Gemma-4 when available locally, is sufficient for the current phase, with the local Lifeboat as recovery fallback; external model qualification is deferred until explicitly requested, while external capability remains optional and non-blocking.",
"requirement_ids": ["local-first model operation", "hardware-neutral routing", "explicit scope decisions"],
"allowed_change_scope": {"modules": ["cognitive-worker", "deployment-governance"], "paths": ["docs/MODEL_FLEET_POLICY.md", "docs/PROJECT_STATUS.md", "docs/evidence/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000270-local-first-model-scope.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "LLM-local/**", "cmd/**", "deploy/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing local CPU provider route", "existing local Lifeboat route"], "may_change": ["documentation and evidence projection only"]},
"semantic_owner": "cognitive-worker model routing policy remains hardware-neutral; this task records the current operating scope",
"canonical_state": ["existing local provider and Lifeboat runtime state", "this explicit scope decision"],
"state_transitions": ["external model qualification is deferred, not promoted or removed"],
"side_effects_and_privileges": [],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["local CPU route remains primary; local Lifeboat remains recovery fallback", "durable reflexes, skills, and local executors continue work when external models are unavailable"],
"assumptions": ["external providers may be added later by a separate explicit task", "GPU presence does not imply quality or priority"],
"acceptance_scenarios": ["policy names one local Qwen3-8B or Gemma-4 conversational model as current primary candidate", "policy names local Lifeboat as fallback", "external qualification is clearly deferred", "external unavailability does not block local/reflex/skill execution", "no external model probe is required for this task"],
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl/independent", "git diff --check", "local runtime health smoke"],
"terminal_gap_states": ["BLOCKED_BY_CONFLICT"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000273",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "d7151f1",
"risk_tier": "E",
"intent": "Reconcile the audit status with the current merged production artifact and fresh authenticated desktop/mobile viewport evidence without changing runtime behavior.",
"requirement_ids": ["current-state evidence", "truthful UI acceptance", "production artifact provenance"],
"allowed_change_scope": {"modules": ["deployment-governance", "creator-web-acceptance"], "paths": ["docs/evidence/**", "docs/PROJECT_STATUS.md", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000273-live-audit-status-reconciliation.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "cmd/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing production health and image provenance", "authenticated browser DOM and viewport observations"], "may_change": ["evidence and status projections only"]},
"semantic_owner": "audit evidence projection",
"canonical_state": ["merged main revision", "live production observations"],
"state_transitions": ["evidence projection advances to the latest verified production revision"],
"side_effects_and_privileges": [],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["failed or unavailable observations remain explicit and do not become passes"],
"assumptions": ["documentation-only changes do not require a Docker rebuild", "mobile viewport acceptance requires an actual 390x844 browser viewport"],
"acceptance_scenarios": ["main and production artifact revision are recorded", "authenticated desktop evidence is recorded", "mobile acceptance remains unclaimed when actual viewport is not 390x844", "external model probing is not performed"],
"required_tests": ["go test ./...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl/independent", "git diff --check", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000274",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "5205845",
"risk_tier": "E",
"intent": "Make the default chat path refuse unverified factual answers instead of exposing model speculation; preserve explicitly classified internal status paths and leave a general research capability for the next routing stage.",
"requirement_ids": ["truth-first default", "no fabricated factual answers", "semantic routing without entity word lists"],
"allowed_change_scope": {"modules": ["creator-web", "cognitive-worker", "interaction"], "paths": ["cmd/creator-web/server.go", "cmd/creator-web/chat_test.go", "cmd/cognitive-worker/interpretation.go", "cmd/cognitive-worker/interpretation_test.go", "modules/interaction/domain/chat.go", "modules/interaction/domain/chat_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000274-truth-first-question-gate.json", "docs/PROJECT_STATUS.md", "docs/evidence/**"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing semantic speech act and information request", "existing local cognitive gateway"], "may_change": ["truthful chat presentation gate", "interpretation prompt guidance"]},
"semantic_owner": "interaction semantics and creator-web presentation boundary",
"canonical_state": ["existing semantic interpretation", "existing verified internal status paths"],
"state_transitions": ["unverified question -> truthful verification-required response", "explicitly classified internal status -> existing status response"],
"side_effects_and_privileges": ["none; no provider call is made for an unverified factual question"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["missing research capability never becomes a fabricated answer"],
"assumptions": ["a later task will connect WEB_RESEARCH to a read-only source connector", "explicit user requests to discuss hypotheses/options remain a separate cognitive path"],
"acceptance_scenarios": ["weather-like factual question does not expose a model-generated answer without evidence", "response explains that verification is unavailable and names safe source category", "internal classified status path remains functional", "an incidental word such as model does not open an inventory or verification route", "routing uses semantic intent rather than city/entity keyword lists"],
"required_tests": ["go test ./cmd/creator-web/... ./cmd/cognitive-worker/...", "go vet ./cmd/creator-web/... ./cmd/cognitive-worker/...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl/independent", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000275",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "1eb3e39",
"risk_tier": "E",
"intent": "Route semantically classified WEB_RESEARCH questions through the bounded research runtime and present only immutable source excerpts; absence or conflict of evidence remains a truthful non-answer.",
"requirement_ids": ["semantic request classification", "truth-first default", "source-backed response", "optional external channel"],
"allowed_change_scope": {"modules": ["core-runtime", "creator-web", "cognitive-worker", "research-runtime", "epistemics", "interaction", "deployment"], "paths": ["contracts/epistemics/research.go", "contracts/interaction/types.go", "adapters/research/search_connector.go", "modules/epistemics/domain/research.go", "cmd/research-runtime/main.go", "cmd/core-runtime/main.go", "cmd/core-runtime/research_client.go", "cmd/core-runtime/cognitive_interpretation.go", "cmd/core-runtime/cognitive_interpretation_test.go", "cmd/creator-web/server.go", "deploy/docker-compose.prod.yml", "tests/research_runtime/search_connector_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000275-semantic-web-research-routing.json", "docs/PROJECT_STATUS.md", "docs/evidence/**"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "LLM-local/**", "modules/interaction/domain/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 1, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing semantic interpretation", "existing epistemics evidence chain", "existing research runtime persistence"], "may_change": ["typed web-search request and source-backed finding presentation"]},
"semantic_owner": "interaction/cognitive interpretation selects meaning; epistemics owns evidence; Core presents the result",
"canonical_state": ["epistemics truth bundle and citation state remain canonical", "no generated advisory is canonical evidence"],
"state_transitions": ["semantic WEB_RESEARCH -> bounded search request -> persisted source snapshot -> source-backed chat response", "search unavailable/conflicted -> verification-required response"],
"side_effects_and_privileges": ["read-only HTTPS search fetches to configured allowlist", "no provider/model selection from user input", "no secret forwarded to search"],
"persistence_and_migrations": ["existing epistemics persistence only; no migration"],
"failure_retry_restart": ["empty/unavailable search never becomes an answer", "runtime restart preserves persisted evidence"],
"assumptions": ["search endpoint template and host allowlist are operator configuration", "local model remains optional; deterministic truth gate is the fallback"],
"acceptance_scenarios": ["simple factual question reaches semantic interpretation", "generic question is routed by WEB_RESEARCH without city/entity keyword lists", "bounded source excerpt and locator are returned", "conflicted/empty evidence is not presented as fact", "search channel can be absent without breaking chat core"],
"required_tests": ["go test ./adapters/research ./modules/epistemics/... ./cmd/research-runtime/... ./cmd/core-runtime/... ./cmd/creator-web/... ./tests/research_runtime/...", "go vet ./...", "go run ./cmd/devctl preflight -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-diff -active dev/ACTIVE_TASK.json", "go run ./cmd/devctl check-architecture", "go run ./cmd/devctl/independent", "git diff --check", "CI", "production health and research-runtime readiness smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000276",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "c09be4c",
"risk_tier": "E",
"intent": "Replace the blocked default search provider with an available configured HTML search endpoint while preserving bounded parsing and truth-first failure behavior.",
"requirement_ids": ["research provider fallback", "no fabricated evidence", "optional external channel"],
"allowed_change_scope": {"modules": ["research-runtime", "deployment"], "paths": ["adapters/research/search_connector.go", "deploy/docker-compose.prod.yml", "tests/research_runtime/search_connector_test.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000276-research-search-provider-fallback.json", "docs/PROJECT_STATUS.md", "docs/evidence/**"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 1, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing WebSearchRequest and evidence chain"], "may_change": ["bounded provider response parsing"]},
"semantic_owner": "research runtime owns source collection and evidence preservation",
"canonical_state": ["existing epistemics source snapshot and citation chain"],
"state_transitions": ["configured provider response -> bounded excerpts", "provider challenge/no result -> unknown evidence"],
"side_effects_and_privileges": ["read-only HTTPS fetch to operator allowlist"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["provider failure never becomes a supported finding"],
"assumptions": ["operator may override search URL and host allowlist", "search results are source excerpts, not generated answers"],
"acceptance_scenarios": ["Bing-style bounded result is collected", "unallowlisted endpoint is rejected", "provider challenge yields no evidence", "no entity-specific query logic exists"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI", "production research-runtime smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000277",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "5058d5a",
"risk_tier": "E",
"intent": "Align the bounded configured search parser with the observed provider HTML while retaining explicit no-evidence behavior.",
"requirement_ids": ["research live smoke", "source-backed response"],
"allowed_change_scope": {"modules": ["research-runtime"], "paths": ["adapters/research/search_connector.go", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000277-research-parser-live-fix.json", "docs/evidence/**"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "deploy/**", "contracts/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing search connector and evidence chain"], "may_change": ["provider HTML parser" ]},
"semantic_owner": "research runtime",
"canonical_state": ["existing persisted source evidence"],
"state_transitions": ["observed provider HTML -> exact bounded excerpt"],
"side_effects_and_privileges": ["none beyond existing allowlisted read-only fetch"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["parser mismatch remains no-evidence, never fabricated evidence"],
"assumptions": ["provider response remains bounded and allowlisted"],
"acceptance_scenarios": ["current configured provider heading markup parses", "empty or challenge response remains UNKNOWN"],
"required_tests": ["go test ./tests/research_runtime ./cmd/research-runtime/...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI", "production research smoke"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000278",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "f1c3abb",
"risk_tier": "E",
"intent": "Record current production evidence for source-backed research routing without changing runtime behavior.",
"requirement_ids": ["truth/research live evidence", "production provenance"],
"allowed_change_scope": {"modules": [], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000278-live-research-evidence-reconciliation.json", "docs/PROJECT_STATUS.md", "docs/evidence/semantic-web-research-routing-2026-08-24-v2.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "modules/**", "adapters/**", "cmd/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["existing production health and research smoke evidence"], "may_change": ["documentation evidence only"]},
"semantic_owner": "evidence reconciliation",
"canonical_state": ["no runtime state changed"],
"state_transitions": ["observed production smoke -> evidence record"],
"side_effects_and_privileges": ["none"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["none"],
"assumptions": ["production smoke was read-only and used no secrets"],
"acceptance_scenarios": ["evidence names exact production revision", "source claims remain explicitly UNVERIFIED", "authenticated UI limitation remains explicit"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000279",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "ffd7ddc",
"risk_tier": "E",
"intent": "Reconcile the audit matrix with current production fleet, bridge and research observations; documentation only.",
"requirement_ids": ["audit current-state evidence", "no stale production claims"],
"allowed_change_scope": {"modules": [], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000279-live-audit-matrix-reconciliation.json", "docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-24-v11.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "modules/**", "adapters/**", "cmd/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["current production health, fleet, bridge and research observations"], "may_change": ["audit evidence only"]},
"semantic_owner": "evidence reconciliation",
"canonical_state": ["no runtime state changed"],
"state_transitions": ["live observations -> current audit evidence"],
"side_effects_and_privileges": ["none"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["none"],
"assumptions": ["all observations were read-only"],
"acceptance_scenarios": ["matrix names exact production revision", "fleet selected tier matches live response", "remaining gaps remain explicit"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,26 @@
{
"task_id": "DEV-000280",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "51db637",
"risk_tier": "E",
"intent": "Add a typed local generic cognitive specialist execution path for Work Graph nodes, preserving independent verification and durable recovery semantics.",
"requirement_ids": ["generic local agent execution", "candidate output is not verified fact", "work graph callback remains independently verifiable"],
"allowed_change_scope": {
"modules": ["work"],
"paths": ["contracts/work/bridge.go", "contracts/work/lease.go", "adapters/postgres/work_repository.go", "cmd/agent-execution-bridge/**", "cmd/agent-worker/**", "tests/agent_execution/**", "dev/ACTIVE_TASK.json", "dev/tasks/DEV-000280-local-generic-agent-execution.json"]
},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "modules/identity/**", "modules/authority/**", "LLM-local/**", "deploy/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["contracts/work@v1", "cognitive gateway advisory endpoint", "work graph result callback"], "may_change": ["contracts/work@v1 bridge extension"]},
"semantic_owner": "work",
"canonical_state": ["work dispatch intent and graph candidate only; no canonical fact or external mutation"],
"state_transitions": ["queued -> leased -> executing -> candidate recorded -> verifying", "unsupported or unavailable -> WAITING with durable retry"],
"side_effects_and_privileges": ["local cognitive gateway call", "work graph candidate callback", "no external mutation"],
"persistence_and_migrations": ["reuse existing work.agent_dispatch_intents and graph candidate persistence; no migration"],
"failure_retry_restart": ["bridge timeout or gateway failure returns error for durable retry", "unsupported context returns WAITING", "lease heartbeat and stale claim recovery remain unchanged"],
"assumptions": ["Cognitive Gateway is the only model boundary", "generic output is an unverified candidate until independent synthesis", "local CPU/Lifeboat fallback remains the default capability"],
"acceptance_scenarios": ["typed generic request reaches gateway and callback", "callback candidate leaves graph eligible for verification rather than success", "missing generic context returns WAITING", "gateway failure is retryable and never reported completed"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY", "BLOCKED_BY_ARCHITECTURE_GAP"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000281",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "080bd44",
"risk_tier": "E",
"intent": "Reconcile production evidence after the generic local Agent Execution rollout.",
"requirement_ids": ["no stale production claims", "generic bridge evidence is explicit", "remaining acceptance gaps remain visible"],
"allowed_change_scope": {"modules": [], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000281-live-generic-agent-execution-evidence.json", "docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-24-v12.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "modules/**", "adapters/**", "cmd/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["production image labels", "agent bridge health", "agent worker readiness", "assistant health"], "may_change": ["audit evidence only"]},
"semantic_owner": "evidence reconciliation",
"canonical_state": ["no runtime state changed"],
"state_transitions": ["live observations -> versioned evidence"],
"side_effects_and_privileges": ["read-only production inspection"],
"persistence_and_migrations": ["documentation only"],
"failure_retry_restart": ["none"],
"assumptions": ["the deployment label is the authoritative runtime revision evidence", "generic candidate output still requires independent graph verification"],
"acceptance_scenarios": ["matrix records 080bd44 production revision", "bridge and worker advertise generic-cognitive-work", "mobile and authenticated chat gaps remain explicit"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000282",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "e132ec6",
"risk_tier": "E",
"intent": "Record the final delayed Portainer auto-deployment and live production revision after DEV-000281.",
"requirement_ids": ["production revision is current", "auto-deploy is documented", "live health remains green"],
"allowed_change_scope": {"modules": [], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000282-final-auto-deploy-evidence.json", "docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-24-v13.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "modules/**", "adapters/**", "cmd/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["Portainer delayed auto-deploy observation", "production image labels", "assistant health", "bridge and worker readiness"], "may_change": ["audit evidence only"]},
"semantic_owner": "evidence reconciliation",
"canonical_state": ["no runtime state changed"],
"state_transitions": ["merged main -> Portainer rollout -> live evidence"],
"side_effects_and_privileges": ["read-only production inspection"],
"persistence_and_migrations": ["documentation only"],
"failure_retry_restart": ["rollout may be delayed; verify revision after the delay"],
"assumptions": ["Portainer Git deployment is the production control plane", "no secret values are needed for normal auto-deploy"],
"acceptance_scenarios": ["production labels and public HTML expose e132ec6", "health remains ok", "generic bridge capability remains advertised"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000283",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "94baf98",
"risk_tier": "E",
"intent": "Add an independent deterministic envelope verifier before a generic local Work Graph candidate is recorded.",
"requirement_ids": ["independent candidate envelope verification", "model output is not truth", "graph success still requires synthesis"],
"allowed_change_scope": {"modules": ["work"], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000283-generic-candidate-verifier.json", "contracts/work/graph.go", "cmd/task-verifier/**", "cmd/agent-execution-bridge/**"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "modules/identity/**", "modules/authority/**", "LLM-local/**", "deploy/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["contracts/work@v1", "task-verifier internal endpoint", "work graph result callback"], "may_change": ["contracts/work graph candidate verification payload"]},
"semantic_owner": "work",
"canonical_state": ["verification evidence is an integrity envelope only; candidate remains unverified semantically"],
"state_transitions": ["generic model output -> deterministic envelope verification -> candidate recording -> VERIFYING"],
"side_effects_and_privileges": ["local verifier call and work graph callback; no external mutation"],
"persistence_and_migrations": ["reuse existing candidate evidence refs; no migration"],
"failure_retry_restart": ["verifier unavailable returns retryable failure", "invalid envelope returns WAITING", "graph success remains gated by independent synthesis"],
"assumptions": ["deterministic verification proves attribution/integrity only, not factual truth", "task-verifier is an independently running local service"],
"acceptance_scenarios": ["valid generic candidate obtains verifier evidence", "invalid candidate is not recorded as completed", "model output remains unverified", "existing task evaluation path remains unchanged"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000284",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "bcbd41f",
"risk_tier": "E",
"intent": "Record a bounded production canary proving that generic Work Graph candidates remain unverified until an independent verifier and synthesis path completes the graph.",
"requirement_ids": ["generic candidate independent verification", "candidate dissent lineage", "graph completion only after verified synthesis"],
"allowed_change_scope": {"modules": [], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000284-live-generic-synthesis-evidence.json", "docs/PROJECT_STATUS.md", "docs/evidence/audit-completion-matrix-2026-08-24-v14.json", "docs/evidence/generic-synthesis-live-2026-08-24-v1.json"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "contracts/**", "modules/**", "adapters/**", "cmd/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["task-verifier generic envelope response", "Work Graph synthesis response", "production runtime labels"], "may_change": ["audit evidence only"]},
"semantic_owner": "evidence reconciliation",
"canonical_state": ["synthetic canary graph and candidates are disposable evidence records; no user data or external mutation"],
"state_transitions": ["candidate recorded -> independently envelope-verified -> synthesis verified -> graph completed"],
"side_effects_and_privileges": ["bounded synthetic production Work Graph records only"],
"persistence_and_migrations": ["documentation only; no migration"],
"failure_retry_restart": ["canary is idempotent by unique synthetic graph id; failure remains evidence gap"],
"assumptions": ["semantic envelope verification is not semantic truth verification", "external model qualification remains deferred"],
"acceptance_scenarios": ["two candidates retain dissent lineage", "synthesis response is independently attributed", "graph reaches COMPLETED only through synthesis endpoint", "semantic content remains explicitly unverified"],
"required_tests": ["go test ./...", "go vet ./...", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI", "production health smoke", "live generic synthesis canary"],
"terminal_gap_states": ["BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000285",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "5c98bc3",
"risk_tier": "E",
"intent": "Route semantically source-dependent current-fact questions to the existing provider-neutral WEB_RESEARCH capability instead of returning an unverified fallback.",
"requirement_ids": ["truth-first source routing", "no entity-specific keyword branches", "local deterministic fallback remains honest"],
"allowed_change_scope": {"modules": ["interaction"], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000285-semantic-research-routing-fix.json", "modules/interaction/domain/chat.go", "modules/interaction/domain/chat_test.go"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "cmd/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["interaction semantic candidate contract", "capability WEB_RESEARCH hint"], "may_change": []},
"semantic_owner": "interaction",
"canonical_state": ["no new canonical state; only semantic interpretation hints change"],
"state_transitions": ["source-dependent question -> WEB_RESEARCH hint -> existing research runtime"],
"side_effects_and_privileges": ["existing read-only research connector may be selected by downstream runtime"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["if research is unavailable, existing truthful unverified response remains"],
"assumptions": ["lexical cues represent semantic classes such as currentness and observed external state, not named entities"],
"acceptance_scenarios": ["weather-like current fact routes to WEB_RESEARCH without city-specific code", "ordinary model mention does not route to research", "non-research factual question remains honest when no source-dependent cue exists"],
"required_tests": ["go test ./modules/interaction/domain", "go vet ./modules/interaction/domain", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI", "production health smoke"],
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_MISSING_CAPABILITY"]
}
@@ -0,0 +1,23 @@
{
"task_id": "DEV-000286",
"task_revision": 1,
"architecture_revision": "ADR-0018",
"base_repo_revision": "b0a8b3e",
"risk_tier": "E",
"intent": "Reject web-search excerpts with no substantive lexical relation to the requested query so truth-first chat never presents irrelevant search results as useful evidence.",
"requirement_ids": ["research relevance boundary", "truth-first no irrelevant evidence", "no entity-specific filtering"],
"allowed_change_scope": {"modules": ["research adapter"], "paths": ["dev/ACTIVE_TASK.json", "dev/tasks/DEV-000286-research-result-relevance-filter.json", "adapters/research/search_connector.go", "tests/research_runtime/search_connector_test.go"]},
"forbidden_change_scope": {"paths": ["architecture/**", "security/**", "database/**", "cmd/**", "deploy/**", "LLM-local/**"]},
"allowed_architecture_delta": {"new_cross_module_dependencies": 0, "new_canonical_owners": 0, "new_privileges": 0, "new_third_party_dependencies": 0, "new_database_owner_roles": 0},
"contracts": {"consume": ["epistemics ResearchCandidate", "provider-neutral WebSearchRequest"], "may_change": []},
"semantic_owner": "epistemics research adapter boundary",
"canonical_state": ["no new canonical state; irrelevant candidates are not emitted"],
"state_transitions": ["raw search result -> relevance gate -> candidate or omission"],
"side_effects_and_privileges": ["read-only search connector only"],
"persistence_and_migrations": ["none"],
"failure_retry_restart": ["zero relevant candidates becomes the existing truthful no-evidence response"],
"assumptions": ["short query terms without substantive tokens retain prior parser behavior for compatibility", "term overlap is a bounded relevance guard, not semantic truth verification"],
"acceptance_scenarios": ["irrelevant dictionary results are omitted for a weather-like query", "relevant result is retained", "no entity-specific branch exists"],
"required_tests": ["go test ./tests/research_runtime", "go vet ./adapters/research ./tests/research_runtime", "preflight", "check-diff", "check-architecture", "independent", "git diff --check", "CI", "production health smoke", "one live chat smoke"],
"terminal_gap_states": ["BLOCKED_BY_SPEC_GAP", "BLOCKED_BY_ARCHITECTURE_GAP", "BLOCKED_BY_MISSING_CAPABILITY"]
}

Some files were not shown because too many files have changed in this diff Show More