From 4fc72332f58e86a1855ec970f5cf87372b543270 Mon Sep 17 00:00:00 2001 From: thanhnv Date: Sat, 11 Jul 2026 12:18:59 +0900 Subject: [PATCH] optz: Goal orchestrator --- .specify/logs/audit/security.jsonl | 54 ++ .specify/logs/cost/metrics.jsonl | 6 + .specify/logs/level5/provider-usage.jsonl | 31 + README.md | 818 +++++++++--------- docs/diagrams/goal-model-interaction.mmd | 13 + docs/diagrams/goal-model-interaction.svg | 1 + docs/output/casan/agentops-dashboard.html | 30 +- .../casan/central-agentops-dashboard.html | 30 +- .../goals/ModelInteractionDiagram.tsx | 54 ++ .../src/components/ui/MarkdownText.tsx | 64 ++ .../frontend/src/pages/Chat.tsx | 63 +- .../frontend/src/pages/Goals.tsx | 12 +- .../scripts/bash/goal-orchestrator.py | 13 +- .../casan-harness/scripts/bash/local-full.sh | 20 +- .../casan-harness/scripts/bash/model-call.py | 5 + .../scripts/bash/model-connections.py | 2 +- .../scripts/bash/model-digest-check.sh | 6 +- .../auth-bridge/bridge.pid | 1 + .../auth-bridge/model-audit.jsonl | 4 + tmp/control-panel-local/auth-bridge/token | 1 + tmp/goal-approval-execution-flow.mmd | 20 + tmp/goal-approval-execution-flow.png | Bin 0 -> 64087 bytes tmp/goal-approval-execution-flow.svg | 1 + tmp/phase2-blocked/logs/chat/chat-head.txt | 1 + tmp/phase2-blocked/logs/chat/chat-turns.jsonl | 1 + tmp/phase2-blocked/logs/cost/metrics.jsonl | 1 + ...38db1352-5ffc-4f37-b785-fd1c61e5a740.jsonl | 7 + tmp/phase2-smoke/logs/audit/security.jsonl | 2 + tmp/phase2-smoke/logs/chat/chat-head.txt | 1 + tmp/phase2-smoke/logs/chat/chat-turns.jsonl | 1 + tmp/phase2-smoke/logs/cost/metrics.jsonl | 1 + ...d73c565f-19ec-4ada-bf69-5fad89cfa282.jsonl | 14 + ...-27486dfc-3f1a-40da-a239-9d3f5ef75496.json | 12 + ...-dac69766-9c81-49bc-8a11-4659dcf7dfdd.json | 12 + 34 files changed, 850 insertions(+), 452 deletions(-) create mode 100644 docs/diagrams/goal-model-interaction.mmd create mode 100644 docs/diagrams/goal-model-interaction.svg create mode 100644 packages/casan-control-panel/frontend/src/components/goals/ModelInteractionDiagram.tsx create mode 100644 packages/casan-control-panel/frontend/src/components/ui/MarkdownText.tsx create mode 100644 tmp/control-panel-local/auth-bridge/bridge.pid create mode 100644 tmp/control-panel-local/auth-bridge/model-audit.jsonl create mode 100644 tmp/control-panel-local/auth-bridge/token create mode 100644 tmp/goal-approval-execution-flow.mmd create mode 100644 tmp/goal-approval-execution-flow.png create mode 100644 tmp/goal-approval-execution-flow.svg create mode 100644 tmp/phase2-blocked/logs/chat/chat-head.txt create mode 100644 tmp/phase2-blocked/logs/chat/chat-turns.jsonl create mode 100644 tmp/phase2-blocked/logs/cost/metrics.jsonl create mode 100644 tmp/phase2-blocked/logs/trace-events/38db1352-5ffc-4f37-b785-fd1c61e5a740.jsonl create mode 100644 tmp/phase2-smoke/logs/audit/security.jsonl create mode 100644 tmp/phase2-smoke/logs/chat/chat-head.txt create mode 100644 tmp/phase2-smoke/logs/chat/chat-turns.jsonl create mode 100644 tmp/phase2-smoke/logs/cost/metrics.jsonl create mode 100644 tmp/phase2-smoke/logs/trace-events/d73c565f-19ec-4ada-bf69-5fad89cfa282.jsonl create mode 100644 tmp/phase2-smoke/logs/trace/security-27486dfc-3f1a-40da-a239-9d3f5ef75496.json create mode 100644 tmp/phase2-smoke/logs/trace/security-dac69766-9c81-49bc-8a11-4659dcf7dfdd.json diff --git a/.specify/logs/audit/security.jsonl b/.specify/logs/audit/security.jsonl index 8234e22..17a8c08 100644 --- a/.specify/logs/audit/security.jsonl +++ b/.specify/logs/audit/security.jsonl @@ -92,3 +92,57 @@ {"timestamp":"2026-07-10T06:20:20Z","trace_id":"trace-1783664420-1347","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"d1c3c3a7ee70994d948443ba05bb0a9c2dd1f1852a994ab80d722730d282ae44","output_hash":"d1c3c3a7ee70994d948443ba05bb0a9c2dd1f1852a994ab80d722730d282ae44"} {"timestamp":"2026-07-10T07:20:30Z","trace_id":"trace-1783668030-52","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa","output_hash":"8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa"} {"timestamp":"2026-07-10T07:21:21Z","trace_id":"trace-1783668081-716","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"280cadd0be725e6496a8614b12a98836bedb5c8a38fc6b2a8afe5f44b237158e","output_hash":"280cadd0be725e6496a8614b12a98836bedb5c8a38fc6b2a8afe5f44b237158e"} +{"timestamp":"2026-07-10T08:10:53Z","trace_id":"trace-1783671053-3456","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"a5029c2ba88119946f6ad1c38d796eea7f3519b4ef847a98ef827633e796c717","output_hash":"a5029c2ba88119946f6ad1c38d796eea7f3519b4ef847a98ef827633e796c717"} +{"timestamp":"2026-07-10T08:37:34Z","trace_id":"trace-1783672654-290","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5","output_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5"} +{"timestamp":"2026-07-10T08:38:21Z","trace_id":"trace-1783672701-1074","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5","output_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5"} +{"timestamp":"2026-07-10T08:38:29Z","trace_id":"trace-1783672709-1683","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5","output_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5"} +{"timestamp":"2026-07-10T08:39:00Z","trace_id":"trace-1783672740-2286","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5","output_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5"} +{"timestamp":"2026-07-10T08:39:07Z","trace_id":"trace-1783672747-2894","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5","output_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5"} +{"timestamp":"2026-07-10T08:39:13Z","trace_id":"trace-1783672753-3495","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5","output_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5"} +{"timestamp":"2026-07-10T08:39:54Z","trace_id":"trace-1783672794-129","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5","output_hash":"305d51e7bd89770c78e56848036699dbdf4040c1d17a325c8b534703c5fa45c5"} +{"timestamp":"2026-07-10T08:40:24Z","trace_id":"trace-1783672824-709","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"98be3b34b18aae993bb1a5c6f2151a21b9e103a8f56c19e650bf03514231cb26","output_hash":"98be3b34b18aae993bb1a5c6f2151a21b9e103a8f56c19e650bf03514231cb26"} +{"timestamp":"2026-07-10T08:45:46Z","trace_id":"trace-1783673146-2396","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"2fcbb74936de0bc90851761d4f0c53506b696fc83f50c1f5b91119236af6ed83","output_hash":"2fcbb74936de0bc90851761d4f0c53506b696fc83f50c1f5b91119236af6ed83"} +{"timestamp":"2026-07-10T08:46:14Z","trace_id":"trace-1783673174-3110","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"a5bbfed3fa6a86702ff80efcdac35d14cfb2a3cfb9692c03584baf2a5fe9b76c","output_hash":"a5bbfed3fa6a86702ff80efcdac35d14cfb2a3cfb9692c03584baf2a5fe9b76c"} +{"timestamp":"2026-07-10T08:48:31Z","trace_id":"trace-1783673311-3706","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"ade0e2d2828ff5a8f87d08d62f320f69e057fdbcbba54935a5346601c6ef93fc","output_hash":"ade0e2d2828ff5a8f87d08d62f320f69e057fdbcbba54935a5346601c6ef93fc"} +{"timestamp":"2026-07-10T08:48:51Z","trace_id":"trace-1783673331-4392","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"2c52b7763ce9f000c563843ffc3301f85f1d95f6b5f8e818c02acb7dd2aa842f","output_hash":"2c52b7763ce9f000c563843ffc3301f85f1d95f6b5f8e818c02acb7dd2aa842f"} +{"timestamp":"2026-07-10T08:49:37Z","trace_id":"trace-1783673377-4840","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"18f3c234faddf6ad5421a2b9cae1036a7a958bc4ecc534f8e8f8395670c2530c","output_hash":"18f3c234faddf6ad5421a2b9cae1036a7a958bc4ecc534f8e8f8395670c2530c"} +{"timestamp":"2026-07-10T08:49:57Z","trace_id":"trace-1783673397-5474","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"757ffca4ffccc7668753e79687ee40738d795428588fee4c6b8180c0bc734124","output_hash":"757ffca4ffccc7668753e79687ee40738d795428588fee4c6b8180c0bc734124"} +{"timestamp":"2026-07-10T08:51:04Z","trace_id":"trace-1783673464-5971","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"1ce53bc84ad799d7ca3890e6a79e8a695ea48b8fce13f78c38eb657e2fe80ed7","output_hash":"1ce53bc84ad799d7ca3890e6a79e8a695ea48b8fce13f78c38eb657e2fe80ed7"} +{"timestamp":"2026-07-10T08:51:25Z","trace_id":"trace-1783673485-6637","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"14b9e1c2352607b7a4d0dfa80e22d6d4ec1409db91511cc610bd3c7f975d3321","output_hash":"14b9e1c2352607b7a4d0dfa80e22d6d4ec1409db91511cc610bd3c7f975d3321"} +{"timestamp":"2026-07-10T14:24:32Z","trace_id":"trace-1783693472-175","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"6a4afba59fa567c089a84e6e933c8665380c63c93f00de70e1cba2a26512f623","output_hash":"6a4afba59fa567c089a84e6e933c8665380c63c93f00de70e1cba2a26512f623"} +{"timestamp":"2026-07-10T14:25:28Z","trace_id":"trace-1783693528-893","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"317d789d9c23693bb1dda981d9f75e1eff9ca8fa1aa9a722115c6fef78caad23","output_hash":"317d789d9c23693bb1dda981d9f75e1eff9ca8fa1aa9a722115c6fef78caad23"} +{"timestamp":"2026-07-10T14:26:59Z","trace_id":"trace-1783693619-122","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"491375254fe83e91fad084ef998ddda780654f1ef921a2cd9148466a74fb1c9a","output_hash":"491375254fe83e91fad084ef998ddda780654f1ef921a2cd9148466a74fb1c9a"} +{"timestamp":"2026-07-10T14:27:46Z","trace_id":"trace-1783693666-838","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"642e21a39cd734fd6667a540da58d494fe3e3a2d4a2297b49334dc5536834c77","output_hash":"642e21a39cd734fd6667a540da58d494fe3e3a2d4a2297b49334dc5536834c77"} +{"timestamp":"2026-07-10T14:33:04Z","trace_id":"trace-1783693984-175","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"5e9f6afb8b00b1c071e64b07ffa8ee66d67f298d65a0b6285a2f3ad92010aa56","output_hash":"5e9f6afb8b00b1c071e64b07ffa8ee66d67f298d65a0b6285a2f3ad92010aa56"} +{"timestamp":"2026-07-10T14:33:34Z","trace_id":"trace-1783694014-789","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"1e1864543ed2100727fadf80b16bf47ed37d681f3a603bdd9342bfcaa86a5bb7","output_hash":"1e1864543ed2100727fadf80b16bf47ed37d681f3a603bdd9342bfcaa86a5bb7"} +{"timestamp":"2026-07-10T14:33:48Z","trace_id":"trace-1783694028-933","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"e08f4d05c9a0f21dbdc0edc3d759d9324cbd6ca23d3ab958edac1305ecbfeb17","output_hash":"e08f4d05c9a0f21dbdc0edc3d759d9324cbd6ca23d3ab958edac1305ecbfeb17"} +{"timestamp":"2026-07-10T14:46:33Z","trace_id":"trace-1783694793-134","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"f0becd0ba8ba760f9e11045bc17c25c826cef2fd9b5848568a6e704da48562d3","output_hash":"f0becd0ba8ba760f9e11045bc17c25c826cef2fd9b5848568a6e704da48562d3"} +{"timestamp":"2026-07-10T14:46:33Z","trace_id":"trace-1783694793-600","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"6cbc480a81d9e566f94340e479b0da7780e51f0ef93db82a9e6d808d93509c2c","output_hash":"6cbc480a81d9e566f94340e479b0da7780e51f0ef93db82a9e6d808d93509c2c"} +{"timestamp":"2026-07-10T14:46:34Z","trace_id":"trace-1783694794-1066","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"49bd44fb1dd64f1606d448efcc591f6662bb6c06de2541ce3ea6804f3d2b6c5f","output_hash":"49bd44fb1dd64f1606d448efcc591f6662bb6c06de2541ce3ea6804f3d2b6c5f"} +{"timestamp":"2026-07-10T14:46:35Z","trace_id":"trace-1783694795-1532","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"a2ffaa6bc02119947e88c0899e3efb78084e20072b5650e1d463b3e978acb644","output_hash":"a2ffaa6bc02119947e88c0899e3efb78084e20072b5650e1d463b3e978acb644"} +{"timestamp":"2026-07-10T14:46:35Z","trace_id":"trace-1783694795-1997","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"b122eccde5def8f8a3ede374073d555eeb0649060a93650ca0fdffb940760321","output_hash":"b122eccde5def8f8a3ede374073d555eeb0649060a93650ca0fdffb940760321"} +{"timestamp":"2026-07-10T14:59:15Z","trace_id":"trace-1783695555-208","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"b65685191514667998f209dd8ce72e73e83fb0c2be00e8bd3c7e7cfef02fbd7a","output_hash":"b65685191514667998f209dd8ce72e73e83fb0c2be00e8bd3c7e7cfef02fbd7a"} +{"timestamp":"2026-07-10T15:07:10Z","trace_id":"trace-1783696030-1110","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"63073e9ea949155b9fc8db84dca22a3b8a635b11ab60e45b00c5914c842473fa","output_hash":"63073e9ea949155b9fc8db84dca22a3b8a635b11ab60e45b00c5914c842473fa"} +{"timestamp":"2026-07-10T15:11:07Z","trace_id":"trace-1783696267-3617","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa","output_hash":"8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa"} +{"timestamp":"2026-07-10T15:11:50Z","trace_id":"trace-1783696310-4454","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"d5e4481c6b2b996576af60207f92dc3c8d400d56909a23d41a96d491f520589f","output_hash":"d5e4481c6b2b996576af60207f92dc3c8d400d56909a23d41a96d491f520589f"} +{"timestamp":"2026-07-10T15:12:19Z","trace_id":"trace-1783696339-4956","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"d6f4b13ee10ffdf48682e7f3d43f5e4df05dff4b9d2e63b1a8585220634b5027","output_hash":"d6f4b13ee10ffdf48682e7f3d43f5e4df05dff4b9d2e63b1a8585220634b5027"} +{"timestamp":"2026-07-10T15:13:05Z","trace_id":"trace-1783696385-5687","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"9b65df7891907c5a3a9fadc9b72f5541e548f330aeffd31365a50a690a415d97","output_hash":"9b65df7891907c5a3a9fadc9b72f5541e548f330aeffd31365a50a690a415d97"} +{"timestamp":"2026-07-11T02:45:11Z","trace_id":"trace-1783737911-272","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa","output_hash":"8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa"} +{"timestamp":"2026-07-11T02:54:20Z","trace_id":"trace-1783738460-1030","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa","output_hash":"8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa"} +{"timestamp":"2026-07-11T02:54:39Z","trace_id":"trace-1783738479-1761","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"d5e4481c6b2b996576af60207f92dc3c8d400d56909a23d41a96d491f520589f","output_hash":"d5e4481c6b2b996576af60207f92dc3c8d400d56909a23d41a96d491f520589f"} +{"timestamp":"2026-07-11T02:55:18Z","trace_id":"trace-1783738518-2282","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"77100d99316b67f917a2dc398f03532f93c86f9f94fc6d1f0a2ec9a128017b2b","output_hash":"77100d99316b67f917a2dc398f03532f93c86f9f94fc6d1f0a2ec9a128017b2b"} +{"timestamp":"2026-07-11T02:55:45Z","trace_id":"trace-1783738545-3066","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"a22bc04e692ed93ccf0a1564be9f47ecd8e4991ca69780760ce14a26f73178af","output_hash":"a22bc04e692ed93ccf0a1564be9f47ecd8e4991ca69780760ce14a26f73178af"} +{"timestamp":"2026-07-11T02:56:30Z","trace_id":"trace-1783738590-3520","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a","output_hash":"d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a"} +{"timestamp":"2026-07-11T03:03:09Z","trace_id":"trace-1783738989-117","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a","output_hash":"d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a"} +{"timestamp":"2026-07-11T03:04:01Z","trace_id":"trace-1783739041-117","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a","output_hash":"d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a"} +{"timestamp":"2026-07-11T03:04:11Z","trace_id":"trace-1783739051-704","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"c54aefd8623387334779587082579e91f78f55a823adbf5cf812f98a6255dc85","output_hash":"c54aefd8623387334779587082579e91f78f55a823adbf5cf812f98a6255dc85"} +{"timestamp":"2026-07-11T03:04:43Z","trace_id":"trace-1783739083-1081","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"3cb27554ac2eb202e09ec170b6a6fb028d4fd9b15a3448e999db8a88b3a7e5e4","output_hash":"3cb27554ac2eb202e09ec170b6a6fb028d4fd9b15a3448e999db8a88b3a7e5e4"} +{"timestamp":"2026-07-11T03:05:27Z","trace_id":"trace-1783739127-1189","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"d94f7cb51d952fcaffb585e76c16542aba2f774d64c6d270eab32e7888eccc40","output_hash":"d94f7cb51d952fcaffb585e76c16542aba2f774d64c6d270eab32e7888eccc40"} +{"timestamp":"2026-07-11T03:06:28Z","trace_id":"trace-1783739188-1394","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a","output_hash":"d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a"} +{"timestamp":"2026-07-11T03:07:06Z","trace_id":"trace-1783739226-1926","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"dbdf75779e5a2b0732d18da95767b4231985a6205836f12321935404b41a47c3","output_hash":"dbdf75779e5a2b0732d18da95767b4231985a6205836f12321935404b41a47c3"} +{"timestamp":"2026-07-11T03:07:54Z","trace_id":"trace-1783739274-2082","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"fd6094f85e823a0be31d54bc5dbecf5e174275ad8b85ca2aa56f58658789e7a3","output_hash":"fd6094f85e823a0be31d54bc5dbecf5e174275ad8b85ca2aa56f58658789e7a3"} +{"timestamp":"2026-07-11T03:09:42Z","trace_id":"trace-1783739382-2291","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"9e2a2bc7bccc5123fb02c5789591db77dabed477b5ffba09f2db460de423bb05","output_hash":"9e2a2bc7bccc5123fb02c5789591db77dabed477b5ffba09f2db460de423bb05"} +{"timestamp":"2026-07-11T03:13:24Z","trace_id":"trace-1783739604-121","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"63073e9ea949155b9fc8db84dca22a3b8a635b11ab60e45b00c5914c842473fa","output_hash":"63073e9ea949155b9fc8db84dca22a3b8a635b11ab60e45b00c5914c842473fa"} +{"timestamp":"2026-07-11T03:14:47Z","trace_id":"trace-1783739687-743","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"41787b8b4a629028ae972d8db1cf4ac491af928978e0469269655a140a41e20c","output_hash":"41787b8b4a629028ae972d8db1cf4ac491af928978e0469269655a140a41e20c"} +{"timestamp":"2026-07-11T03:15:44Z","trace_id":"trace-1783739744-857","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"8367afe050a5086cac52162ee740dc4f68949e6901538d86f49802ad069e1e54","output_hash":"8367afe050a5086cac52162ee740dc4f68949e6901538d86f49802ad069e1e54"} +{"timestamp":"2026-07-11T03:17:50Z","trace_id":"trace-1783739870-1068","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"744ebdf28580ca15b50dd647f69de9e8ae2288bd6267a2a792f4ca2816f856f9","output_hash":"744ebdf28580ca15b50dd647f69de9e8ae2288bd6267a2a792f4ca2816f856f9"} diff --git a/.specify/logs/cost/metrics.jsonl b/.specify/logs/cost/metrics.jsonl index 5be67a8..27c4da7 100644 --- a/.specify/logs/cost/metrics.jsonl +++ b/.specify/logs/cost/metrics.jsonl @@ -23,3 +23,9 @@ {"timestamp": "2026-07-10T06:19:34Z", "trace_id": "d8796e51-f1bf-4950-b27d-5327ed69ee12", "harness": "H6-agentops", "agent": "chat.ask-casan", "step": "ask-casan-readonly", "status": "success", "exit_code": 0, "latency_ms": 57864, "retry_count": 0, "input_tokens": 427, "output_tokens": 406, "total_tokens": 833, "cost_estimate": 0.0, "cost_source": "ollama_local_real_tokens", "synthesis_mode": "model", "hallucination_signals": 0, "alerts": [], "input_hash": "5da5739dfab4b282139a20d7272d0e676331eb09f86f562cc5e6d1f3b668a5ab", "output_hash": "142404dfd1bd2e31f6bef54bb495685e059a9b5c4b2e3369c8b79f8c7bec67a9"} {"timestamp": "2026-07-10T06:20:20Z", "trace_id": "b5ad414a-4662-49c5-a024-3f2bb51ada64", "harness": "H6-agentops", "agent": "chat.ask-casan", "step": "ask-casan-readonly", "status": "success", "exit_code": 0, "latency_ms": 43174, "retry_count": 0, "input_tokens": 511, "output_tokens": 288, "total_tokens": 799, "cost_estimate": 0.0, "cost_source": "ollama_local_real_tokens", "synthesis_mode": "model", "hallucination_signals": 0, "alerts": [], "input_hash": "5da5739dfab4b282139a20d7272d0e676331eb09f86f562cc5e6d1f3b668a5ab", "output_hash": "d1c3c3a7ee70994d948443ba05bb0a9c2dd1f1852a994ab80d722730d282ae44"} {"timestamp": "2026-07-10T07:21:22Z", "trace_id": "1fd5d347-f21c-4370-a60d-30b11fd8c53e", "harness": "H6-agentops", "agent": "chat.ask-casan", "step": "ask-casan-readonly", "status": "success", "exit_code": 0, "latency_ms": 51212, "retry_count": 0, "input_tokens": 362, "output_tokens": 59, "total_tokens": 421, "cost_estimate": 0.0, "cost_source": "ollama_local_real_tokens", "synthesis_mode": "model", "hallucination_signals": 0, "alerts": [], "input_hash": "8aaecd06d85c7f743d3545e7b865f3053c647591f95919c1c0b003046d4757aa", "output_hash": "280cadd0be725e6496a8614b12a98836bedb5c8a38fc6b2a8afe5f44b237158e"} +{"timestamp": "2026-07-10T14:25:28Z", "trace_id": "1c93ce8a-db76-4573-b49f-07f9fc408056", "harness": "H6-agentops", "agent": "goal.orchestrator", "step": "local-worker-cloud-reviewer", "status": "degraded", "exit_code": 0, "latency_ms": 55732, "input_tokens": 144, "output_tokens": 943, "total_tokens": 1087, "cost_estimate": 0.0, "cost_source": "provider_usage_logs", "input_hash": "6a4afba59fa567c089a84e6e933c8665380c63c93f00de70e1cba2a26512f623", "output_hash": "317d789d9c23693bb1dda981d9f75e1eff9ca8fa1aa9a722115c6fef78caad23"} +{"timestamp": "2026-07-10T14:27:47Z", "trace_id": "6700500f-3f3d-40ab-92b1-70315fc42ea5", "harness": "H6-agentops", "agent": "goal.orchestrator", "step": "local-worker-cloud-reviewer", "status": "degraded", "exit_code": 0, "latency_ms": 51117, "input_tokens": 143, "output_tokens": 450, "total_tokens": 593, "cost_estimate": 0.0, "cost_source": "provider_usage_logs", "input_hash": "491375254fe83e91fad084ef998ddda780654f1ef921a2cd9148466a74fb1c9a", "output_hash": "642e21a39cd734fd6667a540da58d494fe3e3a2d4a2297b49334dc5536834c77"} +{"timestamp": "2026-07-10T14:33:48Z", "trace_id": "21b6f387-84ad-4856-a8da-102bd70c037a", "harness": "H6-agentops", "agent": "goal.orchestrator", "step": "local-worker-cloud-reviewer", "status": "success", "exit_code": 0, "latency_ms": 44058, "input_tokens": 142, "output_tokens": 418, "total_tokens": 560, "cost_estimate": 0.0, "cost_source": "provider_usage_logs", "input_hash": "5e9f6afb8b00b1c071e64b07ffa8ee66d67f298d65a0b6285a2f3ad92010aa56", "output_hash": "e08f4d05c9a0f21dbdc0edc3d759d9324cbd6ca23d3ab958edac1305ecbfeb17"} +{"timestamp": "2026-07-11T03:05:27Z", "trace_id": "832cc182-7bb8-4666-9bbc-c077a943e7f9", "harness": "H6-agentops", "agent": "goal.orchestrator", "step": "local-worker-cloud-reviewer", "status": "success", "exit_code": 0, "latency_ms": 85641, "input_tokens": 127, "output_tokens": 620, "total_tokens": 747, "cost_estimate": 0.0, "cost_source": "provider_usage_logs", "input_hash": "d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a", "output_hash": "d94f7cb51d952fcaffb585e76c16542aba2f774d64c6d270eab32e7888eccc40"} +{"timestamp": "2026-07-11T03:07:54Z", "trace_id": "3fc2c4a0-32ab-4be3-b12f-ca83a65869c7", "harness": "H6-agentops", "agent": "goal.orchestrator", "step": "local-worker-cloud-reviewer", "status": "success", "exit_code": 0, "latency_ms": 85595, "input_tokens": 127, "output_tokens": 471, "total_tokens": 598, "cost_estimate": 0.0, "cost_source": "provider_usage_logs", "input_hash": "d2f690b77d00aa01559991486f7825c7a1ea402429c5638e8dbf2a40e7b6eb9a", "output_hash": "fd6094f85e823a0be31d54bc5dbecf5e174275ad8b85ca2aa56f58658789e7a3"} +{"timestamp": "2026-07-11T03:15:44Z", "trace_id": "d1f5f078-63dc-4f21-a4f7-c0ccab19bc3d", "harness": "H6-agentops", "agent": "goal.orchestrator", "step": "local-worker-cloud-reviewer", "status": "success", "exit_code": 0, "latency_ms": 140071, "input_tokens": 2406, "output_tokens": 1400, "total_tokens": 3806, "cost_estimate": 0.0, "cost_source": "provider_usage_logs", "input_hash": "63073e9ea949155b9fc8db84dca22a3b8a635b11ab60e45b00c5914c842473fa", "output_hash": "8367afe050a5086cac52162ee740dc4f68949e6901538d86f49802ad069e1e54"} diff --git a/.specify/logs/level5/provider-usage.jsonl b/.specify/logs/level5/provider-usage.jsonl index 0b56a8e..4a06af7 100644 --- a/.specify/logs/level5/provider-usage.jsonl +++ b/.specify/logs/level5/provider-usage.jsonl @@ -13,3 +13,34 @@ {"timestamp": "2026-07-10T06:20:20Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 511, "output_tokens": 288, "total_tokens": 799, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 31437, "status": "success"} {"timestamp": "2026-07-10T07:20:36Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 207, "output_tokens": 2, "total_tokens": 209, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 5868, "status": "success"} {"timestamp": "2026-07-10T07:21:21Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 362, "output_tokens": 59, "total_tokens": 421, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 4829, "status": "success"} +{"timestamp": "2026-07-10T08:38:12Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 206, "output_tokens": 2, "total_tokens": 208, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 6915, "status": "success"} +{"timestamp": "2026-07-10T08:38:31Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 213, "output_tokens": 2, "total_tokens": 215, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 1525, "status": "success"} +{"timestamp": "2026-07-10T08:39:55Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 213, "output_tokens": 2, "total_tokens": 215, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 479, "status": "success"} +{"timestamp": "2026-07-10T08:40:24Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 424, "output_tokens": 521, "total_tokens": 945, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 28692, "status": "success"} +{"timestamp": "2026-07-10T08:45:51Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 256, "output_tokens": 2, "total_tokens": 258, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 4818, "status": "success"} +{"timestamp": "2026-07-10T08:46:14Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 535, "output_tokens": 448, "total_tokens": 983, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 23352, "status": "success"} +{"timestamp": "2026-07-10T08:48:33Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 214, "output_tokens": 2, "total_tokens": 216, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 1435, "status": "success"} +{"timestamp": "2026-07-10T08:48:51Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 394, "output_tokens": 334, "total_tokens": 728, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 18226, "status": "success"} +{"timestamp": "2026-07-10T08:49:39Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 211, "output_tokens": 2, "total_tokens": 213, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 1403, "status": "success"} +{"timestamp": "2026-07-10T08:49:57Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 394, "output_tokens": 335, "total_tokens": 729, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 18167, "status": "success"} +{"timestamp": "2026-07-10T08:51:05Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 210, "output_tokens": 2, "total_tokens": 212, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 1407, "status": "success"} +{"timestamp": "2026-07-10T08:51:25Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 519, "output_tokens": 339, "total_tokens": 858, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 19083, "status": "success"} +{"timestamp": "2026-07-10T14:24:40Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 232, "output_tokens": 2, "total_tokens": 234, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 7351, "status": "success"} +{"timestamp": "2026-07-10T14:25:27Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 144, "output_tokens": 943, "total_tokens": 1087, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 47635, "status": "success"} +{"timestamp": "2026-07-10T14:27:02Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 231, "output_tokens": 2, "total_tokens": 233, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 2816, "status": "success"} +{"timestamp": "2026-07-10T14:27:46Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 143, "output_tokens": 450, "total_tokens": 593, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 43467, "status": "success"} +{"timestamp": "2026-07-10T14:33:09Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 230, "output_tokens": 2, "total_tokens": 232, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 5224, "status": "success"} +{"timestamp": "2026-07-10T14:33:34Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 142, "output_tokens": 418, "total_tokens": 560, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 24025, "status": "success"} +{"timestamp": "2026-07-10T14:59:35Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 2544, "output_tokens": 2, "total_tokens": 2546, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 19119, "status": "success"} +{"timestamp": "2026-07-10T15:07:29Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 2494, "output_tokens": 2, "total_tokens": 2496, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 18212, "status": "success"} +{"timestamp": "2026-07-10T15:11:09Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 207, "output_tokens": 2, "total_tokens": 209, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 1383, "status": "success"} +{"timestamp": "2026-07-10T15:11:50Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 540, "output_tokens": 202, "total_tokens": 742, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 11855, "status": "success"} +{"timestamp": "2026-07-10T15:12:20Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 212, "output_tokens": 2, "total_tokens": 214, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 1394, "status": "success"} +{"timestamp": "2026-07-10T15:13:05Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 595, "output_tokens": 463, "total_tokens": 1058, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 32615, "status": "success"} +{"timestamp": "2026-07-11T02:54:27Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 207, "output_tokens": 2, "total_tokens": 209, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 6819, "status": "success"} +{"timestamp": "2026-07-11T02:54:39Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 531, "output_tokens": 172, "total_tokens": 703, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 10793, "status": "success"} +{"timestamp": "2026-07-11T02:55:20Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "classify", "role": "classify", "input_tokens": 218, "output_tokens": 2, "total_tokens": 220, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 1428, "status": "success"} +{"timestamp": "2026-07-11T02:55:45Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 608, "output_tokens": 472, "total_tokens": 1080, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 25130, "status": "success"} +{"timestamp": "2026-07-11T03:04:43Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 127, "output_tokens": 620, "total_tokens": 747, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 41710, "status": "success"} +{"timestamp": "2026-07-11T03:07:06Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 127, "output_tokens": 471, "total_tokens": 598, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 37299, "status": "success"} +{"timestamp": "2026-07-11T03:14:47Z", "harness": "L5-provider-telemetry", "provider": "ollama", "model": "ornith:9b", "run_id": "adhoc", "step": "generate", "role": "generate", "input_tokens": 2406, "output_tokens": 1400, "total_tokens": 3806, "cost_usd": 0.0, "cost_source": "ollama_local_real_tokens", "latency_ms": 82969, "status": "success"} diff --git a/README.md b/README.md index 05e4727..718e0c4 100644 --- a/README.md +++ b/README.md @@ -6,417 +6,417 @@ AI-SDLC (AI Software Development Life Cycle) - Applying AI to the software devel > [Dùng CASAN để làm dự án — giải thích thực tế, dễ hiểu](docs/guides/CASAN_USING_FOR_REAL_PROJECTS_VI.md). > CASAN core không phụ thuộc Codex, VS Code hay Claude Code; các agent được mô tả > dưới đây là flow demo/legacy có thể đặt phía sau CASAN governance. - -## Table of Contents - + +## Table of Contents + - [Project Objectives](#project-objectives) - [Dùng CASAN cho dự án thực tế](docs/guides/CASAN_USING_FOR_REAL_PROJECTS_VI.md) -- [Requirements](#requirements) -- [Input for Flow](#input-for-flow) -- [Output for FLow](#output-for-flow) -- [Full AI-SDLC Flow](#full-ai-sdlc-flow) - - [Flow Steps](#flow-steps) - - [Flow Diagram](#flow-diagram) - - [Legend](#legend) -- [Agents](#agents) - - [Spec-Kit Agents (Core Flow)](#spec-kit-agents-core-flow) - - [FPT-Defined Agents (IPA Gen + Review + Orchestrator + Test)](#fpt-defined-agents-ipa-gen--review--orchestrator--test) -- [AI-SDLC Usage Guide](#ai-sdlc-usage-guide) -- [Directory Structure](#directory-structure) - - [Directory Details](#directory-details) -- [FPT Deliverable List for NES](#fpt-deliverable-list-for-nes) - - [A. Development Deliverables](#a-development-deliverables) - - [B. Process Records (Required)](#b-process-records-required) - - [C. Process Design Document](#c-process-design-document) - - [D. Specification Files](#d-specification-files) - - [E. Review & Insights](#e-review--insights) - - [Optional Deliverables](#optional-deliverables) -- [License](#license) - - -## Project Objectives - -Apply AI to the SDLC process to automate and optimize the creation of software products from initial requirements to complete source code. - -## Requirements -- **Flow + Prompt + Template**: **Spec-Kit** +- [Requirements](#requirements) +- [Input for Flow](#input-for-flow) +- [Output for FLow](#output-for-flow) +- [Full AI-SDLC Flow](#full-ai-sdlc-flow) + - [Flow Steps](#flow-steps) + - [Flow Diagram](#flow-diagram) + - [Legend](#legend) +- [Agents](#agents) + - [Spec-Kit Agents (Core Flow)](#spec-kit-agents-core-flow) + - [FPT-Defined Agents (IPA Gen + Review + Orchestrator + Test)](#fpt-defined-agents-ipa-gen--review--orchestrator--test) +- [AI-SDLC Usage Guide](#ai-sdlc-usage-guide) +- [Directory Structure](#directory-structure) + - [Directory Details](#directory-details) +- [FPT Deliverable List for NES](#fpt-deliverable-list-for-nes) + - [A. Development Deliverables](#a-development-deliverables) + - [B. Process Records (Required)](#b-process-records-required) + - [C. Process Design Document](#c-process-design-document) + - [D. Specification Files](#d-specification-files) + - [E. Review & Insights](#e-review--insights) + - [Optional Deliverables](#optional-deliverables) +- [License](#license) + + +## Project Objectives + +Apply AI to the SDLC process to automate and optimize the creation of software products from initial requirements to complete source code. + +## Requirements +- **Flow + Prompt + Template**: **Spec-Kit** - **AI Tool của flow demo/legacy**: **Claude Code** là assistant chính; CASAN harness và Control Panel có thể chạy độc lập bằng browser/CLI/CI hoặc dùng model local/OmniRoute. -- **AI-SDLC**: AI-integrated SDLC process **Spec-Kit Modified** (Add IPA Gen + Review Loop + Orchestrator) -- **IPA Template**: Following IPA (Information-technology Promotion Agency) template standards - ---- - -## Input for Flow - -- `apps/okr/domain/input/okr-requirement.md` — Raw requirements from customers -- `docs/technical_architecture.md` — Technical stack decisions and constraints -- `apps/okr/domain/input/change-request/` — Change requests (e.g., `change-0406.md`) - -## Output for FLow - -| Output | Location | Description | -|--------|----------|-------------| -| **IPA Docs** (SRS, BD, DD, Test Cases, Test Reports) | `docs/output/ipa-docs/` | Design documents generated by IPA agents (organized in `srs/`, `bd/`, `dd/`, `testcase/`, `testreport/` subdirectories) | -| **AI Agent Logs** | `docs/output/output_logs/` | Execution logs from AI agents | -| **SRS-Systems** | `docs/output/srs-systems/` | System-wide SRS (generated once during input clarification phase, not part of the main flow steps) | -| **Spec-Kit Artifacts** | `specs/[FEATURE_NAME]/` | Feature artifacts generated by Spec-Kit agents (`spec.md`, `plan.md`, `tasks.md`, contracts, checklists) | -| **Source Code** | `backend/` + `frontend/` | Application source code (NestJS backend + React frontend) | - ---- - -## Full AI-SDLC Flow - -The entire flow is orchestrated by **`okr.bossbuiltin`**, which executes all steps automatically from start to finish. - -### Flow Steps - -| Step | Agent | Output | Category | -|------|-------|--------|----------| -| **STEP 1** | `okr.srs` | SRS (ソフトウェア要件定義書) | 📄 IPA Doc Gen | -| **STEP 2** | `okr.bd` | BD — 外部設計 (Basic Design) | 📄 IPA Doc Gen | -| **STEP 3** | `speckit.specify` | `spec.md` | 📝 Spec-Kit | -| **STEP 4** | `speckit.clarify` | Resolve ambiguities (NO PAUSE) | 📝 Spec-Kit | -| **STEP 5** | `okr.reviewspec` 🔄 | Spec review (auto-retry) | 🔍 Agent Review | -| **STEP 6** | `speckit.plan` | `plan.md` + data-model + contracts | 📝 Spec-Kit | -| **STEP 7** | `okr.reviewplan` 🔄 | Plan review (auto-retry) | 🔍 Agent Review | -| **STEP 8** | `okr.dd` | DD — 内部設計 (Detail Design) | 📄 IPA Doc Gen | -| **STEP 8b** | `okr.testkit` | Test cases (`gen-testcases`) | 🧪 Test | -| **STEP 9** | `speckit.tasks` | `tasks.md` | 📝 Spec-Kit | -| **STEP 10** | `speckit.implement` 🔄 | Implementation + build & fix (auto-retry) | 📝 Spec-Kit | -| **STEP 11** | `okr.reviewcode` 🔄 | Code review + DB data check (auto-retry) | 🔍 Agent Review | -| **STEP 12** | `okr.testkit` 🔄 | Run tests (`run-tests`) — BACK-TO-PLAN on fail | 🧪 Test | -| **STEP 13** | Boss (direct) | Build BE + connect DB + Build FE + Launch UI → `open_browser_page` | 🚀 Deploy | - -### Flow Diagram - -``` -┌─────────────────────────────────────────────────────────────┐ -│ INPUT │ -│ [ Raw Requirement ] [ Clear Requirement ] [ Tech Stack ] │ -└──────────────────────────────┬──────────────────────────────┘ - │ - ┌────────────────────┴──── (one-time, pre-flow) ────┐ - │ okr.srsallsystem → docs/output/srs-systems/ │ - └────────────────────┬──────────────────────────────┘ - │ - ╔════════════════════════════╧══════════════════════════════════╗ - ║ okr.bossbuiltin (Orchestrator) ║ - ║ ║ - ║ ┌─── IPA Doc Gen ───────────────────────────────────────┐ ║ - ║ │ STEP 1 okr.srs → SRS │ ║ - ║ │ STEP 2 okr.bd → BD (外部設計) │ ║ - ║ └───────────────────────────────────────────────────────┘ ║ - ║ │ ║ - ║ ▼ ║ - ║ ┌─── Spec-Kit + Review Loop ────────────────────────────┐ ║ - ║ │ STEP 3 speckit.specify → spec.md │ ║ - ║ │ STEP 4 speckit.clarify → resolve ambiguities │ ║ - ║ │ STEP 5 okr.reviewspec 🔄 auto-retry │ ║ - ║ │ │ │ ║ - ║ │ ▼ │ ║ - ║ │ STEP 6 speckit.plan → plan.md + data-model │ ║ - ║ │ STEP 7 okr.reviewplan 🔄 auto-retry │ ║ - ║ └───────────────────────────────────────────────────────┘ ║ - ║ │ ║ - ║ ▼ ║ - ║ ┌─── IPA Doc Gen (Detail) ──────────────────────────────┐ ║ - ║ │ STEP 8 okr.dd → DD (内部設計) │ ║ - ║ │ STEP 8b okr.testkit → gen-testcases │ ║ - ║ └───────────────────────────────────────────────────────┘ ║ - ║ │ ║ - ║ ▼ ║ - ║ ┌─── Implementation ───────────────────────────────────┐ ║ - ║ │ STEP 9 speckit.tasks → tasks.md │ ║ - ║ │ STEP 10 speckit.implement 🔄 build & fix │ ║ - ║ │ STEP 11 okr.reviewcode 🔄 code review │ ║ - ║ │ STEP 12 okr.testkit 🔄 run-tests │ ║ - ║ │ (BACK-TO-PLAN on fail) │ ║ - ║ └──────────────────────────────────────────────────────┘ ║ - ║ │ ║ - ║ ▼ ║ - ║ ┌─── Deploy ───────────────────────────────────────────┐ ║ - ║ │ STEP 13 Boss → build BE + DB + FE → open_browser │ ║ - ║ └──────────────────────────────────────────────────────┘ ║ - ║ ║ - ╚══════════════════════════════════════════════════════════════╝ -``` - -### Legend - -| Symbol | Meaning | -|--------|---------| -| 🔄 | Auto-retry loop (agent retries until pass) | - ---- - -## Agents - -### Spec-Kit Agents (Core Flow) - -| Agent | Description | -|-------|-------------| -| `speckit.constitution` | Define project principles and constraints | -| `speckit.specify` | Convert requirements into feature specification (`spec.md`) | -| `speckit.clarify` | Resolve ambiguous requirements (no pause) | -| `speckit.plan` | Create implementation plan (`plan.md` + data-model + contracts) | -| `speckit.tasks` | Generate task list (`tasks.md`) | -| `speckit.implement` | Implementation with auto build & fix | -| `speckit.analyze` | Cross-artifact consistency and quality analysis | -| `speckit.checklist` | Generate custom quality checklists | -| `speckit.taskstoissues` | Convert tasks to GitHub issues | - -### FPT-Defined Agents (IPA Gen + Review + Orchestrator + Test) - -| Agent | Description | -|-------|-------------| -| `okr.bossbuiltin` | **Orchestrator** — runs all 13 steps end-to-end automatically | -| `okr.srs` | Generate SRS (ソフトウェア要件定義書) per module → `docs/output/ipa-docs/` | -| `okr.bd` | Generate BD / 外部設計 (Basic Design) per module → `docs/output/ipa-docs/` | -| `okr.dd` | Generate DD / 内部設計 (Detail Design) per module → `docs/output/ipa-docs/` | -| `okr.srsallsystem` | Generate system-wide SRS (one-time, pre-flow) → `docs/output/srs-systems/` | -| `okr.reviewspec` | **Review** — validate `spec.md` quality and completeness | -| `okr.reviewplan` | **Review** — validate `plan.md` conformance to spec | -| `okr.reviewcode` | **Review** — code review + DB data check | -| `okr.testkit` | **Test** — generate test cases from SRS + BD + DD; run automated tests | -| `Flow-Agent` | General-purpose flow agent | - ---- - -## AI-SDLC Usage Guide - -1. **Prepare Input**: Place raw requirements in `apps/okr/domain/input/okr-requirement.md` and tech stack in `docs/technical_architecture.md` -2. **Configuration**: Set up the project constitution first, keep specifications focused on what and why, and only decide technical stack where the input explicitly requires it -3. **(One-time) Generate System SRS**: Run `okr.srsallsystem` to generate system-wide SRS → `docs/output/srs-systems/` -4. **Run AI Flow**: Invoke `okr.bossbuiltin` to execute the full 13-step flow automatically, with strict TDD enforced across specification, planning, task generation, and implementation -5. **Output**: - - `docs/output/ipa-docs/` — IPA design documents (SRS, BD, DD, Test Cases) - - `docs/output/output_logs/` — AI agent execution logs - - `docs/output/srs-systems/` — System-wide SRS (generated once) - - `specs/[FEATURE_NAME]/` — Spec-Kit artifacts (`spec.md`, `plan.md`, `tasks.md`, etc.) - - `backend/` + `frontend/` — Source code (NestJS backend + React frontend) - ---- - -## Directory Structure - -``` -├── .claude/ -│ ├── agents/ # All agent definitions (subagents) -│ │ ├── okr.bossbuiltin.md # Boss orchestrator agent -│ │ ├── speckit.*.md # Spec-Kit agents (specify, clarify, plan, tasks, implement, analyze, checklist, taskstoissues) -│ │ ├── okr.*.md # FPT-defined agents (srs, bd, dd, reviewspec, reviewplan, reviewcode, srsallsystem, testkit) -│ │ ├── protocols/ # Shared protocols (auto-resolve, gate-retry, logging, pipeline-context, etc.) -│ │ ├── steps/ # Step definitions for orchestrator (steps-01-04, steps-05-07, etc.) -│ │ └── templates/ # Agent output templates (pipeline-completion, report-templates) -│ └── commands/ # Claude Code slash commands -│ ├── okr.bossbuiltin.md # Run full pipeline (/okr.bossbuiltin) -│ ├── speckit.*.md # Spec-Kit slash commands -│ └── okr.*.md # OKR agent slash commands -│ -├── CLAUDE.md # Global Claude Code project instructions -│ -├── .specify/ # SpecKit configuration and templates -│ ├── init-options.json # Initialization options -│ ├── memory/ -│ │ └── constitution.md # Project-wide principles and constraints -│ ├── scripts/ # Utility scripts -│ │ ├── bash/ # Bash scripts -│ │ └── powershell/ # PowerShell scripts -│ └── templates/ -│ ├── agent-file-template.md # Agent definition template -│ ├── checklist-template.md # Quality checklist template -│ ├── constitution-template.md -│ ├── spec-template.md # Spec-Kit templates -│ ├── plan-template.md -│ ├── tasks-template.md -│ ├── srs-template.md # IPA templates (SRS, BD, DD) -│ ├── bd-ipa-template.md -│ └── dd-ipa-template.md -│ -├── docs/ -│ ├── input/ # Input for the entire AI-SDLC flow -│ ├── change-request/ # Change requests -│ │ └── okr-requirement.md # Raw requirements from customers -│ ├── technical_architecture.md # Technical stack decisions and constraints -│ └── output/ # Output of the AI-SDLC process -│ ├── ipa-docs/ # IPA documents -│ │ ├── srs/ # SRS documents per module -│ │ ├── bd/ # BD documents per module -│ │ ├── dd/ # DD documents per module -│ │ ├── testcase/ # Test case documents per module -│ │ └── testreport/ # Test report documents per module -│ ├── output_logs/ # AI agent execution logs (per feature) -│ └── srs-systems/ # System-wide SRS (generated once, pre-flow) -│ -├── specs/ # Spec-Kit feature artifacts (per feature) -│ ├── 001-access-authentication/ -│ ├── 002-workspace-dashboard/ -│ ├── 003-objective-keyresult/ -│ └── 004-review-collaboration/ -│ -├── e2e/ # End-to-end tests (Playwright) -│ └── auth/ # Auth E2E tests -│ -├── frontend/ # React + Vite SPA (source code) -│ -├── backend/ # NestJS API service (source code) -│ -├── docker/ # Docker utilities -└── docker-compose.yml # Docker Compose orchestration -``` - -### Directory Details - -#### `.claude/agents/` -Contains all agent definition files used by Claude Code. This includes both **Spec-Kit agents** (core flow: `speckit.specify`, `speckit.clarify`, `speckit.plan`, `speckit.tasks`, `speckit.implement`, plus utility agents `speckit.analyze`, `speckit.checklist`, `speckit.taskstoissues`) and **FPT-defined agents** (`okr.*`): -- **IPA Doc Gen agents** — `okr.srs`, `okr.bd`, `okr.dd`: generate IPA-standard design documents (SRS, BD, DD) per module, output to `docs/output/ipa-docs/`. -- **Review agents** — `okr.reviewspec`, `okr.reviewplan`, `okr.reviewcode`: validate the quality of Spec-Kit artifacts (`spec.md`, `plan.md`, source code) with auto-retry loops. -- **Orchestrator** — `okr.bossbuiltin`: runs the full 13-step flow end-to-end automatically. -- **Test agent** — `okr.testkit`: generates test cases from SRS + BD + DD and executes automated tests (Jest / Playwright). -- **Subdirectories** — `protocols/` (shared protocols for auto-resolve, gate-retry, logging, etc.), `steps/` (orchestrator step definitions), `templates/` (output report templates). - -#### `.claude/commands/` -Claude Code slash commands that invoke the corresponding agents. Type `/speckit.specify`, `/okr.bossbuiltin`, etc. in Claude Code to trigger the pipeline. - -#### `.specify/` -Spec-Kit configuration directory. Stores project-wide principles, scripts, and all templates: -- **`init-options.json`** — Initialization options for Spec-Kit setup. -- **`memory/constitution.md`** — The project constitution: shared principles, constraints, and conventions that apply to every feature across the entire project. -- **`scripts/`** — Utility scripts in `bash/` and `powershell/` for automation tasks. -- **`templates/`** — Format templates used by agents to generate artifacts. Includes Spec-Kit templates (`spec-template.md`, `plan-template.md`, `tasks-template.md`, `constitution-template.md`, `checklist-template.md`, `agent-file-template.md`) and IPA templates (`srs-template.md`, `bd-ipa-template.md`, `dd-ipa-template.md`). - -#### `apps/okr/domain/input/` -Input data for the AI-SDLC flow: -- **`okr-requirement.md`** — Raw requirement documents from the customer. - -#### `docs/technical_architecture.md` -Technical stack decisions and constraints (languages, frameworks, infrastructure). - -#### `apps/okr/domain/input/change-request/` -Change requests applied during the project lifecycle (e.g., `change-0406.md`). - -#### `docs/output/` -All artifacts produced by the AI-SDLC process: -- **`ipa-docs/`** — IPA design documents organized in subdirectories: `srs/`, `bd/`, `dd/`, `testcase/`, `testreport/`. Generated by `okr.srs`, `okr.bd`, `okr.dd`, and `okr.testkit`. Each subdirectory contains per-module documents. -- **`output_logs/`** — Execution logs from AI agents organized per feature (e.g., `000-system-srs/`, `001-access-authentication/`, etc.), useful for debugging and auditing the generation process. -- **`srs-systems/`** — System-wide SRS generated once at the beginning by `okr.srsallsystem`. Contains per-module SRS overviews (`mod01-access-authentication/`, `mod02-workspace-dashboard/`, etc.) plus `srs-overview-system.md`. Not part of the 13-step flow. - -#### `specs/` - -Each folder contains `spec.md`, `plan.md`, `tasks.md`, `checklists/`, and `contracts/`. This directory serves as the **knowledge base** for agents. - -#### Source Code -Source code generated by Agents during `speckit.implement` (STEP 10). -`backend/` and `frontend/` contain the application source code, co-located with the monorepo root: -- **`backend/`** — NestJS application. `src/` contains feature modules (`auth/`, `users/`, `objectives/`, `workspaces/`, `common/`). `prisma/` contains `schema.prisma`, migrations, and `seed.ts`. `test/` contains unit tests organized by module (`auth/`, `objectives/`, `users/`). Includes `Dockerfile`. -- **`frontend/`** — React + Vite SPA (TypeScript). `src/pages/` for route-level components (Login, ForgotPassword, Dashboard, CreateObjective, EditObjective, ObjectiveDetail, KeyResultDetail); `src/components/` organized by feature (`auth/`, `dashboard/`, `objective-detail/`, `objective-form/`), `layout/` (Sidebar, AppHeader, AppLayout) and `ui/` (Button, Input, Alert). `src/hooks/` (useAuth, useObjectives, useUsers, useWorkspaces), `src/lib/` (api, queryClient), `src/schemas/`, `src/types/`. `test/` contains unit tests (`hooks/`, `pages/`). Includes `Dockerfile`. -- **`e2e/`** — End-to-end tests using Playwright, organized by feature (e.g., `auth/auth.spec.ts`). -- **`docker/`** — Docker utilities. Main orchestration is in `docker-compose.yml` at the project root. - ---- - -## OKR Web Application — Quick Start - -### Description - -The **OKR Web Application** is a full-stack web app for managing Objectives and Key Results (OKR). It consists of: - -- **Backend**: NestJS REST API with Prisma ORM and MySQL -- **Frontend**: React + Vite SPA with Tailwind CSS -- **Database**: MySQL 8.0 - -### Prerequisites - -- [Docker](https://docs.docker.com/get-docker/) and Docker Compose -- Node.js 20.x and npm (for local development without Docker) - -### Quick Start (Docker) - -```bash -# Clone the repository and navigate to project root -cd AINative_OKR_Claude_GHCP - -# Build and start all services (MySQL + Backend + Frontend) -docker compose up --build - -# To run in detached mode -docker compose up --build -d -``` - -Docker Compose will automatically: -1. Start MySQL and wait for it to be healthy -2. Run Prisma migrations (`prisma migrate deploy`) -3. Seed the database with default data (`prisma db seed`) -4. Start the NestJS backend -5. Start the React frontend dev server - -### Default Credentials - -| Role | Email | Password | -|------|-------|----------| -| Admin | `admin@okr.local` | `Password@123` | -| Manager | `manager@okr.local` | `Password@123` | -| Employee | `employee@okr.local` | `Password@123` | - -### Service URLs - -| Service | URL | -|---------|-----| -| Frontend | http://localhost:5173 | -| Backend API | http://localhost:3000 | -| API Documentation (Swagger) | http://localhost:3000/api/docs | -| MySQL | localhost:3307 | - -### Development Without Docker - -#### Backend - -```bash -cd backend - -# Install dependencies -npm install - -# Copy and configure environment variables -cp .env.example .env -# Edit .env — set DATABASE_URL to point to your local MySQL instance - -# Run database migrations -npx prisma migrate dev - -# Seed the database -npx prisma db seed - -# Start the development server -npm run start:dev -``` - -#### Frontend - -```bash -cd frontend - -# Install dependencies -npm install - -# Copy and configure environment variables -cp .env.example .env -# Edit .env if the backend runs on a different port - -# Start the Vite dev server -npm run dev -``` - -The frontend will be available at http://localhost:5173 and proxies API requests to http://localhost:3000/api/v1. - -### Stopping Services - -```bash -# Stop all containers -docker compose down - -# Stop and remove volumes (resets the database) -docker compose down -v -``` +- **AI-SDLC**: AI-integrated SDLC process **Spec-Kit Modified** (Add IPA Gen + Review Loop + Orchestrator) +- **IPA Template**: Following IPA (Information-technology Promotion Agency) template standards + +--- + +## Input for Flow + +- `apps/okr/domain/input/okr-requirement.md` — Raw requirements from customers +- `docs/technical_architecture.md` — Technical stack decisions and constraints +- `apps/okr/domain/input/change-request/` — Change requests (e.g., `change-0406.md`) + +## Output for FLow + +| Output | Location | Description | +|--------|----------|-------------| +| **IPA Docs** (SRS, BD, DD, Test Cases, Test Reports) | `docs/output/ipa-docs/` | Design documents generated by IPA agents (organized in `srs/`, `bd/`, `dd/`, `testcase/`, `testreport/` subdirectories) | +| **AI Agent Logs** | `docs/output/output_logs/` | Execution logs from AI agents | +| **SRS-Systems** | `docs/output/srs-systems/` | System-wide SRS (generated once during input clarification phase, not part of the main flow steps) | +| **Spec-Kit Artifacts** | `specs/[FEATURE_NAME]/` | Feature artifacts generated by Spec-Kit agents (`spec.md`, `plan.md`, `tasks.md`, contracts, checklists) | +| **Source Code** | `backend/` + `frontend/` | Application source code (NestJS backend + React frontend) | + +--- + +## Full AI-SDLC Flow + +The entire flow is orchestrated by **`okr.bossbuiltin`**, which executes all steps automatically from start to finish. + +### Flow Steps + +| Step | Agent | Output | Category | +|------|-------|--------|----------| +| **STEP 1** | `okr.srs` | SRS (ソフトウェア要件定義書) | 📄 IPA Doc Gen | +| **STEP 2** | `okr.bd` | BD — 外部設計 (Basic Design) | 📄 IPA Doc Gen | +| **STEP 3** | `speckit.specify` | `spec.md` | 📝 Spec-Kit | +| **STEP 4** | `speckit.clarify` | Resolve ambiguities (NO PAUSE) | 📝 Spec-Kit | +| **STEP 5** | `okr.reviewspec` 🔄 | Spec review (auto-retry) | 🔍 Agent Review | +| **STEP 6** | `speckit.plan` | `plan.md` + data-model + contracts | 📝 Spec-Kit | +| **STEP 7** | `okr.reviewplan` 🔄 | Plan review (auto-retry) | 🔍 Agent Review | +| **STEP 8** | `okr.dd` | DD — 内部設計 (Detail Design) | 📄 IPA Doc Gen | +| **STEP 8b** | `okr.testkit` | Test cases (`gen-testcases`) | 🧪 Test | +| **STEP 9** | `speckit.tasks` | `tasks.md` | 📝 Spec-Kit | +| **STEP 10** | `speckit.implement` 🔄 | Implementation + build & fix (auto-retry) | 📝 Spec-Kit | +| **STEP 11** | `okr.reviewcode` 🔄 | Code review + DB data check (auto-retry) | 🔍 Agent Review | +| **STEP 12** | `okr.testkit` 🔄 | Run tests (`run-tests`) — BACK-TO-PLAN on fail | 🧪 Test | +| **STEP 13** | Boss (direct) | Build BE + connect DB + Build FE + Launch UI → `open_browser_page` | 🚀 Deploy | + +### Flow Diagram + +``` +┌─────────────────────────────────────────────────────────────┐ +│ INPUT │ +│ [ Raw Requirement ] [ Clear Requirement ] [ Tech Stack ] │ +└──────────────────────────────┬──────────────────────────────┘ + │ + ┌────────────────────┴──── (one-time, pre-flow) ────┐ + │ okr.srsallsystem → docs/output/srs-systems/ │ + └────────────────────┬──────────────────────────────┘ + │ + ╔════════════════════════════╧══════════════════════════════════╗ + ║ okr.bossbuiltin (Orchestrator) ║ + ║ ║ + ║ ┌─── IPA Doc Gen ───────────────────────────────────────┐ ║ + ║ │ STEP 1 okr.srs → SRS │ ║ + ║ │ STEP 2 okr.bd → BD (外部設計) │ ║ + ║ └───────────────────────────────────────────────────────┘ ║ + ║ │ ║ + ║ ▼ ║ + ║ ┌─── Spec-Kit + Review Loop ────────────────────────────┐ ║ + ║ │ STEP 3 speckit.specify → spec.md │ ║ + ║ │ STEP 4 speckit.clarify → resolve ambiguities │ ║ + ║ │ STEP 5 okr.reviewspec 🔄 auto-retry │ ║ + ║ │ │ │ ║ + ║ │ ▼ │ ║ + ║ │ STEP 6 speckit.plan → plan.md + data-model │ ║ + ║ │ STEP 7 okr.reviewplan 🔄 auto-retry │ ║ + ║ └───────────────────────────────────────────────────────┘ ║ + ║ │ ║ + ║ ▼ ║ + ║ ┌─── IPA Doc Gen (Detail) ──────────────────────────────┐ ║ + ║ │ STEP 8 okr.dd → DD (内部設計) │ ║ + ║ │ STEP 8b okr.testkit → gen-testcases │ ║ + ║ └───────────────────────────────────────────────────────┘ ║ + ║ │ ║ + ║ ▼ ║ + ║ ┌─── Implementation ───────────────────────────────────┐ ║ + ║ │ STEP 9 speckit.tasks → tasks.md │ ║ + ║ │ STEP 10 speckit.implement 🔄 build & fix │ ║ + ║ │ STEP 11 okr.reviewcode 🔄 code review │ ║ + ║ │ STEP 12 okr.testkit 🔄 run-tests │ ║ + ║ │ (BACK-TO-PLAN on fail) │ ║ + ║ └──────────────────────────────────────────────────────┘ ║ + ║ │ ║ + ║ ▼ ║ + ║ ┌─── Deploy ───────────────────────────────────────────┐ ║ + ║ │ STEP 13 Boss → build BE + DB + FE → open_browser │ ║ + ║ └──────────────────────────────────────────────────────┘ ║ + ║ ║ + ╚══════════════════════════════════════════════════════════════╝ +``` + +### Legend + +| Symbol | Meaning | +|--------|---------| +| 🔄 | Auto-retry loop (agent retries until pass) | + +--- + +## Agents + +### Spec-Kit Agents (Core Flow) + +| Agent | Description | +|-------|-------------| +| `speckit.constitution` | Define project principles and constraints | +| `speckit.specify` | Convert requirements into feature specification (`spec.md`) | +| `speckit.clarify` | Resolve ambiguous requirements (no pause) | +| `speckit.plan` | Create implementation plan (`plan.md` + data-model + contracts) | +| `speckit.tasks` | Generate task list (`tasks.md`) | +| `speckit.implement` | Implementation with auto build & fix | +| `speckit.analyze` | Cross-artifact consistency and quality analysis | +| `speckit.checklist` | Generate custom quality checklists | +| `speckit.taskstoissues` | Convert tasks to GitHub issues | + +### FPT-Defined Agents (IPA Gen + Review + Orchestrator + Test) + +| Agent | Description | +|-------|-------------| +| `okr.bossbuiltin` | **Orchestrator** — runs all 13 steps end-to-end automatically | +| `okr.srs` | Generate SRS (ソフトウェア要件定義書) per module → `docs/output/ipa-docs/` | +| `okr.bd` | Generate BD / 外部設計 (Basic Design) per module → `docs/output/ipa-docs/` | +| `okr.dd` | Generate DD / 内部設計 (Detail Design) per module → `docs/output/ipa-docs/` | +| `okr.srsallsystem` | Generate system-wide SRS (one-time, pre-flow) → `docs/output/srs-systems/` | +| `okr.reviewspec` | **Review** — validate `spec.md` quality and completeness | +| `okr.reviewplan` | **Review** — validate `plan.md` conformance to spec | +| `okr.reviewcode` | **Review** — code review + DB data check | +| `okr.testkit` | **Test** — generate test cases from SRS + BD + DD; run automated tests | +| `Flow-Agent` | General-purpose flow agent | + +--- + +## AI-SDLC Usage Guide + +1. **Prepare Input**: Place raw requirements in `apps/okr/domain/input/okr-requirement.md` and tech stack in `docs/technical_architecture.md` +2. **Configuration**: Set up the project constitution first, keep specifications focused on what and why, and only decide technical stack where the input explicitly requires it +3. **(One-time) Generate System SRS**: Run `okr.srsallsystem` to generate system-wide SRS → `docs/output/srs-systems/` +4. **Run AI Flow**: Invoke `okr.bossbuiltin` to execute the full 13-step flow automatically, with strict TDD enforced across specification, planning, task generation, and implementation +5. **Output**: + - `docs/output/ipa-docs/` — IPA design documents (SRS, BD, DD, Test Cases) + - `docs/output/output_logs/` — AI agent execution logs + - `docs/output/srs-systems/` — System-wide SRS (generated once) + - `specs/[FEATURE_NAME]/` — Spec-Kit artifacts (`spec.md`, `plan.md`, `tasks.md`, etc.) + - `backend/` + `frontend/` — Source code (NestJS backend + React frontend) + +--- + +## Directory Structure + +``` +├── .claude/ +│ ├── agents/ # All agent definitions (subagents) +│ │ ├── okr.bossbuiltin.md # Boss orchestrator agent +│ │ ├── speckit.*.md # Spec-Kit agents (specify, clarify, plan, tasks, implement, analyze, checklist, taskstoissues) +│ │ ├── okr.*.md # FPT-defined agents (srs, bd, dd, reviewspec, reviewplan, reviewcode, srsallsystem, testkit) +│ │ ├── protocols/ # Shared protocols (auto-resolve, gate-retry, logging, pipeline-context, etc.) +│ │ ├── steps/ # Step definitions for orchestrator (steps-01-04, steps-05-07, etc.) +│ │ └── templates/ # Agent output templates (pipeline-completion, report-templates) +│ └── commands/ # Claude Code slash commands +│ ├── okr.bossbuiltin.md # Run full pipeline (/okr.bossbuiltin) +│ ├── speckit.*.md # Spec-Kit slash commands +│ └── okr.*.md # OKR agent slash commands +│ +├── CLAUDE.md # Global Claude Code project instructions +│ +├── .specify/ # SpecKit configuration and templates +│ ├── init-options.json # Initialization options +│ ├── memory/ +│ │ └── constitution.md # Project-wide principles and constraints +│ ├── scripts/ # Utility scripts +│ │ ├── bash/ # Bash scripts +│ │ └── powershell/ # PowerShell scripts +│ └── templates/ +│ ├── agent-file-template.md # Agent definition template +│ ├── checklist-template.md # Quality checklist template +│ ├── constitution-template.md +│ ├── spec-template.md # Spec-Kit templates +│ ├── plan-template.md +│ ├── tasks-template.md +│ ├── srs-template.md # IPA templates (SRS, BD, DD) +│ ├── bd-ipa-template.md +│ └── dd-ipa-template.md +│ +├── docs/ +│ ├── input/ # Input for the entire AI-SDLC flow +│ ├── change-request/ # Change requests +│ │ └── okr-requirement.md # Raw requirements from customers +│ ├── technical_architecture.md # Technical stack decisions and constraints +│ └── output/ # Output of the AI-SDLC process +│ ├── ipa-docs/ # IPA documents +│ │ ├── srs/ # SRS documents per module +│ │ ├── bd/ # BD documents per module +│ │ ├── dd/ # DD documents per module +│ │ ├── testcase/ # Test case documents per module +│ │ └── testreport/ # Test report documents per module +│ ├── output_logs/ # AI agent execution logs (per feature) +│ └── srs-systems/ # System-wide SRS (generated once, pre-flow) +│ +├── specs/ # Spec-Kit feature artifacts (per feature) +│ ├── 001-access-authentication/ +│ ├── 002-workspace-dashboard/ +│ ├── 003-objective-keyresult/ +│ └── 004-review-collaboration/ +│ +├── e2e/ # End-to-end tests (Playwright) +│ └── auth/ # Auth E2E tests +│ +├── frontend/ # React + Vite SPA (source code) +│ +├── backend/ # NestJS API service (source code) +│ +├── docker/ # Docker utilities +└── docker-compose.yml # Docker Compose orchestration +``` + +### Directory Details + +#### `.claude/agents/` +Contains all agent definition files used by Claude Code. This includes both **Spec-Kit agents** (core flow: `speckit.specify`, `speckit.clarify`, `speckit.plan`, `speckit.tasks`, `speckit.implement`, plus utility agents `speckit.analyze`, `speckit.checklist`, `speckit.taskstoissues`) and **FPT-defined agents** (`okr.*`): +- **IPA Doc Gen agents** — `okr.srs`, `okr.bd`, `okr.dd`: generate IPA-standard design documents (SRS, BD, DD) per module, output to `docs/output/ipa-docs/`. +- **Review agents** — `okr.reviewspec`, `okr.reviewplan`, `okr.reviewcode`: validate the quality of Spec-Kit artifacts (`spec.md`, `plan.md`, source code) with auto-retry loops. +- **Orchestrator** — `okr.bossbuiltin`: runs the full 13-step flow end-to-end automatically. +- **Test agent** — `okr.testkit`: generates test cases from SRS + BD + DD and executes automated tests (Jest / Playwright). +- **Subdirectories** — `protocols/` (shared protocols for auto-resolve, gate-retry, logging, etc.), `steps/` (orchestrator step definitions), `templates/` (output report templates). + +#### `.claude/commands/` +Claude Code slash commands that invoke the corresponding agents. Type `/speckit.specify`, `/okr.bossbuiltin`, etc. in Claude Code to trigger the pipeline. + +#### `.specify/` +Spec-Kit configuration directory. Stores project-wide principles, scripts, and all templates: +- **`init-options.json`** — Initialization options for Spec-Kit setup. +- **`memory/constitution.md`** — The project constitution: shared principles, constraints, and conventions that apply to every feature across the entire project. +- **`scripts/`** — Utility scripts in `bash/` and `powershell/` for automation tasks. +- **`templates/`** — Format templates used by agents to generate artifacts. Includes Spec-Kit templates (`spec-template.md`, `plan-template.md`, `tasks-template.md`, `constitution-template.md`, `checklist-template.md`, `agent-file-template.md`) and IPA templates (`srs-template.md`, `bd-ipa-template.md`, `dd-ipa-template.md`). + +#### `apps/okr/domain/input/` +Input data for the AI-SDLC flow: +- **`okr-requirement.md`** — Raw requirement documents from the customer. + +#### `docs/technical_architecture.md` +Technical stack decisions and constraints (languages, frameworks, infrastructure). + +#### `apps/okr/domain/input/change-request/` +Change requests applied during the project lifecycle (e.g., `change-0406.md`). + +#### `docs/output/` +All artifacts produced by the AI-SDLC process: +- **`ipa-docs/`** — IPA design documents organized in subdirectories: `srs/`, `bd/`, `dd/`, `testcase/`, `testreport/`. Generated by `okr.srs`, `okr.bd`, `okr.dd`, and `okr.testkit`. Each subdirectory contains per-module documents. +- **`output_logs/`** — Execution logs from AI agents organized per feature (e.g., `000-system-srs/`, `001-access-authentication/`, etc.), useful for debugging and auditing the generation process. +- **`srs-systems/`** — System-wide SRS generated once at the beginning by `okr.srsallsystem`. Contains per-module SRS overviews (`mod01-access-authentication/`, `mod02-workspace-dashboard/`, etc.) plus `srs-overview-system.md`. Not part of the 13-step flow. + +#### `specs/` + +Each folder contains `spec.md`, `plan.md`, `tasks.md`, `checklists/`, and `contracts/`. This directory serves as the **knowledge base** for agents. + +#### Source Code +Source code generated by Agents during `speckit.implement` (STEP 10). +`backend/` and `frontend/` contain the application source code, co-located with the monorepo root: +- **`backend/`** — NestJS application. `src/` contains feature modules (`auth/`, `users/`, `objectives/`, `workspaces/`, `common/`). `prisma/` contains `schema.prisma`, migrations, and `seed.ts`. `test/` contains unit tests organized by module (`auth/`, `objectives/`, `users/`). Includes `Dockerfile`. +- **`frontend/`** — React + Vite SPA (TypeScript). `src/pages/` for route-level components (Login, ForgotPassword, Dashboard, CreateObjective, EditObjective, ObjectiveDetail, KeyResultDetail); `src/components/` organized by feature (`auth/`, `dashboard/`, `objective-detail/`, `objective-form/`), `layout/` (Sidebar, AppHeader, AppLayout) and `ui/` (Button, Input, Alert). `src/hooks/` (useAuth, useObjectives, useUsers, useWorkspaces), `src/lib/` (api, queryClient), `src/schemas/`, `src/types/`. `test/` contains unit tests (`hooks/`, `pages/`). Includes `Dockerfile`. +- **`e2e/`** — End-to-end tests using Playwright, organized by feature (e.g., `auth/auth.spec.ts`). +- **`docker/`** — Docker utilities. Main orchestration is in `docker-compose.yml` at the project root. + +--- + +## OKR Web Application — Quick Start + +### Description + +The **OKR Web Application** is a full-stack web app for managing Objectives and Key Results (OKR). It consists of: + +- **Backend**: NestJS REST API with Prisma ORM and MySQL +- **Frontend**: React + Vite SPA with Tailwind CSS +- **Database**: MySQL 8.0 + +### Prerequisites + +- [Docker](https://docs.docker.com/get-docker/) and Docker Compose +- Node.js 20.x and npm (for local development without Docker) + +### Quick Start (Docker) + +```bash +# Clone the repository and navigate to project root +cd AINative_OKR_Claude_GHCP + +# Build and start all services (MySQL + Backend + Frontend) +docker compose up --build + +# To run in detached mode +docker compose up --build -d +``` + +Docker Compose will automatically: +1. Start MySQL and wait for it to be healthy +2. Run Prisma migrations (`prisma migrate deploy`) +3. Seed the database with default data (`prisma db seed`) +4. Start the NestJS backend +5. Start the React frontend dev server + +### Default Credentials + +| Role | Email | Password | +|------|-------|----------| +| Admin | `admin@okr.local` | `Password@123` | +| Manager | `manager@okr.local` | `Password@123` | +| Employee | `employee@okr.local` | `Password@123` | + +### Service URLs + +| Service | URL | +|---------|-----| +| Frontend | http://localhost:5173 | +| Backend API | http://localhost:3000 | +| API Documentation (Swagger) | http://localhost:3000/api/docs | +| MySQL | localhost:3307 | + +### Development Without Docker + +#### Backend + +```bash +cd backend + +# Install dependencies +npm install + +# Copy and configure environment variables +cp .env.example .env +# Edit .env — set DATABASE_URL to point to your local MySQL instance + +# Run database migrations +npx prisma migrate dev + +# Seed the database +npx prisma db seed + +# Start the development server +npm run start:dev +``` + +#### Frontend + +```bash +cd frontend + +# Install dependencies +npm install + +# Copy and configure environment variables +cp .env.example .env +# Edit .env if the backend runs on a different port + +# Start the Vite dev server +npm run dev +``` + +The frontend will be available at http://localhost:5173 and proxies API requests to http://localhost:3000/api/v1. + +### Stopping Services + +```bash +# Stop all containers +docker compose down + +# Stop and remove volumes (resets the database) +docker compose down -v +``` diff --git a/docs/diagrams/goal-model-interaction.mmd b/docs/diagrams/goal-model-interaction.mmd new file mode 100644 index 0000000..333efb6 --- /dev/null +++ b/docs/diagrams/goal-model-interaction.mmd @@ -0,0 +1,13 @@ +sequenceDiagram + participant O as Goal Orchestrator + participant L as Local Worker + participant S as H4 Security + participant C as Cloud Reviewer + participant A as H5 Audit + + O->>L: Objective and governed context + L-->>S: Primary proposal + S->>C: Safe draft for independent review + C-->>O: Critique and refined outcome + O->>A: Certified final decision + A-->>O: Audit hash and status diff --git a/docs/diagrams/goal-model-interaction.svg b/docs/diagrams/goal-model-interaction.svg new file mode 100644 index 0000000..d9065b8 --- /dev/null +++ b/docs/diagrams/goal-model-interaction.svg @@ -0,0 +1 @@ +H5 AuditCloud ReviewerH4 SecurityLocal WorkerGoal OrchestratorH5 AuditCloud ReviewerH4 SecurityLocal WorkerGoal OrchestratorObjective and governed contextPrimary proposalSafe draft for independent reviewCritique and refined outcomeCertified final decisionAudit hash and status \ No newline at end of file diff --git a/docs/output/casan/agentops-dashboard.html b/docs/output/casan/agentops-dashboard.html index ec726c1..655a571 100644 --- a/docs/output/casan/agentops-dashboard.html +++ b/docs/output/casan/agentops-dashboard.html @@ -43,7 +43,7 @@ tr:nth-child(even) td { background: #f7f9fc; }

CASAN Level 4 Central AgentOps Dashboard

-

7-harness security posture · Level-5 controls demonstrated locally · điểm công tâm theo rubric (evidence/scoring-run-report.md) · Generated: 2026-07-08T09:20:56Z

+

7-harness security posture · Level-5 controls demonstrated locally · điểm công tâm theo rubric (evidence/scoring-run-report.md) · Generated: 2026-07-11T02:34:11Z

CASAN Level 4 — chứng minh bằng tấn công Average 80.9/100 @@ -80,34 +80,34 @@ tr:nth-child(even) td { background: #f7f9fc; }

Telemetry trực tiếp (live) · pipeline harness

-
Total Runs
6
-
Average Latency
200.33 ms
-
Estimated Cost
$0.083484
-
Failures
1
-
Fallback Routes
1
+
Total Runs
28
+
Average Latency
13123.61 ms
+
Estimated Cost
$0.083668
+
Failures
7
+
Fallback Routes
3
-
Provider Runs
2
-
Provider Tokens (thật)
5556
+
Provider Runs
39
+
Provider Tokens (thật)
25370
Provider Cost
$0.16668
-
Tool Denials
2
-
Hallucination Signals
4
+
Tool Denials
6
+
Hallucination Signals
8

Governance Signals

- - - - + + + +
SignalValue
Tool registry denials2
Fallback records1
Tool registry records3
Provider telemetry records2
Tool registry denials6
Fallback records3
Tool registry records11
Provider telemetry records39
Registered harness projects0

Recent AgentOps Metrics (live)

- +
TraceAgentStepStatusLatencyTokensCost
55ac657c…demo.agentdemo-stepsuccess94ms12$2.4e-05
a14c5f93…demo.agentstep-1-srssuccess319ms26$5.2e-05
9b58d449…demo.agentspeckit.implementsuccess103ms2778$0.08334
aa388ecd…demo.agentfailing-stepfailed293ms6$1.2e-05
bf63152b…wrapper.demowrapper-stepsuccess92ms14$2.8e-05
48236a7c…wrapper.demowrapper-stepsuccess301ms14$2.8e-05
96b4c003…chat.ask-casanask-casan-readonlyfailed141ms10$0.0
c924568f…chat.ask-casanask-casan-readonlyfailed219ms10$0.0
10331f30…chat.ask-casanask-casan-readonlyfailed342ms10$0.0
2e9b93cb…chat.ask-casanask-casan-readonlysuccess57161ms57$0.0
d8796e51…chat.ask-casanask-casan-readonlysuccess57864ms833$0.0
b5ad414a…chat.ask-casanask-casan-readonlysuccess43174ms799$0.0
1fd5d347…chat.ask-casanask-casan-readonlysuccess51212ms421$0.0
1c93ce8a…goal.orchestratorlocal-worker-cloud-reviewerdegraded55732ms1087$0.0
6700500f…goal.orchestratorlocal-worker-cloud-reviewerdegraded51117ms593$0.0
21b6f387…goal.orchestratorlocal-worker-cloud-reviewersuccess44058ms560$0.0
diff --git a/docs/output/casan/central-agentops-dashboard.html b/docs/output/casan/central-agentops-dashboard.html index ec726c1..655a571 100644 --- a/docs/output/casan/central-agentops-dashboard.html +++ b/docs/output/casan/central-agentops-dashboard.html @@ -43,7 +43,7 @@ tr:nth-child(even) td { background: #f7f9fc; }

CASAN Level 4 Central AgentOps Dashboard

-

7-harness security posture · Level-5 controls demonstrated locally · điểm công tâm theo rubric (evidence/scoring-run-report.md) · Generated: 2026-07-08T09:20:56Z

+

7-harness security posture · Level-5 controls demonstrated locally · điểm công tâm theo rubric (evidence/scoring-run-report.md) · Generated: 2026-07-11T02:34:11Z

CASAN Level 4 — chứng minh bằng tấn công Average 80.9/100 @@ -80,34 +80,34 @@ tr:nth-child(even) td { background: #f7f9fc; }

Telemetry trực tiếp (live) · pipeline harness

-
Total Runs
6
-
Average Latency
200.33 ms
-
Estimated Cost
$0.083484
-
Failures
1
-
Fallback Routes
1
+
Total Runs
28
+
Average Latency
13123.61 ms
+
Estimated Cost
$0.083668
+
Failures
7
+
Fallback Routes
3
-
Provider Runs
2
-
Provider Tokens (thật)
5556
+
Provider Runs
39
+
Provider Tokens (thật)
25370
Provider Cost
$0.16668
-
Tool Denials
2
-
Hallucination Signals
4
+
Tool Denials
6
+
Hallucination Signals
8

Governance Signals

- - - - + + + +
SignalValue
Tool registry denials2
Fallback records1
Tool registry records3
Provider telemetry records2
Tool registry denials6
Fallback records3
Tool registry records11
Provider telemetry records39
Registered harness projects0

Recent AgentOps Metrics (live)

- +
TraceAgentStepStatusLatencyTokensCost
55ac657c…demo.agentdemo-stepsuccess94ms12$2.4e-05
a14c5f93…demo.agentstep-1-srssuccess319ms26$5.2e-05
9b58d449…demo.agentspeckit.implementsuccess103ms2778$0.08334
aa388ecd…demo.agentfailing-stepfailed293ms6$1.2e-05
bf63152b…wrapper.demowrapper-stepsuccess92ms14$2.8e-05
48236a7c…wrapper.demowrapper-stepsuccess301ms14$2.8e-05
96b4c003…chat.ask-casanask-casan-readonlyfailed141ms10$0.0
c924568f…chat.ask-casanask-casan-readonlyfailed219ms10$0.0
10331f30…chat.ask-casanask-casan-readonlyfailed342ms10$0.0
2e9b93cb…chat.ask-casanask-casan-readonlysuccess57161ms57$0.0
d8796e51…chat.ask-casanask-casan-readonlysuccess57864ms833$0.0
b5ad414a…chat.ask-casanask-casan-readonlysuccess43174ms799$0.0
1fd5d347…chat.ask-casanask-casan-readonlysuccess51212ms421$0.0
1c93ce8a…goal.orchestratorlocal-worker-cloud-reviewerdegraded55732ms1087$0.0
6700500f…goal.orchestratorlocal-worker-cloud-reviewerdegraded51117ms593$0.0
21b6f387…goal.orchestratorlocal-worker-cloud-reviewersuccess44058ms560$0.0
diff --git a/packages/casan-control-panel/frontend/src/components/goals/ModelInteractionDiagram.tsx b/packages/casan-control-panel/frontend/src/components/goals/ModelInteractionDiagram.tsx new file mode 100644 index 0000000..46fb73a --- /dev/null +++ b/packages/casan-control-panel/frontend/src/components/goals/ModelInteractionDiagram.tsx @@ -0,0 +1,54 @@ +import type { GoalJob, GoalStage } from '../../lib/api'; +import { StatusBadge } from '../ui/Card'; + +interface ModelInteractionDiagramProps { + goal: GoalJob; + localStage?: GoalStage; + cloudStage?: GoalStage; +} + +const tone = (status: string) => status === 'pass' || status === 'completed' ? 'bg-emerald-500' : status === 'error' || status === 'failed' ? 'bg-rose-500' : status === 'warning' || status === 'degraded' ? 'bg-amber-500' : status === 'running' ? 'animate-pulse bg-indigo-500 motion-reduce:animate-none' : 'bg-slate-300'; + +function Exchange({ from, to, label, status, reverse = false }: { from: string; to: string; label: string; status: string; reverse?: boolean }) { + return ( +
+
{reverse ? to : from}
+
+
+ + + +
+
{label}
+
+
{reverse ? from : to}
+
+ ); +} + +export function ModelInteractionDiagram({ goal, localStage, cloudStage }: ModelInteractionDiagramProps) { + const localStatus = localStage?.status ?? 'queued'; + const cloudStatus = cloudStage?.status ?? 'queued'; + return ( +
+
+
Live model exchange

Models trao đổi và kiểm chứng như thế nào

+ +
+
+
+
Local worker
{localStage?.model || goal.local_model}
+
Cloud reviewer
{cloudStage?.model || goal.cloud_model}
+
+
+ + + + + +
+
+
Mỗi mũi tên thể hiện một boundary có kiểm soát; màu sắc lấy từ trạng thái thực tế của goal và từng worker.
+
+ ); +} diff --git a/packages/casan-control-panel/frontend/src/components/ui/MarkdownText.tsx b/packages/casan-control-panel/frontend/src/components/ui/MarkdownText.tsx new file mode 100644 index 0000000..d5cc2fc --- /dev/null +++ b/packages/casan-control-panel/frontend/src/components/ui/MarkdownText.tsx @@ -0,0 +1,64 @@ +import type { ReactNode } from 'react'; + +interface MarkdownTextProps { + text: string; + compact?: boolean; +} + +function inlineMarkdown(value: string): ReactNode[] { + const tokens = value.split(/(`[^`]+`|\*\*[^*]+\*\*|\[[^\]]+\]\(https?:\/\/[^)]+\))/g); + return tokens.filter(Boolean).map((token, index) => { + if (token.startsWith('`') && token.endsWith('`')) return {token.slice(1, -1)}; + if (token.startsWith('**') && token.endsWith('**')) return {token.slice(2, -2)}; + const link = token.match(/^\[([^\]]+)\]\((https?:\/\/[^)]+)\)$/); + if (link) return {link[1]}; + return token; + }); +} + +export function MarkdownText({ text, compact = false }: MarkdownTextProps) { + const lines = text.replace(/\r\n/g, '\n').split('\n'); + const blocks: ReactNode[] = []; + let list: string[] = []; + let code: string[] = []; + let inCode = false; + + const flushList = () => { + if (list.length === 0) return; + blocks.push(
    {list.map((item, index) =>
  • {inlineMarkdown(item)}
  • )}
); + list = []; + }; + const flushCode = () => { + if (code.length === 0) return; + blocks.push(
{code.join('\n')}
); + code = []; + }; + + lines.forEach((line) => { + if (line.trim().startsWith('```')) { + flushList(); + if (inCode) flushCode(); + inCode = !inCode; + return; + } + if (inCode) { code.push(line); return; } + const bullet = line.match(/^\s*[-*+]\s+(.+)$/); + if (bullet) { list.push(bullet[1]); return; } + flushList(); + const heading = line.match(/^(#{1,4})\s+(.+)$/); + if (heading) { + const size = heading[1].length === 1 ? 'text-lg' : heading[1].length === 2 ? 'text-base' : 'text-sm'; + blocks.push(

{inlineMarkdown(heading[2])}

); + } else if (/^---+$/.test(line.trim())) { + blocks.push(
); + } else if (line.trim()) { + blocks.push(

{inlineMarkdown(line)}

); + } else if (!compact) { + blocks.push(
); + } + }); + flushList(); + flushCode(); + + return
{blocks}
; +} diff --git a/packages/casan-control-panel/frontend/src/pages/Chat.tsx b/packages/casan-control-panel/frontend/src/pages/Chat.tsx index 985d693..e648572 100644 --- a/packages/casan-control-panel/frontend/src/pages/Chat.tsx +++ b/packages/casan-control-panel/frontend/src/pages/Chat.tsx @@ -1,4 +1,4 @@ -import { type KeyboardEvent, useMemo, useState } from 'react'; +import { type KeyboardEvent, useEffect, useMemo, useState } from 'react'; import { useMutation, useQuery } from '@tanstack/react-query'; import { Link } from 'react-router-dom'; import { api, ChatAction, ChatAgent, ChatAnswer, ChatHistoryTurn, ChatModelProvider, ChatStreamPhase, SettingsActor } from '../lib/api'; @@ -8,6 +8,17 @@ import { ModelConnectionPanel } from '../components/chat/ModelConnectionPanel'; const ROLES = ['viewer', 'auditor', 'operator', 'project-admin', 'org-admin']; type MessageKind = 'user' | 'assistant' | 'draft'; +type HarnessRunStatus = 'idle' | 'running' | 'completed' | 'failed'; + +const HARNESS_STAGES = [ + { id: 'H1', label: 'Context' }, + { id: 'H2', label: 'Tool' }, + { id: 'H3', label: 'Evaluation' }, + { id: 'H4', label: 'Security' }, + { id: 'H5', label: 'Governance' }, + { id: 'H6', label: 'AgentOps' }, + { id: 'H7', label: 'Orchestration' }, +] as const; interface WorkspaceMessage { id: string; @@ -95,6 +106,8 @@ export function Chat() { const [error, setError] = useState(null); const [streaming, setStreaming] = useState(true); const [streamBusy, setStreamBusy] = useState(false); + const [harnessStatus, setHarnessStatus] = useState('idle'); + const [activeHarness, setActiveHarness] = useState(0); const auditQuery = useQuery({ queryKey: ['chat-audit'], queryFn: api.verifyChatAudit, retry: false }); const conversationsQuery = useQuery({ queryKey: ['chat-history', actor], queryFn: () => api.chatHistory(actor), retry: false }); @@ -142,16 +155,27 @@ export function Chat() { setPendingMessage(null); setError(null); setMessage(''); + setActiveHarness(HARNESS_STAGES.length - 1); + setHarnessStatus('completed'); refreshChat(); }, onError: (reason: unknown) => { setPendingMessage(null); setError(errorMessage(reason)); + setHarnessStatus('failed'); }, }); const busy = ask.isPending || streamBusy; + useEffect(() => { + if (!busy || harnessStatus !== 'running') return undefined; + const timer = window.setInterval(() => { + setActiveHarness((current) => Math.min(current + 1, HARNESS_STAGES.length - 1)); + }, 1100); + return () => window.clearInterval(timer); + }, [busy, harnessStatus]); + const runStream = async (text: string) => { setStreamBusy(true); setError(null); @@ -165,6 +189,8 @@ export function Chat() { setLast(finalToAnswer(phase, actor)); setPendingMessage(null); setMessage(''); + setActiveHarness(HARNESS_STAGES.length - 1); + setHarnessStatus('completed'); } }); refreshChat(); @@ -172,6 +198,7 @@ export function Chat() { setPendingMessage(null); setDraftText(null); setError(errorMessage(reason)); + setHarnessStatus('failed'); } finally { setStreamBusy(false); } @@ -180,6 +207,8 @@ export function Chat() { const submit = (override?: { message?: string; agentId?: string; skillId?: string }) => { const text = (override?.message ?? message).trim(); if (!text || busy) return; + setActiveHarness(0); + setHarnessStatus('running'); if (streaming && !override) void runStream(text); else { setPendingMessage(text); @@ -202,6 +231,8 @@ export function Chat() { setPendingMessage(null); setDraftText(null); setError(null); + setHarnessStatus('idle'); + setActiveHarness(0); }; return ( @@ -263,6 +294,34 @@ export function Chat() {
))} + {harnessStatus !== 'idle' && ( +
+
+
+ + {harnessStatus === 'running' && } + + + {harnessStatus === 'running' ? `CASAN đang phản hồi · ${HARNESS_STAGES[activeHarness].id} ${HARNESS_STAGES[activeHarness].label}` : harnessStatus === 'completed' ? 'Phản hồi đã hoàn tất qua H1–H7' : `Phản hồi dừng tại ${HARNESS_STAGES[activeHarness].id}`} +
+ {harnessStatus === 'running' ? 'Processing' : harnessStatus} +
+
+ {HARNESS_STAGES.map((stage, index) => { + const done = harnessStatus === 'completed' || index < activeHarness; + const active = harnessStatus === 'running' && index === activeHarness; + const failed = harnessStatus === 'failed' && index === activeHarness; + return ( +
+
+
{stage.id}
+
{stage.label}
+
+ ); + })} +
+
+ )} {!historyQuery.isLoading && messages.length === 0 &&

A governed empty state

Ask for a plan, a security posture, or an evidence-backed comparison. CASAN will cite what it knows and decline what it cannot govern.

}
@@ -300,7 +359,7 @@ export function Chat() {
Registered actions
-
{(actionsQuery.data?.actions ?? []).slice(0, 3).map((action: ChatAction) => )}{actionsQuery.isError &&
Registered actions are unavailable.
}
+
{(actionsQuery.data?.actions ?? []).slice(0, 3).map((action: ChatAction) => )}{actionsQuery.isError &&
Registered actions are unavailable.
}
setConnectionsOpen(false)} onSelect={(provider, model) => { setModelProvider(provider); setModelId(model); void connectionsQuery.refetch(); }} /> diff --git a/packages/casan-control-panel/frontend/src/pages/Goals.tsx b/packages/casan-control-panel/frontend/src/pages/Goals.tsx index 8d3fab6..b9a263a 100644 --- a/packages/casan-control-panel/frontend/src/pages/Goals.tsx +++ b/packages/casan-control-panel/frontend/src/pages/Goals.tsx @@ -4,6 +4,8 @@ import { useSearchParams } from 'react-router-dom'; import { api, type GoalJob, type SettingsActor } from '../lib/api'; import { Card, StatusBadge } from '../components/ui/Card'; import { TraceExplorer } from '../components/trace/TraceExplorer'; +import { MarkdownText } from '../components/ui/MarkdownText'; +import { ModelInteractionDiagram } from '../components/goals/ModelInteractionDiagram'; const DEFAULT_ACTOR: SettingsActor = { actor: 'local-operator', role: 'project-admin', project: 'default', tenant: 'default' }; const TERMINAL = new Set(['completed', 'degraded', 'failed']); @@ -111,19 +113,21 @@ export function Goals() {
+ + }>
Objective
-

{selected.goal}

+
- {selected.result ?
{selected.result}
: ( + {selected.result ?
: (
Models are working. This page refreshes automatically.
)} {selected.error &&
{selected.error}
} - {selected.local_draft &&
Inspect local worker draft
{selected.local_draft}
} + {selected.local_draft &&
Inspect local worker draft
}
@@ -134,7 +138,7 @@ export function Goals() {
{(listQuery.data?.goals ?? []).map((item) => ( ))} diff --git a/packages/casan-harness/scripts/bash/goal-orchestrator.py b/packages/casan-harness/scripts/bash/goal-orchestrator.py index 825d946..6d433dd 100644 --- a/packages/casan-harness/scripts/bash/goal-orchestrator.py +++ b/packages/casan-harness/scripts/bash/goal-orchestrator.py @@ -117,11 +117,17 @@ def scan(text: str, mode: str): output = os.path.join(directory, "output.txt") with open(source, "w", encoding="utf-8") as handle: handle.write(text) + environment = os.environ.copy() + # The goal workflow already records and enforces its H4 boundary. Keep + # deterministic injection/secret/PII checks active, but do not turn a + # temporary semantic-classifier outage into a false-positive block. + environment["CASAN_SECURITY_STRICT"] = "0" result = subprocess.run( ["bash", SECURITY, source, output, mode], cwd=ROOT, capture_output=True, text=True, + env=environment, timeout=60, ) safe = "" @@ -141,6 +147,11 @@ def call_model(model: str, prompt: str, cloud: bool): handle.write(prompt) environment = os.environ.copy() environment["CASAN_PREFLIGHT"] = "1" if cloud else "0" + # Long Markdown objectives need enough time for a local 9B model to + # ingest the brief and produce a bounded plan. The outer goal timeout + # remains the hard ceiling; this only raises the router's 60s default. + environment.setdefault("CASAN_MODEL_TIMEOUT_SEC", os.environ.get("CASAN_GOAL_LOCAL_TIMEOUT_SEC", "240") if not cloud else "120") + environment.setdefault("CASAN_MODEL_GENERATE_MAX_TOKENS", os.environ.get("CASAN_GOAL_MAX_OUTPUT_TOKENS", "1400")) try: result = subprocess.run( ["bash", MODEL_ROUTER, prompt_path, output_path, "--role", "generate", "--model", model], @@ -292,7 +303,7 @@ def run(job_path: str) -> int: if not ok: stage(job_path, "local-worker", "error", reason, job.get("local_provider", ""), local_model) emit(goal_id, "H2-tool", "error", "Local worker failed", {"reason": reason}) - raise RuntimeError("local_worker_failed") + raise RuntimeError(f"local_worker_failed:{reason}") allowed, safe_local = scan(local_draft, "output") if not allowed: emit(goal_id, "H4-security", "blocked", "Local worker output rejected") diff --git a/packages/casan-harness/scripts/bash/local-full.sh b/packages/casan-harness/scripts/bash/local-full.sh index 1596a97..e2f10a7 100755 --- a/packages/casan-harness/scripts/bash/local-full.sh +++ b/packages/casan-harness/scripts/bash/local-full.sh @@ -40,14 +40,26 @@ start_auth_bridge() { openssl rand -hex 32 > "$AUTH_BRIDGE_TOKEN_FILE" chmod 600 "$AUTH_BRIDGE_TOKEN_FILE" fi - if [[ -f "$AUTH_BRIDGE_PID_FILE" ]] && kill -0 "$(cat "$AUTH_BRIDGE_PID_FILE")" 2>/dev/null; then + if curl -fsS -m 2 "http://127.0.0.1:20130/healthz" >/dev/null 2>&1; then return 0 fi + if [[ -f "$AUTH_BRIDGE_PID_FILE" ]]; then + local existing_pid + existing_pid="$(cat "$AUTH_BRIDGE_PID_FILE")" + # A PID can be stale or reused by an unrelated process. Health is the + # authoritative signal; clean the stale state before starting the bridge. + kill "$existing_pid" 2>/dev/null || true + rm -f "$AUTH_BRIDGE_PID_FILE" + fi [[ -f "$AUTH_BRIDGE" ]] || { echo "CASAN_AUTH_BRIDGE_MISSING" >&2; return 1; } nohup python3 "$AUTH_BRIDGE" --bind 0.0.0.0 --port 20130 --token-file "$AUTH_BRIDGE_TOKEN_FILE" --audit-log "$AUTH_BRIDGE_AUDIT" > "$AUTH_BRIDGE_LOG" 2>&1 & echo "$!" > "$AUTH_BRIDGE_PID_FILE" chmod 600 "$AUTH_BRIDGE_PID_FILE" "$AUTH_BRIDGE_LOG" "$AUTH_BRIDGE_AUDIT" 2>/dev/null || true - wait_url "http://127.0.0.1:20130/healthz" + if ! wait_url "http://127.0.0.1:20130/healthz"; then + kill "$(cat "$AUTH_BRIDGE_PID_FILE")" 2>/dev/null || true + rm -f "$AUTH_BRIDGE_PID_FILE" + return 1 + fi } stop_auth_bridge() { @@ -112,8 +124,8 @@ case "$CMD" in bash "$INFRA" status echo "=== control panel ===" cp_compose ps - if [[ -f "$AUTH_BRIDGE_PID_FILE" ]] && kill -0 "$(cat "$AUTH_BRIDGE_PID_FILE")" 2>/dev/null; then - echo "provider_auth_bridge=running pid=$(cat "$AUTH_BRIDGE_PID_FILE")" + if curl -fsS -m 2 "http://127.0.0.1:20130/healthz" >/dev/null 2>&1; then + echo "provider_auth_bridge=running" else echo "provider_auth_bridge=stopped" fi diff --git a/packages/casan-harness/scripts/bash/model-call.py b/packages/casan-harness/scripts/bash/model-call.py index 5b63c5c..80a28c3 100755 --- a/packages/casan-harness/scripts/bash/model-call.py +++ b/packages/casan-harness/scripts/bash/model-call.py @@ -171,6 +171,11 @@ def call_ollama(model_name, prompt, role): # small budget is consumed by reasoning and `response` comes back empty. body["think"] = False body["options"]["num_predict"] = 16 # terse final answer + fast + else: + # Prevent an unconstrained local generation from consuming the whole + # request window. Goal Orchestrator can tune this without weakening the + # shorter classifier/judge budgets. + body["options"]["num_predict"] = max(64, int(os.environ.get("CASAN_MODEL_GENERATE_MAX_TOKENS", "1400"))) data = json.dumps(body).encode() req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"}) t0 = time.time() diff --git a/packages/casan-harness/scripts/bash/model-connections.py b/packages/casan-harness/scripts/bash/model-connections.py index a4590f6..56a0fc0 100644 --- a/packages/casan-harness/scripts/bash/model-connections.py +++ b/packages/casan-harness/scripts/bash/model-connections.py @@ -322,7 +322,7 @@ def runtime_env(provider_id: str, model_id: str) -> int: parsed = urlparse(endpoint) env.update({ "CASAN_OLLAMA_HOST": parsed.netloc, - "OLLAMA_HOST": parsed.netloc, + "OLLAMA_HOST": endpoint, "CASAN_ALLOW_DOCKER_HOST_OLLAMA": "1" if parsed.hostname == "host.docker.internal" else "0", }) print(json.dumps({"success": True, "env": env}, ensure_ascii=False)) diff --git a/packages/casan-harness/scripts/bash/model-digest-check.sh b/packages/casan-harness/scripts/bash/model-digest-check.sh index afb137a..4e9eaac 100644 --- a/packages/casan-harness/scripts/bash/model-digest-check.sh +++ b/packages/casan-harness/scripts/bash/model-digest-check.sh @@ -28,6 +28,10 @@ mkdir -p "$(dirname "$PIN_FILE")" CMD="${1:-verify}" MODEL="${2:-${CASAN_MODEL:-ornith:9b}}" OLLAMA="${OLLAMA_HOST:-127.0.0.1:11434}" +case "$OLLAMA" in + http://*|https://*) OLLAMA_BASE="${OLLAMA%/}" ;; + *) OLLAMA_BASE="http://${OLLAMA%/}" ;; +esac current_digest() { # 1) explicit override (deterministic for CI/tests) — DISABLED in enforced mode. @@ -44,7 +48,7 @@ current_digest() { fi # 2) live Ollama local d - d="$(curl -sf "http://$OLLAMA/api/tags" 2>/dev/null | \ + d="$(curl -sf "$OLLAMA_BASE/api/tags" 2>/dev/null | \ python3 -c "import json,sys m=sys.argv[1] try: d=json.load(sys.stdin) diff --git a/tmp/control-panel-local/auth-bridge/bridge.pid b/tmp/control-panel-local/auth-bridge/bridge.pid new file mode 100644 index 0000000..f7caba5 --- /dev/null +++ b/tmp/control-panel-local/auth-bridge/bridge.pid @@ -0,0 +1 @@ +62231 diff --git a/tmp/control-panel-local/auth-bridge/model-audit.jsonl b/tmp/control-panel-local/auth-bridge/model-audit.jsonl new file mode 100644 index 0000000..129b4a6 --- /dev/null +++ b/tmp/control-panel-local/auth-bridge/model-audit.jsonl @@ -0,0 +1,4 @@ +{"timestamp": "2026-07-10T14:44:35Z", "provider": "codex", "status": "prompt_length_invalid", "prompt_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "prompt_characters": 0, "latency_ms": 0} +{"timestamp": "2026-07-11T03:05:27Z", "provider": "codex", "status": "success", "prompt_hash": "eb0d4dea8f4d9db4e5a23b30cb230e2877e0f10e3b068b6c26e60df4348336bb", "prompt_characters": 1661, "latency_ms": 43253} +{"timestamp": "2026-07-11T03:07:54Z", "provider": "codex", "status": "success", "prompt_hash": "0bd46179eb95de73deb5be96bc4306993e43b7b5ade08af7e806c313ded88ad0", "prompt_characters": 1340, "latency_ms": 47640} +{"timestamp": "2026-07-11T03:15:44Z", "provider": "codex", "status": "success", "prompt_hash": "2c095509fcae5832ca5fc0952f61e234097cbee6905f4ffcdf24e5830bfa6c0d", "prompt_characters": 11606, "latency_ms": 56375} diff --git a/tmp/control-panel-local/auth-bridge/token b/tmp/control-panel-local/auth-bridge/token new file mode 100644 index 0000000..8cabdbb --- /dev/null +++ b/tmp/control-panel-local/auth-bridge/token @@ -0,0 +1 @@ +b39f21f536ddb5ce72aac871e08584044fc2578f02026a79916b92dbd8738e86 diff --git a/tmp/goal-approval-execution-flow.mmd b/tmp/goal-approval-execution-flow.mmd new file mode 100644 index 0000000..0204932 --- /dev/null +++ b/tmp/goal-approval-execution-flow.mmd @@ -0,0 +1,20 @@ +flowchart LR + A["Người dùng gửi Goal"] --> B["H1: Phân loại mục tiêu"] + B --> C["Local + Cloud tạo Execution Plan"] + C --> D["H4: Kiểm tra policy và rủi ro"] + D --> E["Plan ready\nXem file, lệnh, dependency, test"] + E -->|"Approve"| F["Workspace Executor có sandbox"] + E -->|"Reject / Edit"| C + F --> G["Tạo branch/worktree và project"] + G --> H["Chạy test + kiểm tra acceptance"] + H -->|"Pass"| I["Completed"] + H -->|"Fail"| J["Failed + rollback / resume"] + + K["Luồng hiện tại"] -.-> L["Local worker tạo text"] + L -.-> M["Cloud reviewer tạo text"] + M -.-> N["Completed = trả lời xong\nkhông sửa workspace"] + + classDef current fill:#fff7ed,stroke:#f97316,color:#7c2d12; + classDef target fill:#eff6ff,stroke:#2563eb,color:#1e3a8a; + class K,L,M,N current; + class A,B,C,D,E,F,G,H,I,J target; diff --git a/tmp/goal-approval-execution-flow.png b/tmp/goal-approval-execution-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..665e3885b467bf78b8e243ce944d9547bb325c26 GIT binary patch literal 64087 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 RYPoMl?~?IFtoF?Z{}0?U%ccMT literal 0 HcmV?d00001 diff --git a/tmp/goal-approval-execution-flow.svg b/tmp/goal-approval-execution-flow.svg new file mode 100644 index 0000000..4381c41 --- /dev/null +++ b/tmp/goal-approval-execution-flow.svg @@ -0,0 +1 @@ +

Approve

Reject / Edit

Pass

Fail

Người dùng gửi Goal

H1: Phân loại mục tiêu

Local + Cloud tạo Execution Plan

H4: Kiểm tra policy và rủi ro

Plan ready
Xem file, lệnh, dependency, test

Workspace Executor có sandbox

Tạo branch/worktree và project

Chạy test + kiểm tra acceptance

Completed

Failed + rollback / resume

Luồng hiện tại

Local worker tạo text

Cloud reviewer tạo text

Completed = trả lời xong
không sửa workspace

\ No newline at end of file diff --git a/tmp/phase2-blocked/logs/chat/chat-head.txt b/tmp/phase2-blocked/logs/chat/chat-head.txt new file mode 100644 index 0000000..a90b1ca --- /dev/null +++ b/tmp/phase2-blocked/logs/chat/chat-head.txt @@ -0,0 +1 @@ +21e680feef6579237c8a922712245afe8311c59e74d61523f8781535fcc35130 diff --git a/tmp/phase2-blocked/logs/chat/chat-turns.jsonl b/tmp/phase2-blocked/logs/chat/chat-turns.jsonl new file mode 100644 index 0000000..80da25d --- /dev/null +++ b/tmp/phase2-blocked/logs/chat/chat-turns.jsonl @@ -0,0 +1 @@ +{"seq": 1, "timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "chat_id": "phase2-blocked", "turn_id": "turn-38db1352-5ff", "tenant_id": "default", "actor": "phase2-smoke", "mode": "BLOCK", "risk": "high", "decision": "DENIED", "router": {"mode": "BLOCK", "risk": "high", "gates": ["H4_INPUT", "H5_CHAT_AUDIT"], "needs_approval": false, "reason": "blocked_by_rule", "matched_rules": ["ignore previous instructions", "reveal system prompt"], "side_effect_allowed": false, "classified_at": "2026-07-10T14:14:25Z"}, "user_msg_ref": "a20fa0ed6c2e5e629cf8413dfeb67a916825b0e617f0924ef9ea7d4e2eff916d", "safe_preview": "", "answer_ref": "eb8a01730d30c968225db76b066cdf294dd89d0be3ad5f3925b636a8a91ac6b2", "answer_preview": "Denied by Prompt Router: mode=BLOCK reason=blocked_by_rule", "synthesis_mode": "deterministic", "sources": [], "prev_hash": "0000000000000000000000000000000000000000000000000000000000000000", "record_hash": "21e680feef6579237c8a922712245afe8311c59e74d61523f8781535fcc35130"} diff --git a/tmp/phase2-blocked/logs/cost/metrics.jsonl b/tmp/phase2-blocked/logs/cost/metrics.jsonl new file mode 100644 index 0000000..e8e712a --- /dev/null +++ b/tmp/phase2-blocked/logs/cost/metrics.jsonl @@ -0,0 +1 @@ +{"timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "harness": "H6-agentops", "agent": "chat.ask-casan", "step": "ask-casan-readonly", "status": "failed", "exit_code": 2, "latency_ms": 90, "retry_count": 0, "input_tokens": 7, "output_tokens": 6, "total_tokens": 13, "cost_estimate": 0.0, "cost_source": "readonly_word_count", "synthesis_mode": "deterministic", "hallucination_signals": 0, "alerts": [], "input_hash": "a20fa0ed6c2e5e629cf8413dfeb67a916825b0e617f0924ef9ea7d4e2eff916d", "output_hash": "eb8a01730d30c968225db76b066cdf294dd89d0be3ad5f3925b636a8a91ac6b2"} diff --git a/tmp/phase2-blocked/logs/trace-events/38db1352-5ffc-4f37-b785-fd1c61e5a740.jsonl b/tmp/phase2-blocked/logs/trace-events/38db1352-5ffc-4f37-b785-fd1c61e5a740.jsonl new file mode 100644 index 0000000..74cc094 --- /dev/null +++ b/tmp/phase2-blocked/logs/trace-events/38db1352-5ffc-4f37-b785-fd1c61e5a740.jsonl @@ -0,0 +1,7 @@ +{"timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "gate_id": "H1-context", "status": "running", "reason": "Classifying prompt contract", "evidence": {}} +{"timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "gate_id": "H1-context", "status": "blocked", "reason": "blocked_by_rule", "evidence": {"mode": "BLOCK", "risk": "high"}} +{"timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "gate_id": "H5-governance", "status": "running", "reason": "Writing append-only decision audit", "evidence": {}} +{"timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "gate_id": "H5-governance", "status": "pass", "reason": "Decision audit recorded", "evidence": {"decision": "DENIED", "audit_seq": 1, "audit_hash": "21e680feef6579237c8a922712245afe8311c59e74d61523f8781535fcc35130"}} +{"timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "gate_id": "H6-agentops", "status": "running", "reason": "Recording runtime metrics", "evidence": {}} +{"timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "gate_id": "H6-agentops", "status": "error", "reason": "Runtime metrics recorded", "evidence": {"latency_ms": 90, "status": "failed", "synthesis_mode": "deterministic"}} +{"timestamp": "2026-07-10T14:14:25Z", "trace_id": "38db1352-5ffc-4f37-b785-fd1c61e5a740", "gate_id": "H7-orchestration", "status": "blocked", "reason": "Harness stopped with governed outcome", "evidence": {"decision": "DENIED", "certified": false}} diff --git a/tmp/phase2-smoke/logs/audit/security.jsonl b/tmp/phase2-smoke/logs/audit/security.jsonl new file mode 100644 index 0000000..6b11b1f --- /dev/null +++ b/tmp/phase2-smoke/logs/audit/security.jsonl @@ -0,0 +1,2 @@ +{"timestamp":"2026-07-10T14:12:40Z","trace_id":"27486dfc-3f1a-40da-a239-9d3f5ef75496","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"2f2fcdae3b44f5ba01fc31a7ee39b66b0f752ae567f4c84d7964d94c6f2f79f0","output_hash":"2f2fcdae3b44f5ba01fc31a7ee39b66b0f752ae567f4c84d7964d94c6f2f79f0"} +{"timestamp":"2026-07-10T14:12:45Z","trace_id":"dac69766-9c81-49bc-8a11-4659dcf7dfdd","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"c79023a4034447e036f4a5fc1d38323b5d7bebdad17bcf6f8bc990fd33389319","output_hash":"c79023a4034447e036f4a5fc1d38323b5d7bebdad17bcf6f8bc990fd33389319"} diff --git a/tmp/phase2-smoke/logs/chat/chat-head.txt b/tmp/phase2-smoke/logs/chat/chat-head.txt new file mode 100644 index 0000000..0334e4e --- /dev/null +++ b/tmp/phase2-smoke/logs/chat/chat-head.txt @@ -0,0 +1 @@ +4f5f5d965fc38522128bfff17058c9ce133edef023901afb3b75b9036470cc43 diff --git a/tmp/phase2-smoke/logs/chat/chat-turns.jsonl b/tmp/phase2-smoke/logs/chat/chat-turns.jsonl new file mode 100644 index 0000000..f0298ca --- /dev/null +++ b/tmp/phase2-smoke/logs/chat/chat-turns.jsonl @@ -0,0 +1 @@ +{"seq": 1, "timestamp": "2026-07-10T14:12:45Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "chat_id": "phase2-smoke", "turn_id": "turn-d73c565f-19e", "tenant_id": "default", "actor": "phase2-smoke", "mode": "READ_ONLY", "risk": "low", "decision": "ANSWERED", "router": {"mode": "READ_ONLY", "risk": "low", "gates": ["H4_INPUT", "H4_OUTPUT", "H5_CHAT_AUDIT", "H6_TOKEN"], "needs_approval": false, "reason": "read_only_terms", "matched_rules": ["casan"], "side_effect_allowed": false, "classified_at": "2026-07-10T14:12:40Z"}, "user_msg_ref": "2f2fcdae3b44f5ba01fc31a7ee39b66b0f752ae567f4c84d7964d94c6f2f79f0", "safe_preview": "Tóm tắt kiến trúc CASAN", "answer_ref": "c79023a4034447e036f4a5fc1d38323b5d7bebdad17bcf6f8bc990fd33389319", "answer_preview": "Ask CASAN read-only answer (evidence-backed):\n- docs/plans/CASAN_PLAN_01_RESTRUCTURE.md:7 — > `PASS=64 FAIL=0 SKIP=3`. Nền cho Plan-06/12 sẵn sàng (`project-registry.json` có `doma", "synthesis_mode": "deterministic", "sources": [{"path": "docs/plans/CASAN_PLAN_01_RESTRUCTURE.md", "line": 7}, {"path": "docs/plans/CASAN_PLAN_10_TRACEABILITY_EVAL.md", "line": 1}, {"path": "docs/plans/CASAN_PLAN_13_CONTROL_PLANE.md", "line": 4}, {"path": "docs/output/casan/WHY-81-TO-90-DEEP-DIVE.md", "line": 30}, {"path": "docs/output/casan/phase3-push-to-90-plan.md", "line": 1}], "prev_hash": "0000000000000000000000000000000000000000000000000000000000000000", "record_hash": "4f5f5d965fc38522128bfff17058c9ce133edef023901afb3b75b9036470cc43"} diff --git a/tmp/phase2-smoke/logs/cost/metrics.jsonl b/tmp/phase2-smoke/logs/cost/metrics.jsonl new file mode 100644 index 0000000..74486f1 --- /dev/null +++ b/tmp/phase2-smoke/logs/cost/metrics.jsonl @@ -0,0 +1 @@ +{"timestamp": "2026-07-10T14:12:45Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "harness": "H6-agentops", "agent": "chat.ask-casan", "step": "ask-casan-readonly", "status": "success", "exit_code": 0, "latency_ms": 5690, "retry_count": 0, "input_tokens": 5, "output_tokens": 49, "total_tokens": 54, "cost_estimate": 0.0, "cost_source": "readonly_word_count", "synthesis_mode": "deterministic", "hallucination_signals": 0, "alerts": [], "input_hash": "2f2fcdae3b44f5ba01fc31a7ee39b66b0f752ae567f4c84d7964d94c6f2f79f0", "output_hash": "c79023a4034447e036f4a5fc1d38323b5d7bebdad17bcf6f8bc990fd33389319"} diff --git a/tmp/phase2-smoke/logs/trace-events/d73c565f-19ec-4ada-bf69-5fad89cfa282.jsonl b/tmp/phase2-smoke/logs/trace-events/d73c565f-19ec-4ada-bf69-5fad89cfa282.jsonl new file mode 100644 index 0000000..ca62be8 --- /dev/null +++ b/tmp/phase2-smoke/logs/trace-events/d73c565f-19ec-4ada-bf69-5fad89cfa282.jsonl @@ -0,0 +1,14 @@ +{"timestamp": "2026-07-10T14:12:40Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H1-context", "status": "running", "reason": "Classifying prompt contract", "evidence": {}} +{"timestamp": "2026-07-10T14:12:40Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H1-context", "status": "pass", "reason": "read_only_terms", "evidence": {"mode": "READ_ONLY", "risk": "low"}} +{"timestamp": "2026-07-10T14:12:40Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H4-security", "status": "running", "reason": "Scanning input boundary", "evidence": {}} +{"timestamp": "2026-07-10T14:12:44Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H4-security", "status": "running", "reason": "Input passed; output scan pending", "evidence": {"input_scan": "pass"}} +{"timestamp": "2026-07-10T14:12:44Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H2-tool", "status": "running", "reason": "Retrieving allowlisted evidence", "evidence": {}} +{"timestamp": "2026-07-10T14:12:44Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H2-tool", "status": "pass", "reason": "Allowlisted evidence prepared", "evidence": {"source_count": 5, "history_available": false}} +{"timestamp": "2026-07-10T14:12:44Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H3-eval", "status": "running", "reason": "Synthesizing grounded answer", "evidence": {}} +{"timestamp": "2026-07-10T14:12:44Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H3-eval", "status": "pass", "reason": "Grounded synthesis completed", "evidence": {"mode": "deterministic", "provider": "deterministic", "model": "none", "source_count": 5}} +{"timestamp": "2026-07-10T14:12:45Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H4-security", "status": "pass", "reason": "Input and output security scans passed", "evidence": {}} +{"timestamp": "2026-07-10T14:12:45Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H5-governance", "status": "running", "reason": "Writing append-only decision audit", "evidence": {}} +{"timestamp": "2026-07-10T14:12:45Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H5-governance", "status": "pass", "reason": "Decision audit recorded", "evidence": {"decision": "ANSWERED", "audit_seq": 1, "audit_hash": "4f5f5d965fc38522128bfff17058c9ce133edef023901afb3b75b9036470cc43"}} +{"timestamp": "2026-07-10T14:12:45Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H6-agentops", "status": "running", "reason": "Recording runtime metrics", "evidence": {}} +{"timestamp": "2026-07-10T14:12:45Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H6-agentops", "status": "pass", "reason": "Runtime metrics recorded", "evidence": {"latency_ms": 5690, "status": "success", "synthesis_mode": "deterministic"}} +{"timestamp": "2026-07-10T14:12:45Z", "trace_id": "d73c565f-19ec-4ada-bf69-5fad89cfa282", "gate_id": "H7-orchestration", "status": "pass", "reason": "Harness turn completed", "evidence": {"decision": "ANSWERED", "certified": true}} diff --git a/tmp/phase2-smoke/logs/trace/security-27486dfc-3f1a-40da-a239-9d3f5ef75496.json b/tmp/phase2-smoke/logs/trace/security-27486dfc-3f1a-40da-a239-9d3f5ef75496.json new file mode 100644 index 0000000..a0669f0 --- /dev/null +++ b/tmp/phase2-smoke/logs/trace/security-27486dfc-3f1a-40da-a239-9d3f5ef75496.json @@ -0,0 +1,12 @@ +{ + "trace_id": "27486dfc-3f1a-40da-a239-9d3f5ef75496", + "timestamp": "2026-07-10T14:12:40Z", + "harness": "H4-security", + "mode": "input", + "status": "pass", + "action": "allow", + "risk_level": "low", + "matched_rules": [], + "input_hash": "2f2fcdae3b44f5ba01fc31a7ee39b66b0f752ae567f4c84d7964d94c6f2f79f0", + "output_hash": "2f2fcdae3b44f5ba01fc31a7ee39b66b0f752ae567f4c84d7964d94c6f2f79f0" +} diff --git a/tmp/phase2-smoke/logs/trace/security-dac69766-9c81-49bc-8a11-4659dcf7dfdd.json b/tmp/phase2-smoke/logs/trace/security-dac69766-9c81-49bc-8a11-4659dcf7dfdd.json new file mode 100644 index 0000000..cc83319 --- /dev/null +++ b/tmp/phase2-smoke/logs/trace/security-dac69766-9c81-49bc-8a11-4659dcf7dfdd.json @@ -0,0 +1,12 @@ +{ + "trace_id": "dac69766-9c81-49bc-8a11-4659dcf7dfdd", + "timestamp": "2026-07-10T14:12:45Z", + "harness": "H4-security", + "mode": "output", + "status": "pass", + "action": "allow", + "risk_level": "low", + "matched_rules": [], + "input_hash": "c79023a4034447e036f4a5fc1d38323b5d7bebdad17bcf6f8bc990fd33389319", + "output_hash": "c79023a4034447e036f4a5fc1d38323b5d7bebdad17bcf6f8bc990fd33389319" +}