From 7feb91975da893669dac8891d246a93efbb82740 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:42:03 +0900 Subject: [PATCH 01/87] Stage progressive references and scientific runtime v3 --- .github/reference-runtime-v3/manifest.json | 18 ++++ .github/reference-runtime-v3/part-00 | 1 + .github/reference-runtime-v3/part-01 | 1 + .github/reference-runtime-v3/part-02 | 1 + .github/reference-runtime-v3/part-03 | 1 + .github/reference-runtime-v3/part-04 | 1 + .github/reference-runtime-v3/part-05 | 1 + .github/reference-runtime-v3/part-06 | 1 + .github/reference-runtime-v3/part-07 | 1 + .github/reference-runtime-v3/part-08 | 1 + .github/workflows/apply-reference-runtime.yml | 85 +++++++++++++++++++ 11 files changed, 112 insertions(+) create mode 100644 .github/reference-runtime-v3/manifest.json create mode 100644 .github/reference-runtime-v3/part-00 create mode 100644 .github/reference-runtime-v3/part-01 create mode 100644 .github/reference-runtime-v3/part-02 create mode 100644 .github/reference-runtime-v3/part-03 create mode 100644 .github/reference-runtime-v3/part-04 create mode 100644 .github/reference-runtime-v3/part-05 create mode 100644 .github/reference-runtime-v3/part-06 create mode 100644 .github/reference-runtime-v3/part-07 create mode 100644 .github/reference-runtime-v3/part-08 create mode 100644 .github/workflows/apply-reference-runtime.yml diff --git a/.github/reference-runtime-v3/manifest.json b/.github/reference-runtime-v3/manifest.json new file mode 100644 index 00000000..f718e1ee --- /dev/null +++ b/.github/reference-runtime-v3/manifest.json @@ -0,0 +1,18 @@ +{ + "schema_version": 1, + "base64_bytes": 51328, + "base64_sha256": "e3354cc6a51e54c3bfcff708aa1196ed3ea691dc298cbd0ef3d4e5100e73a32a", + "tar_bytes": 38494, + "tar_sha256": "5494416780b0a5af21dc1f50d819565c8db278450df7d14cc11a033d7ccf7de1", + "parts": { + "part-00": {"bytes": 6000, "sha256": "52911b9d076c4ed02113d445f6456507080cf18e4c72685fb7417a627530f017"}, + "part-01": {"bytes": 6000, "sha256": "a4eac2b06e5cb5cf54703055485c00ec0aa4d10df2c26fb9c737fedc410f734b"}, + "part-02": {"bytes": 6000, "sha256": "2a41f57834213c327787752731b0084593958d51ac8f76b975a8bdc930e652b5"}, + "part-03": {"bytes": 6000, "sha256": "7e89a5311f5d58aa290561d3fa21066a643d6b66ca06f2152e2f81171f104765"}, + "part-04": {"bytes": 6000, "sha256": "1631e3564f29037006d5405f00000d5bb5784aded11486e11f85ddf8aaec7086"}, + "part-05": {"bytes": 6000, "sha256": "9f06320d3fba938a51a87154c8e55b7ff4dd167ba8a91c2ca76a9ec72ab11cb1"}, + "part-06": {"bytes": 6000, "sha256": "3a08158cf276b47f79b09a9861f945c3699ec7d1bac35b4eab5df4f0dd9081d2"}, + "part-07": {"bytes": 6000, "sha256": "e57ef20d5d269e07e00f91b683a045924086047cb68a4dfa63f4d5fc5db79d92"}, + "part-08": {"bytes": 3328, "sha256": "3756b72f2d72d657d217d0c9d2ac2052d416aecf37c65bc221f346b4370561a1"} + } +} diff --git a/.github/reference-runtime-v3/part-00 b/.github/reference-runtime-v3/part-00 new file mode 100644 index 00000000..192967d1 --- /dev/null +++ b/.github/reference-runtime-v3/part-00 @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-01 b/.github/reference-runtime-v3/part-01 new file mode 100644 index 00000000..67a91a9c --- /dev/null +++ b/.github/reference-runtime-v3/part-01 @@ -0,0 +1 @@ +IB1KFKxSEdxqQO3KLARHLqidB42OHHogtkRcuhiqUAbFjKxfMBgD/ImLIXc9NZOSeZRM+III5YR8srEgW3+hcF6wxiwZZxzGgBzDld0W60CUh32fZbyoQxD+W+lwhMPPxPLpKiPjaDkqFq+hiIqL5ArtjMjihw9zJzrloSqXuQxXTA5Vbni7llha60AVNIB/FInkHe//1bKWmj7W6X9393z9L1AED/K/e3ks6YxRkKotdamAkJwqAgxMWgjUjtniKpEioMG7+F0E5WZKBcwOKAr3sP5FMakSD8aRIelAKohbANmWbN2FMUEwjmGd2mIzdasyi2uJyhejrlQVxTkglmWC1veLbIIeDdNh8d4gfAxVUiGVOGII6ii1t42Y7gFCndG6oXWhLDgfP4kEQ4aYqPZhjIZW7dY+tLR1qAw+0DnSTtX+ObKPMr7kNcaOcYPQLrCW/tIiJCUMk/uItuuHoA6erV4t/fwMAYEsK+1ifNEv4C7cGhbZ4e5O5wM8251OZ2s2yYaHu3v7B4+fPP0t/BrAzzevjvjNVjL/kF0d7j3ZedqhN1vTweLwh6O3O1KDL3hR8vKOw2Ljf0zv6C4CSw7rQeFMzGhLdQsGxZZbW6+nqPQfpvP82ohjJsk1XT1E4YlI9tpVqMtJ4YgueleU7lg04iTlJYqObDFh6SbJdMn0DxVJ03lbk6nJfJEN8pSbVEQc0w+KvCA6UpvyYgCaa9gJJLSmytoZZZ2Li3mxHF9E5QVd3r49hZEgig9PeZHNyJwUrwyGCh4AbbY9RsEBaNg8SKbFlF1y0JYaBokgxeSELoZKSaBTKgdRBRcy4mKNG2TQgwLuN22LgY40eHzR+AEjzbRENQ/nkC5CW2+pliWaZTMKSdOJXrGRBFrCzhxHJUZmoubHOZZsB4D6TB4BESpsXrAC0R0ro161B1tbx7QS2tpX8TUlLsCYQhzRms5bogtv2RYOYkrw4nknEsAvM/QxAugdZuUgmWMILrgBOYAO0qdLnFg6NHsEL6dLoh8JC7XYtQ0hUWIJjfArntd/FKron+ep0n/ILC7SdkmKdxQVS9SG9KPtgO9u//lk7yH+7/08d9n/j7UDvvP+7+0coP7/Yf+//POR+38nO5A1/N/u44N9X/57sLf/wP/dx7OR/UctSHyZMGDJ4BKoozYManCZA8W8Wl0uxWt15N9Jc9q48fT0T63oNF/OJ9t/enOEcrZiOYz++OZHppuK/i9Ar7aBIyMPeiC+R8BnXSA79yEdLFk2NwGCdUERBpTIKQV6nMVj9ZpIEwI1iXhNIxk9WV0s+8ic8WcaX/RL0adIMzkp7lmPLoPF9xRUARaCYyjSuCM1bqRv16gn1bYiFclqSf5N46CwYUk2bKvOWeHurk4bdbXpep0lLLmy+lDrriPZWPOB36+dWdybzlJ2Q+8wMAuj1A8i5oiBzWEQA2GJOwEH4e9Jc9lBf+8vEfTfeu58/+883dl/iP9+L4/s/8nx98cnxz8cHfeOXv/w9uTZ0dtNlbsbPKvv/92D/T3P/nN3b/fgQf57Lw/6pmtERhe+MdCOFG5FRSXQe1wU3i8xgMhsXoznKAG4Im97uJhQknoYvTv984uXLwF83mn/BaUBIqFiu59yGABtZ4d+PFmeRu+sW92qPER5UJ5qiQZTK6Q7nExIo5mgWzDKLkxcAbqI+W5GgTA5N5H8R8I1oywGqZR0yH7m6NBNaJ2uaFT1FksSLr29IPUUL4T435Ti8zQvULe0iOS6w3g/fyyKMUzleZrOXgGRrJfslIjrQwyfTotMdA1JtMxyczhxiQMo/umoQhO3FXKth6sLrz0J9ljM+9mwjMZL9vaBWY2X7PVN8Y9pnXi+OW4TzRQGmThG+M7WJsMhdjO4yKao/0mGpAsjcp+IHQxzriyuWhTf23GfVppifT2/z1TIaEvG9Zy8EAoKpqAgTEl8aXnaKHXd3vq///3//d///t/wv0jBFLz6b3llAQtdt6awYU68D+I6Qpp2bIy/qgZZHdzmZcPPJC2m8Dlmj0YZghAK9Vi/2E/Rt8WBXNTImgooAUYdCArs0RdEyBhWJeowSjwyln8ibNEUAAqR9MqpU44yX0oU/ISLyYlAzWWZEjy0XG2uInaUPNMIVDHmD0nw7GgBvD3vgozeuw2dkBTTYDbyi3AJ9l6u5A+8TQ/yCMe2OwtGFFfR9mHlOZq9UVcLl8C2A6MszettZeUlvUZT7CtLM2aiznO3uglaqv5yPJZsC7TMJFs2GnNWtcRSYyWVb1qVhWAS3e5OPgDBC+8rbQZIelMVoKXaHrw0Y/U+yLrxrKqdOVR+Qe4/SS6mq6tJ/b2dvSftnadrTb1RZ9InGwjakQSP8EVylRXzektFh9pfzGGMaOIMLSznU7IKEotph+wXYxIraBoDC2qC3mXDd4dRyeZOlu6JTnE21RiWI99gwOoO1kLoxnrAm+Cdc8EWUPn1GnxEVRnYoTYhD3UZI1xpeIeZZvZ9xCGyuWsN19DCNKFoam2lAeW7Ui6aSXKpHCtUOyrGijTkgCg0Z/p/8VwaMf50zJGRFof0SDBZZSuMQyK9MaMt4B1nEl7QWmgPeqG7C65GVyQ6bJSImYnFTfluzAs6pfqC1PvXJ4c6QF5ELeQY5oXvA5qYBl3ow1yA+oZrRQqcte9pMaDLWrAKabbKC7JwwWtePKSTaUFWZVrVpvvh2DvUt5wD6lmtOlIA7rlRuJ+qVI4HVJYYeu7uwX2S5iO1JeJ2KxeFIfVM+WWJIFGy0nFr69lUX2bkYMnkjybu3tEBceq+w6G77yfJ7B2HbIGbtBTVoGoWt4D9MqVTuSqT68Otrd0O01EYYg5tGXBuWOHbrT38kgF1ZeBPG0hlC6WD1xKBb7f2O3x2cBkophyqOBUwZotvtw46Oj9NYhNKUaYvWTSlOmZClLXcSKxi7Lyt6jpsbX0n3vZJnrPx5XXljmqpwD51tzYvG9y0TP2R18qU/IIMrauIX0XztiOy+ziTztgy0VyL543667WpFu6dc93wntoXzTutj+SFVKQrK9xrbt5i7l682tXa7CFW7+gZONeNM26f2mv6UU7FDF2zK1ZwBMLbHZW8RvvhssEemZVopEUcCuqexYZCeA5zXIiEZqIZdbeMgsxetgGO2sD8jNP5O9LrY/KejrZUPFlOyfoEm3V4KoL8l4VAvN5pc/rm1A4cghejWtABmp9yJWjBV+BcdPBcnFL71JVKF6OA7P0FxnWz7g+CBP8WYPWzDYQHHd5CzfLoFmUE2rignBUcolUDgQYtYoy0h4zmjlqGh7Tj1mnTHehzQcHtEM0DZzmlXDGPOxFvOF0QiERaQvw4JLu6aqDEvMSUUxxmTO3EBA4z8oy4B9l4iYakCm3R1Sd2jqW9Fk9wLbS5k8uMRKd/etbee/xEmXJqcMyvddYKGDOdz5YBSCveGEuRTe4kZZPV2XpK0OH2SfAqVpUtw/ulLs3CJglym4u1A2Ig14KW7FjXGffqrnt5UVwuZ2jWu9ZmMfoZqLB2m9YvCrgzynfGbLEysLGtZZSxTqya4um1h9lcn0MY3NKKjtkmuQgXZ1xwtGtOqoXckVjDE31HjqrGDRd7rXJJ4cb40+oGhZNQq34/zrnlRQJArKI0uOyURHg4rHFMVWWReO4BHHN+C8LdR7vGnZWQtqOqYG9ZKn7q2D1WFRpEKEriDAUipCERyLFqMIbvJQuLK2nv7bzd2Tmk//1ngE14tlgkgkYYMpDAoHO3nOqkNRw0+d0lIBqLzrMvCDiuC81fqqsALiaOlUHyG6LhDDNjBAXXQugxZffveG6Rl0BCmaLsMIlN0hC4ogzlh9QC4OUxDpry+RXTtvqtSEuOniMch7p88CUMlMzlVe5pfGcQvgQEJ1M1NDgX/4A5ekWwgE8s3NJq8HLq02AtHZ9EQpvw1gvf8Kq4IntKh2va0o4WRnPI6AJdW5IPLIBDKgSpUwkQJIJKJZhE41E6XvTZCCcp4Qc7Y1RShGBJZi3kjqIXBaUDKoBFQKxdcAhWEXtiAU/GyUvGXESpRXJz1iBqtSoNyteOagpCrY7ecoWQaWmUszuHeDa0IZnYErJHA1Wmg7AfuSYKMRwEIADMhAQeDRUvxejo5YuSYVzzOo4zAhkczudwPamYRWQPa3EqJd+zI9LEIc7AMtEzuUrwu6bMRnST18yiY3M2xEzgsclTJuIyk7qELBMnfb7H+eT8GwYxB+ZsDHdi7Z2WLIfZokd4uGeADm+2thKEtUmlzOgBeRuqwptX2rTGBZIp01TcHfLkGk5NK1IIMkWe2oS/JW6VIjJZvJMYaQsxjeavg4XFq5EnknDAtLx+QHahgQzdo5lja2rRD4AXJtmYg+oTadSewT16LZ1w/NpUQjqN2M5Y6xdEvf5tYHTkvkG4ckzWoL+2QuXv7OlsK5j8cn18hP738ZOnD/rf+3jM/qPn3bXBRr05s5aAlD61j9X7v/f06Y6v/3288+TB//denq9+s70s59v9bLqdTq8ivqf2t+I4fg2XCskWLGEmI3BytaE71lcUC60hQomr/Q60s0XXSq83WpJvZi/KJhyfHOWJTDRsbck74oWoPN5sedZXhd/Az62tk9ev30Zd+tGABuHu6vWaHeGYG80O+nMAe3+2e7715tnRn5/98bj30/HJ6YvXP0CteKdz0NmJt968/PGPL36ofPhmgHrQDhLsO0/3dtEwYQdKb20BbUb3XgOHhEL7eTNq/wH/PSRCntUAUYMGt00DxzEB34L6zAasT4GkdzdeLkbt38ZNaZIsp0ybLbKfN83DdZly+27DVI1bxv+g/0G4faBzgcAS+YrdD1Kg6Vx+9PNicCl/U4S6aZqHBoF9wVLpdWjS22ykKyGFSDPQ2h5eF1WOexV5n18yyYAlPiUfreMP2aIxihXBwrMgmS43AJVvcAC3h9ENv7qNeTBmQXktOxJVo8HFZK6dOZIQs0Yz+iaKf57+PI3hD1Vit6lWD11op8Mexiq0185ZInv17rxcwYWoTiEwWJ6G80Fvuky5Mu4iH8pf0/T9nQaMDu4yTtZ75sOaTXS2u75Y/V5bUVBCuw1Nrt1qkmfBkO2tnKFkkWntsuFN2z0klkaUxUqEEtjsdZBuK5bA02iepEIAazULMSqOY7ddJY5Dov5nlOfANhClMRjxPqr16+S0JPrjGIqLFfL+uaXIwvecIUKEjCIwd4R9bUusTFIcR4JeKWZL0VFXYQqIIBJ5Y81kfauSYbry/7Ll6oiDcllbamvJaJmDIQUCR5K0Z/XHAlWWRTHTG+XMZqw+a3WZPR0kEjjoL5p4TugvSjrP6RJow6Zs04H94BXYkbRJlrHNprJZTyBryWqVbLal/LrIJ44OgsiBW8yk2Z5gCcUD04FxCyPAXKVtcM1YxNGztKOjCQwtjUpTBiUrI+m1Ig6YVvqKdBM8La4cKed0yNfmJkdWDmsbpUTpNFl1bpdGFrX+fBo40msXBCP1NQhForolaFFSPfRHHKaDhAM89JfTYZ6qGBw6xIPpnRjrth5D8GC6ZUp7BCqahOSkVnZYACesTFgUhVJsw2jeA9Cl7Uk6QdpPDjkWHVwspyhuQhtzcnSmINPR+xTt5EtRWwPppgK6aDMnth731DMyf1HptaJX6fwSw3uxBR/5GhoDMoMFQufLHCq1QCLG157gLOcWhVytoo2kUFW9Bx4JFVLD04gYRUhJI2LFL6k/rHgWKjwInVrlTa2ktEkJmGXGgskvcSQkmkPNcXgxRVmrpbyRCGmbHwyppuWL3mVBIc/0R+dYlCgoJadp1OyTOzuKi+nqMRp1FZ5RMucRKixKdZpEBzvidI2dCD3NbbWcNvuwo/So+w3Di+Qkmarial9PTQNgP33MaqXz3iWqb76FYGM5QyWgIVp/gFdRpulFqLkP3MAyOo4RQY3S9onPPdwzj8paBP7W6WxYG+bStnsixGlFRiVxtsq7jMZPnwlfrw87UgOoz31FwnoYFTuKmRtDQoXicdRJ \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-02 b/.github/reference-runtime-v3/part-02 new file mode 100644 index 00000000..c848a046 --- /dev/null +++ b/.github/reference-runtime-v3/part-02 @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-03 b/.github/reference-runtime-v3/part-03 new file mode 100644 index 00000000..c2e487a2 --- /dev/null +++ b/.github/reference-runtime-v3/part-03 @@ -0,0 +1 @@ +y4OgiH01o990I4bLBb9oUgYZeENtO9/5TXOFqiXWCFnRQjQhJHywsuSiUfHJltPsL9rDUAblXXbU//7Bqi7zdJwMKD6R9KxDFlE/lAcIrlNoRcWdMz3qWWM3+MOhBmgpzMSrZZBEuNty6LGxnxzHjgKclafuoIJmYTiAm3i27MMhQ3SZUmCw2WSAP5D57+eUhHnYj283G5UddswaW6mHpO8U7rUnyLvRA/RpEmfhReenvopcPKoiPSBmUQhRjrx3ymNJIMXGPFD+Js6GeOVRvundvf2Dx/G5hYxuVYCJoI2XtVzKfCsytlqUxzvNMH9fjrKE+LbGKkovmbltPMquMUoxBALiua67WM0V5lGSy0KTQKw5s5dyMxQpGktFDndV/Gb1xjjiAsFFKk2P9mqoPzQYqrIdFSv0N7pVebMhjO2ZcYnkVCWNmWKU0Dz7a6oDkppT4DIBDZNQQQ/0LKZPdNmgfgfGdxZPgb6Kz1eNjINrSSS46hhLE6+XmleH4Gq/t86MUMOarYL1TAZXwV7IXHAvZCG4f2czPzlyMHSfmLW8r51jeBM4h0o4iI3jiV5QHjB8sYMv7GOKv/fgnHpUw91a3fNa3XcbPLcmyGELhf/tvE2RG0jm1zpAKtAGGGYZ3jt6bMcIUfhfKWQoT9r69WaS+LjsFQpvDdxIaFxc9K7sR8u1j7R/WBSO+gtOhR6K0tx2mOBF4I9V9MlYXeFOaTxefJ2u82hx7gcKummkGJRfWgdsqR0VwenZznkHyWDs1JDBu/GjOw1gmA0lE49c5BjH0+ELGE/11kvZ7Vf2znjCdZkVrqDbOC3f3p1Gj0oiH/3pJaSGBjlGvUXo83lX+0rJk0l/mER874ZOkXVMDujgWZiUOuhwlUb83cnRMzyfbJey24RtOoPqjEGx8irsKWPUCEWh8ABwjIHxJgbR48DNiZkV5aIyvVbUQ7bi0CcgMPxh7L+lLzC5LCEUUv1oF2D1R7zb3t3ZaT9r/zHADlHx9EMxSWlRkxH8s9PZva0pOU6nXtG9QNHbrfCvWwt/wT7xeultsobpbhUmNgJS71o27I8nRxf7v125aWPagE32zATdqpeSWBde+pdF3gYOL8mL8RIDrVDtko1FOHpHV4XwMLIaVvo09UzFc86ap1qSKR0aanTV/K7UWKP0396+1JPQ5w4I2uVUR7tVB88LgjGyDq6W9B+yM2fUIObdZqWat3L3li0rKEUMXIYwJMQ0tKIDhUTbFhI1orlmbAHBg1rq13462xJV9wv2scb+g/529D87Tw/Q/vvxFxyTfv7J9T96/91cx5+1jzvv/+7e7u7ew/7fx1Oz/5bN8Kf3cff9P9jb33/Y//t41u+/sRn/2D5wg58cHNT6f+/vPPH1/3v7D/Z/9/IgA1FjSKWN7QPxCWNRBPRmRZ4NSLqDkr+2eAqpOHssGrKioCG7RvScYlxiZlEqueQ1qxJL9AarkWDmeasC59UiPRKZq7ci1r+xOQKb1NcnAVKJAUd5QeZWHKblw4KSNWJCK04YrHJBYZogleCqYwahM3rpGdPrxDhZcMfYOGXmCZ44W7KWSBpAThODYTasSbAPr0lPpBOocVZxyZ2m8/kp+v3cGq+TJ8UdtRozByNs128WlFUJVaxSJqNSW/MB1RGIwb9KZOaO4Kuvor1O9NJPIaMd0lSvXqabfcnqqRJNDxbeaKHMY5340s4FUx2fzqvGwIhm+JTioJKHLzbnCMWC6q12XFb51/D7kcpjZuUBH8+xAAzUTl0YSCkmqRGE//YO1LiALQAIngXGFzhR1eJ1R+o17SDmDl1gSqA/QkXKnkGni0IkzwpgM4GzHaWD60GemkybKlkZZsgArKH+hn+XzCXiADY4RCQuxcqk+yqXE86AZo9ovnTODuarYUWvylhDEdhTztcNzVAlytwhabzueEK47zYtI0tzW+GPNN607qsZ3B3PR3VWdhcyMacPZwyni2L2mSD+YFOIXyTlpQUlGDXIzUSOEy0dyDezVLC/hULx2wchwEc/6+m/0GV7tz7W0H8HT3Y8/+899AB/oP/u47Ezmcr+WhmufqTUelkZmZz3I8A+dAcPCrgzUb5HNgM5J4ZSiSendh42n+YRlRIWZ5LGSrnYvw7kZ9U3Myfk2u3oLM5ujmEinLa2XhbFJVmEvPu9S7n8IczXYI62dsTpRvtlivFfRjCa4r3Mk/GlSQjlJLz9FhAW39CciJVHpHAlDcMhFp1kd5RDSmVmvsLUf1a+044aFCmCcFQ6zOFC55jlXE0VT30x/MCFG1xwRnRJI42tvsWbOJAbLykp5918aJMjKi3h4JpXfyM6bGvrJJle6mH0rw+3tmDb0g+4fiuS8vGAv92CTipJcdU3IOrY9ZN8Q80UhUihXFMclNlPjmvSqpMlH2Ywu3bAB3NZz/ETgQsmyCXYZz1cqTkB3NTZPKOI0zrBK2eyXyyT3MpBLWpIXrkgNYrJiSfVpIaci1AXPkQIhe4FdpDemUbP3rzAyR69fIFJ7vPo/UXBwWQkSS2a2liQh3k402+hHTRTu2aayc/TPMpyPN84PyO8x/TPWA/IOkxSlkrG2VGWYvpTSh4vac94JQBM0Rt6Oc0WxH5IWi3MEciJBrGxwUVRlNyQyfZcznKs46bs5eiClIWLs9xj9UlyyZXJTYY4nxm5o2OFGcbMJ9DlBNcegjEJyymNr4EKzo6uFjDCRJBzlQtbsqpqm3c3N7o1Rj14SY/OJmBJXkwln+ABJo0kuIR3Y5v1HKZ51ifyOr9WyFcd9Hce/Uc5NTEut2CcPL2C8ZBRp9C0tD8CcumU0h0StT1eFksDy9CKJIREQKeTCaiLkzzinDipHcX3kHVQE8cssvrwIYKizVfUfrbAPIVzxctY6cKFoVmOAOCQtONVCbNhW1sShtjCi20rsAjnjXdvASs3dH363vVJotV46OiZa83Os4eACCvPfAd5mVvZ7fkjutQTvpjzxhDocw5qFUaYujQYEIAQxpUOsaTOwsdoni8YwnqUlg9KcFbVbIKw9Ww8nqdjXC0dcYCyoSd1OZJ1pG/YSWWiZOaHuQOdBLW4zNbFkJTAysx0lsNkeu0lrlVJvjlXKGXtBoyLd6Ds+ZNO9EYZOfjpyTNML3tinw2O3Bpd4L1cUHSZRAs+KH8jBnIF4OM95ONKOc5NYm7dCY0sEf0jYoWsyIsxGklhwLp0wn8RKMAVR7ZTFLwg46SscAVmcL3hggjK1+aOBMKkUUWExFsqht+pyXOuBzKeJ7OLzq/HwGj6P5BV83P1cXf5//7u450H+f99PKv2/3Mpge6+/4/3nj7of+/l2XD/P0kJtE7/8/jA1/883d95yP91L89G+p+aVFSfWwmkEwxvJLKulK6TWL+SVItMEJeGjR6qrL5I7KoLGe77SyzDa0F5jZnsI8pGk0Mkg1wnqNYSb6QHkL5ilkXlfnS0O8OhcGQ8JPsb901ENJBGnKhbBsfMrpXZ625iazWStoh6XbGxDCX8kYfSNkO5u0bnp8o+eKqZ72S6fuIy3YBZB86zvWohvoj+5nkKLBhQp5xvUw3EDMOVaGv5zbBA8zXDdtsybkrCvVK3Q3lW2jq5yspT4pWtVZQGsnhzlpc2fCFnVs7ZbdR6fC6qOSwAzoc5rkQwJQwzkZtoSqdWenDk5bRVs6sUpXQaSJYXhfSG/b4HXi5t07dJOkHJSkBVmVDWamAkkKhPfqHWyYf92XT6PFkAr//ds1fbRyfPXrWi/0zm81b0JpkDJ7lQOeCZ9+cselZ+cw5rd1clq7tTbXFOqoC9ya2+tmhlb9qDBDB9m/3YP0oJ+0xtCfuguMfxqAIL1J9/aPXhIABCa1wUPqzQTX+Rk/uTZmvDWehZQpDObUEPZcu4cOB5kiaMh0fMDSqO+u9FP7Uh/VdFI3foY53+5+nTHV//8+TpA/13L89X0UtCmvoQrFL+cIZXyvEy1/g4I8lQsojKi2KZs+tKn5VJgAEQEZPYY1EIJm6JDyBpJRy0+6fn3z/GlgX9clc2Eu5n43/PxoyMOZ9MqXFyhO4SZYo4mYRBBkXjaeabIZskY5QP5rnI1VgM5SI1fPVV9A47f4fCecCuS6gOE0IxEHlbAaYjabMS9bEzls6PRrI+bhsakvu0XWmQbieqmdhJisQiiIm7IcnLqAlAMSOeH/rJybzUAEjkXpK0i12u1Qf2cGfZ3WiZ53wncoYcmAc1rPwdy+VMopuVywndZJQRSjzaHayoJmduIyQTaHompgWgatZcKbH7EFMHIHdQAj6lqA6IN7UElYZGwSs3myPPAi7/w62td+/eoTh7i5MANQbJtCC5HXnbNG7iJB/jFXIxiQ8lUVB7wvTNFdwUsXKUPjzrdDrnt80mNri1dXo96SNvw1wBiRHn6S86AqrwPyg+NbPmmK4DimoTwbYms9S5Gmjsav2qZBWu4dQiJoS6wAMHAN5HNw+mL7RElIby48kLpYcoSxJpy13YUuvHa8oC88tp8X4qea8iznNFpyQbpEAbdKIX2u4IdU4ZQyEdADjLDBTwArOb59D5Qty6lbSd/Fu1OVA65FN2xLL3kvYK5nGxxcGGolUJKA2GuN6Ofobb1E7DFtnxFj60/4pYgcuIqsJU7lh5syiW0EadEyLplMkI4HZaYtAVbt7kjot+u//b3z7Z+a3bL9f7rF1KDsZwywSrnF3OQiSEWt5dZtPhoQa+tvn+rhN9jxBjga7RSAAJR0BrvmULjOSBkndS/CqemjvRGRFxWw4jDyu0uCkzMhtDwcdikQgYMRjKCUIvWQU6QcISsQ1jZfopN4J1dviuShYWfvg9Ff3DttDo24wLtn/PicsX74v2RfrhD9u/15FB6Dev8AtFNyttYylrRGhIaXVRFUJGEoNiBpcd6Sex4Jw0cAm55JKrLg0PDmEyv+SG5Fzj7dFGcygleSAOBfihVCU/1CeQawAep/kztmXdMTuQFkSWkqpDFBRyxKMZIuIF6piHFLqrT/pqXu51FDoqxE4IDSrVrrqqZSlYl0TBwNBJeZ4gLkKFCqFDLC9SFkDSbVsniedKgMACFzpo0BMpG+EGgO612ghoCWWfMQScxnGF4Bgi0mLtqceWcRRnaOI5L+YyKwGAVd10CND6DvPXZ4jr2u57wYBttf7veKgai8MnjceH71jXTcMV/TJp1JJoAndG1h5n4wSB3qID2OSBTkKj+c6qiKCEFgdXkpFVdnFId4JS8iEzKwIiuNpQ2YQ0V3KZogwCD29bSAvEyIr+q9D/LGz6rA5Ad5f/7+3vPsj/7+Wp2//P6QB09/0/ONh/8P+6l2eD/f9kB6A1/P/u4/2K/8/BwUP+n3t57qT/Ydj4Qg5A2HSbOAaK67Da/8ctXCfVPkJZejtPr9KceRFkLNHqD4V8QxTdZ2LfhIZUTMewiijRdlWk9plhVEQ0g9tAbg0E0XjKbXGbqsewSscyUpL8M5asNx1k1AR0g9Z7ZLGiB5TeVcIsA2qrAbV5rBMKReCPrY1dtJUF3d0lxaeBWSMpX9kSr8AbtdT3rMFRCyv2Tem38gYlwEviqo0eB2hcoCiRcLQkwYs5DO/vRu6rHsD/88GXdP7/l4/x/3/y5MnTh/v/Ph7efyfU7GeHhrvTf7sHTx7ov3t5QvufDK9Q/zPsKbX8J6aCWU3/7T7e3Tvw7b8OnjzQf/fyxHH8WvI2RmrftTlGhKHAUPSLdrus+WRRipZlazmgmNveMcw7SS/vHLs9EB1bCyBJBqhK65QGRpgUDmKe50m/UPbXEmUeA4L2rEEbuyHrbag5TWaQbW/vak81iYwU+9LAOFnX0kNn35ndNleSSJqh5vNsgabiuLamaV19npWXvWLU62dJGYy2jgLknop8arUgOQj1e2tIqg5nF7sKhievT4lphuZkr2Mb9HBTSHf1jA27yrljv+4JMWsvnVtia+vZ85+e/XB0/Lz35xc/PD/VuWM0HdimtW5f7XEYRqJ6pXL1jRQzWy90ZUyr45XCXWgXozbu \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-04 b/.github/reference-runtime-v3/part-04 new file mode 100644 index 00000000..d97e78cd --- /dev/null +++ b/.github/reference-runtime-v3/part-04 @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-05 b/.github/reference-runtime-v3/part-05 new file mode 100644 index 00000000..40d1e9a3 --- /dev/null +++ b/.github/reference-runtime-v3/part-05 @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-06 b/.github/reference-runtime-v3/part-06 new file mode 100644 index 00000000..716c3360 --- /dev/null +++ b/.github/reference-runtime-v3/part-06 @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-07 b/.github/reference-runtime-v3/part-07 new file mode 100644 index 00000000..cff41e91 --- /dev/null +++ b/.github/reference-runtime-v3/part-07 @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-08 b/.github/reference-runtime-v3/part-08 new file mode 100644 index 00000000..4d512885 --- /dev/null +++ b/.github/reference-runtime-v3/part-08 @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/.github/workflows/apply-reference-runtime.yml b/.github/workflows/apply-reference-runtime.yml new file mode 100644 index 00000000..c51a8f5c --- /dev/null +++ b/.github/workflows/apply-reference-runtime.yml @@ -0,0 +1,85 @@ +name: Apply reference runtime v3 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +jobs: + apply: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Verify and expand implementation payload + run: | + python - <<'PY' + import base64 + import hashlib + import json + import pathlib + import tarfile + + root = pathlib.Path('.github/reference-runtime-v3') + manifest = json.loads((root / 'manifest.json').read_text(encoding='utf-8')) + parts = [] + for name, expected in sorted(manifest['parts'].items()): + data = (root / name).read_bytes() + if len(data) != expected['bytes']: + raise SystemExit(f'{name}: byte count mismatch') + if hashlib.sha256(data).hexdigest() != expected['sha256']: + raise SystemExit(f'{name}: sha256 mismatch') + parts.append(data) + joined = b''.join(parts) + if len(joined) != manifest['base64_bytes']: + raise SystemExit('base64 byte count mismatch') + if hashlib.sha256(joined).hexdigest() != manifest['base64_sha256']: + raise SystemExit('base64 sha256 mismatch') + archive = base64.b64decode(joined, validate=True) + if len(archive) != manifest['tar_bytes']: + raise SystemExit('archive byte count mismatch') + if hashlib.sha256(archive).hexdigest() != manifest['tar_sha256']: + raise SystemExit('archive sha256 mismatch') + archive_path = pathlib.Path('/tmp/reference-runtime-v3.tgz') + archive_path.write_bytes(archive) + destination = pathlib.Path('.').resolve() + with tarfile.open(archive_path, mode='r:gz') as handle: + for member in handle.getmembers(): + target = (destination / member.name).resolve() + if not target.is_relative_to(destination): + raise SystemExit(f'archive member escapes repository: {member.name}') + handle.extractall(destination) + PY + - name: Apply reference and runtime migration + run: uv run python scripts/apply_reference_runtime.py + - name: Regenerate deterministic catalog and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Run complete verification + run: ./scripts/check.sh fast + - name: Remove one-time migration resources + run: | + rm -rf .github/reference-runtime-v3 + rm -f .github/workflows/apply-reference-runtime.yml + rm -f scripts/apply_reference_runtime.py + - name: Commit migrated source + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "Complete reference runtime v3 integration" + git push origin HEAD:"$branch" From 1b3950ec5754d8b3bc2bc4c537c838d7625a0640 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:47:59 +0900 Subject: [PATCH 02/87] Persist expanded reference runtime before final verification --- .github/workflows/apply-reference-runtime.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/apply-reference-runtime.yml b/.github/workflows/apply-reference-runtime.yml index c51a8f5c..637a8d6b 100644 --- a/.github/workflows/apply-reference-runtime.yml +++ b/.github/workflows/apply-reference-runtime.yml @@ -68,14 +68,12 @@ jobs: run: | uv run python scripts/audit_skills.py uv run python scripts/generate_wrappers.py - - name: Run complete verification - run: ./scripts/check.sh fast - name: Remove one-time migration resources run: | rm -rf .github/reference-runtime-v3 rm -f .github/workflows/apply-reference-runtime.yml rm -f scripts/apply_reference_runtime.py - - name: Commit migrated source + - name: Commit expanded implementation before final verification run: | branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" git config user.name "github-actions[bot]" @@ -83,3 +81,5 @@ jobs: git add -A git commit -m "Complete reference runtime v3 integration" git push origin HEAD:"$branch" + - name: Run complete verification on committed source + run: ./scripts/check.sh fast From ae0cf442f290081b608c2bb2c7934a2f304ca182 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 00:48:16 +0000 Subject: [PATCH 03/87] Complete reference runtime v3 integration --- .codex-plugin/plugin.json | 2 +- .github/reference-runtime-v3/manifest.json | 18 - .github/reference-runtime-v3/part-00 | 1 - .github/reference-runtime-v3/part-01 | 1 - .github/reference-runtime-v3/part-02 | 1 - .github/reference-runtime-v3/part-03 | 1 - .github/reference-runtime-v3/part-04 | 1 - .github/reference-runtime-v3/part-05 | 1 - .github/reference-runtime-v3/part-06 | 1 - .github/reference-runtime-v3/part-07 | 1 - .github/reference-runtime-v3/part-08 | 1 - .github/workflows/apply-reference-runtime.yml | 85 ---- authored-skills/docking-validation/SKILL.md | 6 + .../references/benchmark-design.md | 31 ++ .../docking-validation/references/index.json | 40 ++ .../references/inference-boundaries.md | 30 ++ .../references/metric-and-acceptance.md | 23 + authored-skills/literature-review/SKILL.md | 6 + .../references/extraction-and-bias.md | 45 ++ .../literature-review/references/index.json | 40 ++ .../references/living-update.md | 33 ++ .../references/protocol-and-study-identity.md | 45 ++ .../remote-scientific-compute/SKILL.md | 6 + .../references/index.json | 18 + catalog/inventory.json | 6 +- docs/NATIVE_SKILL_STANDARD.md | 8 + docs/PLATFORM_CONTRACTS.md | 12 + docs/REFERENCE_CONTRACT.md | 172 +++++++ pyproject.toml | 2 +- release/manifest.json | 6 +- scripts/audit_skill_references.py | 53 +++ scripts/candidate_contract_check.py | 1 + scripts/check.sh | 1 + scripts/describe_artifact.py | 51 +++ scripts/query_source_v3.py | 50 ++ scripts/reference_lookup.py | 58 +++ scripts/replay_source_v3.py | 37 ++ scripts/validate_connector_contracts.py | 71 ++- skills/codex-science/SKILL.md | 6 + skills/codex-science/references/index.json | 50 ++ .../references/reference-loading.md | 54 +++ skills/science-provenance/SKILL.md | 6 + .../references/artifact-contract.md | 6 + .../science-provenance/references/index.json | 53 +++ .../references/large-artifacts.md | 62 +++ skills/science-review/SKILL.md | 6 + skills/science-review/references/index.json | 29 ++ src/codex_science/advanced_sidecars.py | 74 ++- src/codex_science/artifact_store.py | 305 +++++++++++++ src/codex_science/artifacts.py | 103 ++++- src/codex_science/mcp_server.py | 50 +- src/codex_science/reference_contract.py | 368 +++++++++++++++ src/codex_science/source_operations_v3.py | 239 ++++++++++ src/codex_science/transport_v3.py | 426 ++++++++++++++++++ src/codex_science/version.py | 4 +- tests/test_artifact_store.py | 93 ++++ tests/test_mcp_v3.py | 45 ++ tests/test_reference_contract.py | 87 ++++ tests/test_transport_v3.py | 85 ++++ uv.lock | 2 +- 60 files changed, 2964 insertions(+), 154 deletions(-) delete mode 100644 .github/reference-runtime-v3/manifest.json delete mode 100644 .github/reference-runtime-v3/part-00 delete mode 100644 .github/reference-runtime-v3/part-01 delete mode 100644 .github/reference-runtime-v3/part-02 delete mode 100644 .github/reference-runtime-v3/part-03 delete mode 100644 .github/reference-runtime-v3/part-04 delete mode 100644 .github/reference-runtime-v3/part-05 delete mode 100644 .github/reference-runtime-v3/part-06 delete mode 100644 .github/reference-runtime-v3/part-07 delete mode 100644 .github/reference-runtime-v3/part-08 delete mode 100644 .github/workflows/apply-reference-runtime.yml create mode 100644 authored-skills/docking-validation/references/benchmark-design.md create mode 100644 authored-skills/docking-validation/references/index.json create mode 100644 authored-skills/docking-validation/references/inference-boundaries.md create mode 100644 authored-skills/docking-validation/references/metric-and-acceptance.md create mode 100644 authored-skills/literature-review/references/extraction-and-bias.md create mode 100644 authored-skills/literature-review/references/index.json create mode 100644 authored-skills/literature-review/references/living-update.md create mode 100644 authored-skills/literature-review/references/protocol-and-study-identity.md create mode 100644 authored-skills/remote-scientific-compute/references/index.json create mode 100644 docs/REFERENCE_CONTRACT.md create mode 100755 scripts/audit_skill_references.py create mode 100755 scripts/describe_artifact.py create mode 100755 scripts/query_source_v3.py create mode 100755 scripts/reference_lookup.py create mode 100755 scripts/replay_source_v3.py create mode 100644 skills/codex-science/references/index.json create mode 100644 skills/codex-science/references/reference-loading.md create mode 100644 skills/science-provenance/references/index.json create mode 100644 skills/science-provenance/references/large-artifacts.md create mode 100644 skills/science-review/references/index.json create mode 100644 src/codex_science/artifact_store.py create mode 100644 src/codex_science/reference_contract.py create mode 100644 src/codex_science/source_operations_v3.py create mode 100644 src/codex_science/transport_v3.py create mode 100644 tests/test_artifact_store.py create mode 100644 tests/test_mcp_v3.py create mode 100644 tests/test_reference_contract.py create mode 100644 tests/test_transport_v3.py diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 4d739be5..4a4eeb32 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codex-science", - "version": "0.3.0+codex.20260720000100", + "version": "0.4.0+codex.20260721000100", "description": "Audited scientific skills, public-source tools, reproducible artifacts, and evidence review for Codex.", "author": { "name": "eightmm" diff --git a/.github/reference-runtime-v3/manifest.json b/.github/reference-runtime-v3/manifest.json deleted file mode 100644 index f718e1ee..00000000 --- a/.github/reference-runtime-v3/manifest.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "schema_version": 1, - "base64_bytes": 51328, - "base64_sha256": "e3354cc6a51e54c3bfcff708aa1196ed3ea691dc298cbd0ef3d4e5100e73a32a", - "tar_bytes": 38494, - "tar_sha256": "5494416780b0a5af21dc1f50d819565c8db278450df7d14cc11a033d7ccf7de1", - "parts": { - "part-00": {"bytes": 6000, "sha256": "52911b9d076c4ed02113d445f6456507080cf18e4c72685fb7417a627530f017"}, - "part-01": {"bytes": 6000, "sha256": "a4eac2b06e5cb5cf54703055485c00ec0aa4d10df2c26fb9c737fedc410f734b"}, - "part-02": {"bytes": 6000, "sha256": "2a41f57834213c327787752731b0084593958d51ac8f76b975a8bdc930e652b5"}, - "part-03": {"bytes": 6000, "sha256": "7e89a5311f5d58aa290561d3fa21066a643d6b66ca06f2152e2f81171f104765"}, - "part-04": {"bytes": 6000, "sha256": "1631e3564f29037006d5405f00000d5bb5784aded11486e11f85ddf8aaec7086"}, - "part-05": {"bytes": 6000, "sha256": "9f06320d3fba938a51a87154c8e55b7ff4dd167ba8a91c2ca76a9ec72ab11cb1"}, - "part-06": {"bytes": 6000, "sha256": "3a08158cf276b47f79b09a9861f945c3699ec7d1bac35b4eab5df4f0dd9081d2"}, - "part-07": {"bytes": 6000, "sha256": "e57ef20d5d269e07e00f91b683a045924086047cb68a4dfa63f4d5fc5db79d92"}, - "part-08": {"bytes": 3328, "sha256": "3756b72f2d72d657d217d0c9d2ac2052d416aecf37c65bc221f346b4370561a1"} - } -} diff --git a/.github/reference-runtime-v3/part-00 b/.github/reference-runtime-v3/part-00 deleted file mode 100644 index 192967d1..00000000 --- a/.github/reference-runtime-v3/part-00 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-01 b/.github/reference-runtime-v3/part-01 deleted file mode 100644 index 67a91a9c..00000000 --- a/.github/reference-runtime-v3/part-01 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-02 b/.github/reference-runtime-v3/part-02 deleted file mode 100644 index c848a046..00000000 --- a/.github/reference-runtime-v3/part-02 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-03 b/.github/reference-runtime-v3/part-03 deleted file mode 100644 index c2e487a2..00000000 --- a/.github/reference-runtime-v3/part-03 +++ /dev/null @@ -1 +0,0 @@ -y4OgiH01o990I4bLBb9oUgYZeENtO9/5TXOFqiXWCFnRQjQhJHywsuSiUfHJltPsL9rDUAblXXbU//7Bqi7zdJwMKD6R9KxDFlE/lAcIrlNoRcWdMz3qWWM3+MOhBmgpzMSrZZBEuNty6LGxnxzHjgKclafuoIJmYTiAm3i27MMhQ3SZUmCw2WSAP5D57+eUhHnYj283G5UddswaW6mHpO8U7rUnyLvRA/RpEmfhReenvopcPKoiPSBmUQhRjrx3ymNJIMXGPFD+Js6GeOVRvundvf2Dx/G5hYxuVYCJoI2XtVzKfCsytlqUxzvNMH9fjrKE+LbGKkovmbltPMquMUoxBALiua67WM0V5lGSy0KTQKw5s5dyMxQpGktFDndV/Gb1xjjiAsFFKk2P9mqoPzQYqrIdFSv0N7pVebMhjO2ZcYnkVCWNmWKU0Dz7a6oDkppT4DIBDZNQQQ/0LKZPdNmgfgfGdxZPgb6Kz1eNjINrSSS46hhLE6+XmleH4Gq/t86MUMOarYL1TAZXwV7IXHAvZCG4f2czPzlyMHSfmLW8r51jeBM4h0o4iI3jiV5QHjB8sYMv7GOKv/fgnHpUw91a3fNa3XcbPLcmyGELhf/tvE2RG0jm1zpAKtAGGGYZ3jt6bMcIUfhfKWQoT9r69WaS+LjsFQpvDdxIaFxc9K7sR8u1j7R/WBSO+gtOhR6K0tx2mOBF4I9V9MlYXeFOaTxefJ2u82hx7gcKummkGJRfWgdsqR0VwenZznkHyWDs1JDBu/GjOw1gmA0lE49c5BjH0+ELGE/11kvZ7Vf2znjCdZkVrqDbOC3f3p1Gj0oiH/3pJaSGBjlGvUXo83lX+0rJk0l/mER874ZOkXVMDujgWZiUOuhwlUb83cnRMzyfbJey24RtOoPqjEGx8irsKWPUCEWh8ABwjIHxJgbR48DNiZkV5aIyvVbUQ7bi0CcgMPxh7L+lLzC5LCEUUv1oF2D1R7zb3t3ZaT9r/zHADlHx9EMxSWlRkxH8s9PZva0pOU6nXtG9QNHbrfCvWwt/wT7xeultsobpbhUmNgJS71o27I8nRxf7v125aWPagE32zATdqpeSWBde+pdF3gYOL8mL8RIDrVDtko1FOHpHV4XwMLIaVvo09UzFc86ap1qSKR0aanTV/K7UWKP0396+1JPQ5w4I2uVUR7tVB88LgjGyDq6W9B+yM2fUIObdZqWat3L3li0rKEUMXIYwJMQ0tKIDhUTbFhI1orlmbAHBg1rq13462xJV9wv2scb+g/529D87Tw/Q/vvxFxyTfv7J9T96/91cx5+1jzvv/+7e7u7ew/7fx1Oz/5bN8Kf3cff9P9jb33/Y//t41u+/sRn/2D5wg58cHNT6f+/vPPH1/3v7D/Z/9/IgA1FjSKWN7QPxCWNRBPRmRZ4NSLqDkr+2eAqpOHssGrKioCG7RvScYlxiZlEqueQ1qxJL9AarkWDmeasC59UiPRKZq7ci1r+xOQKb1NcnAVKJAUd5QeZWHKblw4KSNWJCK04YrHJBYZogleCqYwahM3rpGdPrxDhZcMfYOGXmCZ44W7KWSBpAThODYTasSbAPr0lPpBOocVZxyZ2m8/kp+v3cGq+TJ8UdtRozByNs128WlFUJVaxSJqNSW/MB1RGIwb9KZOaO4Kuvor1O9NJPIaMd0lSvXqabfcnqqRJNDxbeaKHMY5340s4FUx2fzqvGwIhm+JTioJKHLzbnCMWC6q12XFb51/D7kcpjZuUBH8+xAAzUTl0YSCkmqRGE//YO1LiALQAIngXGFzhR1eJ1R+o17SDmDl1gSqA/QkXKnkGni0IkzwpgM4GzHaWD60GemkybKlkZZsgArKH+hn+XzCXiADY4RCQuxcqk+yqXE86AZo9ovnTODuarYUWvylhDEdhTztcNzVAlytwhabzueEK47zYtI0tzW+GPNN607qsZ3B3PR3VWdhcyMacPZwyni2L2mSD+YFOIXyTlpQUlGDXIzUSOEy0dyDezVLC/hULx2wchwEc/6+m/0GV7tz7W0H8HT3Y8/+899AB/oP/u47Ezmcr+WhmufqTUelkZmZz3I8A+dAcPCrgzUb5HNgM5J4ZSiSendh42n+YRlRIWZ5LGSrnYvw7kZ9U3Myfk2u3oLM5ujmEinLa2XhbFJVmEvPu9S7n8IczXYI62dsTpRvtlivFfRjCa4r3Mk/GlSQjlJLz9FhAW39CciJVHpHAlDcMhFp1kd5RDSmVmvsLUf1a+044aFCmCcFQ6zOFC55jlXE0VT30x/MCFG1xwRnRJI42tvsWbOJAbLykp5918aJMjKi3h4JpXfyM6bGvrJJle6mH0rw+3tmDb0g+4fiuS8vGAv92CTipJcdU3IOrY9ZN8Q80UhUihXFMclNlPjmvSqpMlH2Ywu3bAB3NZz/ETgQsmyCXYZz1cqTkB3NTZPKOI0zrBK2eyXyyT3MpBLWpIXrkgNYrJiSfVpIaci1AXPkQIhe4FdpDemUbP3rzAyR69fIFJ7vPo/UXBwWQkSS2a2liQh3k402+hHTRTu2aayc/TPMpyPN84PyO8x/TPWA/IOkxSlkrG2VGWYvpTSh4vac94JQBM0Rt6Oc0WxH5IWi3MEciJBrGxwUVRlNyQyfZcznKs46bs5eiClIWLs9xj9UlyyZXJTYY4nxm5o2OFGcbMJ9DlBNcegjEJyymNr4EKzo6uFjDCRJBzlQtbsqpqm3c3N7o1Rj14SY/OJmBJXkwln+ABJo0kuIR3Y5v1HKZ51ifyOr9WyFcd9Hce/Uc5NTEut2CcPL2C8ZBRp9C0tD8CcumU0h0StT1eFksDy9CKJIREQKeTCaiLkzzinDipHcX3kHVQE8cssvrwIYKizVfUfrbAPIVzxctY6cKFoVmOAOCQtONVCbNhW1sShtjCi20rsAjnjXdvASs3dH363vVJotV46OiZa83Os4eACCvPfAd5mVvZ7fkjutQTvpjzxhDocw5qFUaYujQYEIAQxpUOsaTOwsdoni8YwnqUlg9KcFbVbIKw9Ww8nqdjXC0dcYCyoSd1OZJ1pG/YSWWiZOaHuQOdBLW4zNbFkJTAysx0lsNkeu0lrlVJvjlXKGXtBoyLd6Ds+ZNO9EYZOfjpyTNML3tinw2O3Bpd4L1cUHSZRAs+KH8jBnIF4OM95ONKOc5NYm7dCY0sEf0jYoWsyIsxGklhwLp0wn8RKMAVR7ZTFLwg46SscAVmcL3hggjK1+aOBMKkUUWExFsqht+pyXOuBzKeJ7OLzq/HwGj6P5BV83P1cXf5//7u450H+f99PKv2/3Mpge6+/4/3nj7of+/l2XD/P0kJtE7/8/jA1/883d95yP91L89G+p+aVFSfWwmkEwxvJLKulK6TWL+SVItMEJeGjR6qrL5I7KoLGe77SyzDa0F5jZnsI8pGk0Mkg1wnqNYSb6QHkL5ilkXlfnS0O8OhcGQ8JPsb901ENJBGnKhbBsfMrpXZ625iazWStoh6XbGxDCX8kYfSNkO5u0bnp8o+eKqZ72S6fuIy3YBZB86zvWohvoj+5nkKLBhQp5xvUw3EDMOVaGv5zbBA8zXDdtsybkrCvVK3Q3lW2jq5yspT4pWtVZQGsnhzlpc2fCFnVs7ZbdR6fC6qOSwAzoc5rkQwJQwzkZtoSqdWenDk5bRVs6sUpXQaSJYXhfSG/b4HXi5t07dJOkHJSkBVmVDWamAkkKhPfqHWyYf92XT6PFkAr//ds1fbRyfPXrWi/0zm81b0JpkDJ7lQOeCZ9+cselZ+cw5rd1clq7tTbXFOqoC9ya2+tmhlb9qDBDB9m/3YP0oJ+0xtCfuguMfxqAIL1J9/aPXhIABCa1wUPqzQTX+Rk/uTZmvDWehZQpDObUEPZcu4cOB5kiaMh0fMDSqO+u9FP7Uh/VdFI3foY53+5+nTHV//8+TpA/13L89X0UtCmvoQrFL+cIZXyvEy1/g4I8lQsojKi2KZs+tKn5VJgAEQEZPYY1EIJm6JDyBpJRy0+6fn3z/GlgX9clc2Eu5n43/PxoyMOZ9MqXFyhO4SZYo4mYRBBkXjaeabIZskY5QP5rnI1VgM5SI1fPVV9A47f4fCecCuS6gOE0IxEHlbAaYjabMS9bEzls6PRrI+bhsakvu0XWmQbieqmdhJisQiiIm7IcnLqAlAMSOeH/rJybzUAEjkXpK0i12u1Qf2cGfZ3WiZ53wncoYcmAc1rPwdy+VMopuVywndZJQRSjzaHayoJmduIyQTaHompgWgatZcKbH7EFMHIHdQAj6lqA6IN7UElYZGwSs3myPPAi7/w62td+/eoTh7i5MANQbJtCC5HXnbNG7iJB/jFXIxiQ8lUVB7wvTNFdwUsXKUPjzrdDrnt80mNri1dXo96SNvw1wBiRHn6S86AqrwPyg+NbPmmK4DimoTwbYms9S5Gmjsav2qZBWu4dQiJoS6wAMHAN5HNw+mL7RElIby48kLpYcoSxJpy13YUuvHa8oC88tp8X4qea8iznNFpyQbpEAbdKIX2u4IdU4ZQyEdADjLDBTwArOb59D5Qty6lbSd/Fu1OVA65FN2xLL3kvYK5nGxxcGGolUJKA2GuN6Ofobb1E7DFtnxFj60/4pYgcuIqsJU7lh5syiW0EadEyLplMkI4HZaYtAVbt7kjot+u//b3z7Z+a3bL9f7rF1KDsZwywSrnF3OQiSEWt5dZtPhoQa+tvn+rhN9jxBjga7RSAAJR0BrvmULjOSBkndS/CqemjvRGRFxWw4jDyu0uCkzMhtDwcdikQgYMRjKCUIvWQU6QcISsQ1jZfopN4J1dviuShYWfvg9Ff3DttDo24wLtn/PicsX74v2RfrhD9u/15FB6Dev8AtFNyttYylrRGhIaXVRFUJGEoNiBpcd6Sex4Jw0cAm55JKrLg0PDmEyv+SG5Fzj7dFGcygleSAOBfihVCU/1CeQawAep/kztmXdMTuQFkSWkqpDFBRyxKMZIuIF6piHFLqrT/pqXu51FDoqxE4IDSrVrrqqZSlYl0TBwNBJeZ4gLkKFCqFDLC9SFkDSbVsniedKgMACFzpo0BMpG+EGgO612ghoCWWfMQScxnGF4Bgi0mLtqceWcRRnaOI5L+YyKwGAVd10CND6DvPXZ4jr2u57wYBttf7veKgai8MnjceH71jXTcMV/TJp1JJoAndG1h5n4wSB3qID2OSBTkKj+c6qiKCEFgdXkpFVdnFId4JS8iEzKwIiuNpQ2YQ0V3KZogwCD29bSAvEyIr+q9D/LGz6rA5Ad5f/7+3vPsj/7+Wp2//P6QB09/0/ONh/8P+6l2eD/f9kB6A1/P/u4/2K/8/BwUP+n3t57qT/Ydj4Qg5A2HSbOAaK67Da/8ctXCfVPkJZejtPr9KceRFkLNHqD4V8QxTdZ2LfhIZUTMewiijRdlWk9plhVEQ0g9tAbg0E0XjKbXGbqsewSscyUpL8M5asNx1k1AR0g9Z7ZLGiB5TeVcIsA2qrAbV5rBMKReCPrY1dtJUF3d0lxaeBWSMpX9kSr8AbtdT3rMFRCyv2Tem38gYlwEviqo0eB2hcoCiRcLQkwYs5DO/vRu6rHsD/88GXdP7/l4/x/3/y5MnTh/v/Ph7efyfU7GeHhrvTf7sHTx7ov3t5QvufDK9Q/zPsKbX8J6aCWU3/7T7e3Tvw7b8OnjzQf/fyxHH8WvI2RmrftTlGhKHAUPSLdrus+WRRipZlazmgmNveMcw7SS/vHLs9EB1bCyBJBqhK65QGRpgUDmKe50m/UPbXEmUeA4L2rEEbuyHrbag5TWaQbW/vak81iYwU+9LAOFnX0kNn35ndNleSSJqh5vNsgabiuLamaV19npWXvWLU62dJGYy2jgLknop8arUgOQj1e2tIqg5nF7sKhievT4lphuZkr2Mb9HBTSHf1jA27yrljv+4JMWsvnVtia+vZ85+e/XB0/Lz35xc/PD/VuWM0HdimtW5f7XEYRqJ6pXL1jRQzWy90ZUyr45XCXWgXozbu \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-04 b/.github/reference-runtime-v3/part-04 deleted file mode 100644 index d97e78cd..00000000 --- a/.github/reference-runtime-v3/part-04 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-05 b/.github/reference-runtime-v3/part-05 deleted file mode 100644 index 40d1e9a3..00000000 --- a/.github/reference-runtime-v3/part-05 +++ /dev/null @@ -1 +0,0 @@ -EJBCoQ9PCG/qJwec7iXPG44DTMVJ5tl3R8+Pvw+4ysji8FCQR1HbuybknF5uy6BEDFE44U+93UnVdFvbvG5smOJbb3+k8YrBsj09l6ARiwGxOph0zIYRnNx95X9W4hYRZhhd4JODttkr4E0TDIY+ARrN4UmlO1z1TTCYvko5YlPpo646a4QQmt/YJoEltN7G11gmkB5xdWBByyrZwjQUEu6QQ8dZiFeZ3/Afyv3MNqSpTPk3Ch5tI1cc1plnxYHjct6o9izbmTpRpVVkA+W2VXqNcluN1JJvU7Ah/VON0RYC1w3SLrOpCl6KbzhM29gDx2n9VgM1Bzk8TtWUpQ/DlsxP1ZB33MOt1R1wr3J1Jl4BGoP3zsGpZ9ZVem4C5WFjFTspTRjVIWQPS1V1aKri+uil5kLTwZUNTqJgqo5mX2Xg6irrcp9cbZugjyhfbBg7F6m6Eo28kuYnbhJ1QB+PWtGjzi9FNlUce/PW4DA3zuSdTb9+DBlUcSuegsvSxrl9GlK8RiHnFrfp9aBG7qOJUU6ais5/gMYbtcyAzwiEKH83HkgNhNF8qMNm0BImtMnO5nrmMLDV9PrWXfG6nh3atbkBuOvOrYq1dizrOhdy9079SuaPui71BUOBNuEiS+ncE1JPldUzBf/RoS03jrNanY5JwbiRrl3jCpXDVc1CiehtDzWWdwHo64STteFa15NZMnzl2uKt0jqIe76U2CxW4mH2ZrQdxarL3wEauToIdaTdeVkxajdUsFvMhE+eBUYWJjQrwxAKSMz01m3oC4mabbCeb38QsnR1e6yYuhIx5YZZ+wgis2bJfvw4QlOdGgs1WiSTMlMUyqTqm6Sr6YVCPNwInKQzKnreEs8ustZb+F/JwnEz1yANIoIWLXRp01rYSzpO52HAqVAr9lTcYdQdRXvfA9RP04qFBdfUKsSlg0+5WEuLm9agLCq3Ib7iOE3rkZUatsC3Ffqqcqf7I1NVNxtKFmLCpDU9CBWXi9Zxc9SmaSZZYEQiOkJVYLKM3dSvqrO6t8eNMK9WCUfPXnFB5a/VouVGaMfy9uFKX8OhqhUfXZaahHh97IaHGW3TFgfac658r/6GPsj6sFpmhzBKRflGN4Fu1dYEkyf4w1ibR+GT8hiEhl+hU1bPwrG3XOH4oeAksMG+GYiJ9x4IeH/n+ZBRgdI9kr8itfwIl/fRuRHUsWqvx3I8LScra1ky7xx4wmlLyV/P5SmRCAfd7KEYUYOt54lam/MBRSe3GtVWI/E7bGjQq9debsHtPlFnjpKqt5JcsA/43YmFtWZdnsY04AG7FjxM2nH7sIZcWcnu6yO8Z3VflH55bTdWlNkaP2Sl5JCCFS9kC4ruMDqp5ua0duTZNf7Hd9wzB01sYo5nw5BjCe02q4x7wtjEKWvJ2JzylnWiU951Y/LszWwZFoOJbwFn+0Drblcp6qt97toLLlPdHD+uPwSOWUb9VtuEs8Q0RiSj7GVDEkkSwViC/o0H5eRiXzEkm3pfMSZH/HhHM1prVOxlIILP1YNbR07pa4PdHk3FavoRwQVySZB+SBXyBYh8Wj/yrjKKVNW8yoay4jrUTQqdX5+nrJKoy4lLL5F3Kx+rHZnER7VdViLZe8m9Kt9bK7IysXRG5XDprsrftGKsXkKmLv5qOk1buVa0tvScyOSa6PzqTVMvoIrC5n4RmMECjvXqWmmz7ZHrUfTriJnPa9Vq1vVjLFl/bduXhyds/zUZzHplOocT8nkMwFbbf+3vPHn62Lf/2ts9eLD/uo8njuMTYGHblLLp1dGbiDeeBdrLYYZSPcoDzyZfZN95TTlAZ8t+ng3abNGKGhAKE/MxFmAq2Ot1fbzWu1h/DRKgF4uxk9gym14BemKTL05crt+EM4JOp0T4VrJZ/hvM8/okJYUPYktU2VDOSy6/ujFeK33pnb7+8eTouPfdf/T+fPwfLevn29evX+rfp2+Oj07DiUBHqfqhmkTtUs/+ADwITzjUAo+nV8xSzn1a9q72vcG9fnN88uzti9c/nPZ+2g+1AaszLSm+vKmrlmWWjNFCNw3mMRU9nqoDsNf76fjkFLra2npz8vrt66PXL9UbtFDZ29l73N550t79bbz18viPz454nXo/PHtFFm8c1RAlm3RrOcZfvBSy80k+u0hGRU6CRP/b/EN2FXjfHzvBaSqfs6KfTC97vyQz1u5Vv5cLOE0s2fY+DuArLkGSr+hhcJH2s1Dli3TSz0Mf8owj9iwwUE+5omlctHAb6XIOsDGbDALfgBSajtPpinbHC3Iqr7x+n5Q9OaOBzxezZEWbKFSfz+ZFoOJkDLTIdejDNYxz1e5NYQd6XKbyrchDW4bhYZMcp1fbKBZhiVqogdmwH3o7z4arBgoTX6TFJP3AuTNCLSz7CBHhLxMnmYtf4C/LbHA5Dq3sfJoMkMNKQjAC/DLgtsmqYc8v0iRQE+X30xAILC/7vUUxWz3c5TTD5YiVJVFTIQUbberQIOUsHdCdRn8AP+qUQnMgeN9ZFEXObiWoDPNxTHPr6PUPPxwfvX194rXecGu3uDXFX6LRKL9BDg1unKYzksCwteg/T8fJ4LqHLTewZTHBtVpenQy4agiLrWCqQieYZGw1iJGSrIGbMmSwcMoxd/ycqDHJfg6VZ5iXQiOeIRoBDjWQTZW+I/FwTTaqqh0DG5Ns+jKdjsnibBd/Jx/078c7O7etanN5NskWTnOii5P2sslyYlpTv3bQNpoTaGLTXsPez1hrjA6jMxn/uVckGQL5RJnV39jzl3hzgYa93LiYyzEZvgbC7E8Z2QEzh4abRRb72VUqH7hJTj/178U8H1oVKja9ewxOd4cZfQJpukLM9Jw0Lx4gxcdMCERJ1C+WwCFi5tP5BFjUv8KfVQLyWmhMyYTHSsJZvgTaM9Ih6KKrPZ21K74f+OQh1gAoxnpB+0a2vnJoumYIOjW1VdOegcGYUV0cagUjYE/SBec4vqnMrxb6LJDw2hunKgrsHc/N6oNDrUNxJAbT8lNb3w21rgRZPYC1YjSy+zizFna6zPP4PNRANh3kyyHMf5rMyovCxR1oNpAyVWfGQSfuLihCQKjl7NzfCcbY/8wYY38jjAEjTtuagxCP3UifHg7W/6e3b99EQF4M0zklGuTQKxGGXmrTNYC+AIsUBWbjNFIJCRHLpB9QTZ8tKPVTDvsxTcsSmMNJMl1kg/JvF7U4HNlHYRhp9kzhl/O/f6yz93eGdv5RcYUSl5SfjDLQ9FRhDN3qCsTxEsp7JIWRa7SEvNDHu4VxJZaY/YwRhCCFq/1IyzOi5CrJ8qSf5VBqFToIHAP31N/WHwz/Hvnsm2R3gLuE7MypDj3vMBgV/ozEfjidU3pBqmclE2RLPysRFJWOhK/vxOLh8XXD6WIdn9Ss5dGkvfCA66VeOPo38LVCfybMlmkdGJRBIICNuwLWfoo++ggQ5fUUZl1mpZ6RIaDV7333t38EWsCa1giqsMAZQ/45zRxf4Mxpl+xQH8P0wylP7dXRm9ogH1qm2avVD+FDLu9uWQqAZ79wC2sRJurIb5gTFWa2wXvG+9lzeF4pgRPyOOdbt3klF4W2CSIu0+tDh2Nu1APGLTv//yse42wAyPKiGJr14XTajTkLbMmnlfJl8fugF0ddAhCFsmKUWc9nAzzxe50dsuUn51HdBznolnxz8B+rB5mSOrsyRhSXkncd2l1QssXVzP6dB0n9EpYR71T8p2Un1pS/blePH7WCPTEjM3GEVYaftUOVeCN49Rlcim3iqOnfQzspkLZXC+T7uT2/NbEgOC2Tin/DE68blngdrjJznSuhf8jQ1QpKI9tJ3otRe3/vCdESyq5bVAeOZZHaEtIp0w9t6mqK8aL7Rfht1SjddpCgIuTqvn7UNnxsMHbK162HrwIZIuLkXjuoXFuUSKo3MCY+3hR8uW37Nt0yGJ3wR5pX0+7itQs3KMkO4hXzkBNfIdesmVW+1aTGRvliMSjyn7TXuK+XqDZFFQfJjOmGTJGeBcqR8S8ka44487G+nQPEJTXDqrgX01FBVRWFRGqUttx0eEaMV7ulRKlrFOGCSAemMCztn3WZKxUa7mRYzxe9oQB6UQ2HF72/SKeKgsop/4fJVqAu3W+jZZmiOIdwu9wBCL3XKZrxjJGLGwYzftfFOXMBBpWFG4CKDfMqhXS1MdrCbYoPfMcm9e5T2gW+2td0A7RVvqob/M7khd2TgxmIdZCk1OHoWXVHHlgh9Oq4nBbvpzI0AD/+g8xxsSHJ7aaG4SBZc4XxINbfBSEjaay5KbKtzGHPQltEVFmclFkNJlqS+XiJTmcl2XNht7yCdN6aLeedLhtXYIX0BuJIY9F74nHoOKZIG58yO7ND5NBiZnrI9Jlt8laJSqVGS7nlV/JbwbjVegKbBj9e2Ue0SC7p3Jt2AwGNjTH+2XnlYx1tWDM+NkXsMoXJ6E0Pp1Ht2tQ5i4HYN1wl53jTUhStRgLy1RqJI6/hUI2BKVhz9H2R7SJs7ezvnS9eq84bqKh5Io4/epnhSNZdfUYEYYRJrjyiZUtvWraspVUVjQRQeAiwaJQbAhUeABW+2oJI2y9Z5GbUarN5G4AqudS6pgHx2ePph0N/cx056aFN3ngGp9zURULgD7emXmyYB/cTGrXgAxi2bY3SQRsLTYbffP21hWdi79BZgXDi/fh21XGr2HIE5ZCI1FUsxHN9ETRNrMs7Q/Herw3F4SYrkB3QHvzDQztrt+4wdL6pVNDNFaDtr2V1sNXVZio6OHrv5vXrik/oqom8BX5UbrHK2JQnltIQ1Z7UnjlOdBoM7K6r0ZkVs+CcKf3OR2EGr4dNzr4W/1TZKnxsGU4YEbQqG9v1X1RaNshjbV0fu/iuHvS2FneI+r6lDAf+Ng4rDQoDOKASq3IK9JCRQPW+8Sxa0eNNjoR0UxUOuLOzjgFBlp2TmDUdtZ1RD3YADoyhRS+b0R/QhGNTKtLqWIWe38UUE9CECapVl27EzJiWx/IG9xZEPmfTUAaZ4IK8pC2y3DKVgql2SXYxdQjv7O8xSf2mK+D21E8X79MUFwHZ9N2dmv78a9aV7oe7NijAt5ZtuOa0jYAsudlygLdL/w2kKAkSAStE+euGWl9VwG3FEFRSZiPoXr8wTnHCexQM+Lxjd7p2EUK4Ch/iZ7qeUbDVyybD0zhZuJKNhpZ+GKSzRdT4c3pNYNcysN6yoLEZJZjOY/BR3CsnxBg0K70e0z8USWdd6x8l6NtI2DyKj9S+RjQHQN0wltv4FjXlWXkscvOAVrtG0hIQC/GHkOi8qcz/0FepXAyzorGBYkfs9ru+ssira6I+5NmU4f667GAv04o4BkuEI6dUMthWRAyGErGc57FBP3R4OQNOmhyT2eNEpPaKg6iAJTWHYbCepygI/RgQ1V16S1UR3T9F8XcVVknqLY3UZieTZS2WC/adst2mVG2EAmRGEIF0G3ELYfAQcz6wz1SzrsFRviwvGp/FgSrk/6NTo2h+8dP8gNb4/xzsPHnq+f88frrzEP/5Xp44jt/MizEG+s+u0vYwKzEM/pLCmOl8P1pUR4I2oyZ4NsZYXadESdzV80dSDIccgeYpN4T5oUgBblxmkhI5gpb5pEumi2xiQj3L71aE//0r6pQ+Lcx0K3oBxCWqMnSCvq2tFz88P/5/ScwbkXoKzlAH5xFvnRx/f3xy/APKIY+PUEbSOzkm1V0Hrc0wQME8/l9fffVz+c2JXuXG/zhcorL1vybJrPlz+fX/EyP71HkR/Rf+86q59erZyZ+fv/73H3ovX/zw50CDP5+d/a+fz8+/+fn850ZD7165ffa/muffNH9umpC3wI2zaQ7gNsceR61bB0uopessF4Omjuw/yQbzogTcOx12d5qdrCxGaFKxaJgi8TdA1O+Q0vk/TaeWT711gekR1OWeZiRIxn7kiIoGLo143o8Ju/NtYTDv+wuM5sDp6A678pmCtDR2d/YOoq8j/Me7zbhr5aNLld0kM/zdznQtcyJ/WIkZTFCS \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-06 b/.github/reference-runtime-v3/part-06 deleted file mode 100644 index 716c3360..00000000 --- a/.github/reference-runtime-v3/part-06 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-07 b/.github/reference-runtime-v3/part-07 deleted file mode 100644 index cff41e91..00000000 --- a/.github/reference-runtime-v3/part-07 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/reference-runtime-v3/part-08 b/.github/reference-runtime-v3/part-08 deleted file mode 100644 index 4d512885..00000000 --- a/.github/reference-runtime-v3/part-08 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/workflows/apply-reference-runtime.yml b/.github/workflows/apply-reference-runtime.yml deleted file mode 100644 index 637a8d6b..00000000 --- a/.github/workflows/apply-reference-runtime.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Apply reference runtime v3 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -jobs: - apply: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Verify and expand implementation payload - run: | - python - <<'PY' - import base64 - import hashlib - import json - import pathlib - import tarfile - - root = pathlib.Path('.github/reference-runtime-v3') - manifest = json.loads((root / 'manifest.json').read_text(encoding='utf-8')) - parts = [] - for name, expected in sorted(manifest['parts'].items()): - data = (root / name).read_bytes() - if len(data) != expected['bytes']: - raise SystemExit(f'{name}: byte count mismatch') - if hashlib.sha256(data).hexdigest() != expected['sha256']: - raise SystemExit(f'{name}: sha256 mismatch') - parts.append(data) - joined = b''.join(parts) - if len(joined) != manifest['base64_bytes']: - raise SystemExit('base64 byte count mismatch') - if hashlib.sha256(joined).hexdigest() != manifest['base64_sha256']: - raise SystemExit('base64 sha256 mismatch') - archive = base64.b64decode(joined, validate=True) - if len(archive) != manifest['tar_bytes']: - raise SystemExit('archive byte count mismatch') - if hashlib.sha256(archive).hexdigest() != manifest['tar_sha256']: - raise SystemExit('archive sha256 mismatch') - archive_path = pathlib.Path('/tmp/reference-runtime-v3.tgz') - archive_path.write_bytes(archive) - destination = pathlib.Path('.').resolve() - with tarfile.open(archive_path, mode='r:gz') as handle: - for member in handle.getmembers(): - target = (destination / member.name).resolve() - if not target.is_relative_to(destination): - raise SystemExit(f'archive member escapes repository: {member.name}') - handle.extractall(destination) - PY - - name: Apply reference and runtime migration - run: uv run python scripts/apply_reference_runtime.py - - name: Regenerate deterministic catalog and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Remove one-time migration resources - run: | - rm -rf .github/reference-runtime-v3 - rm -f .github/workflows/apply-reference-runtime.yml - rm -f scripts/apply_reference_runtime.py - - name: Commit expanded implementation before final verification - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git commit -m "Complete reference runtime v3 integration" - git push origin HEAD:"$branch" - - name: Run complete verification on committed source - run: ./scripts/check.sh fast diff --git a/authored-skills/docking-validation/SKILL.md b/authored-skills/docking-validation/SKILL.md index 098a08e1..9ed37044 100644 --- a/authored-skills/docking-validation/SKILL.md +++ b/authored-skills/docking-validation/SKILL.md @@ -6,6 +6,12 @@ license: MIT # Docking Validation +## Reference usage + +Read [benchmark design](references/benchmark-design.md) before defining states, pockets, controls, or data splits. Read [metrics and acceptance](references/metric-and-acceptance.md) before computing metrics, fixing thresholds, calibrating scores, or declaring a pass. Read [inference boundaries](references/inference-boundaries.md) before writing pose, ranking, affinity, mechanism, or generalization claims. + +Do not infer benchmark information boundaries, metric aggregation, or claim strength from an engine name or score. Record the reference hashes used for a material benchmark or claim in a `reference-use-ledger`. + ## Decision contract Prespecify the use case, prediction unit, receptor and ligand states, pocket information allowed at inference, benchmark split, controls, primary metric, uncertainty method, applicability domain, and success threshold before inspecting outcomes. Treat benchmark construction, preparation, pose generation, scoring, and decision analysis as separate stages with separate artifacts. diff --git a/authored-skills/docking-validation/references/benchmark-design.md b/authored-skills/docking-validation/references/benchmark-design.md new file mode 100644 index 00000000..a5cdbfbd --- /dev/null +++ b/authored-skills/docking-validation/references/benchmark-design.md @@ -0,0 +1,31 @@ +# Docking benchmark design + +Read before creating a split, defining a pocket, choosing receptor states, or selecting positive and negative controls. + +## Prediction unit + +State whether the prediction unit is a complex, ligand, pose, target, target family, receptor state, or screening library. Metrics and independence groups must use the same unit. + +## State manifest + +Record biological assembly, chains, missing residues, mutations, protonation, tautomers, stereochemistry, formal charges, metals, cofactors, waters, covalent bonds, receptor state, ligand microstate, preparation software, parameters, and hashes. + +## Information boundary + +For a held-out complex, do not derive the pocket, ligand frame, receptor conformation, template, restraints, or feature engineering from the held-out bound pose unless the task explicitly permits that information. + +## Splits + +Preserve: + +- cold scaffold; +- cold analog series; +- cold target; +- cold target family; +- cold receptor state; +- temporal holdout; +- known and possible model-training overlap. + +## Controls + +At minimum include valid redocking controls, cross-docking where robustness is claimed, simple scoring or geometric baselines, chemically relevant negatives or property-matched decoys for screening, and known failure cases. diff --git a/authored-skills/docking-validation/references/index.json b/authored-skills/docking-validation/references/index.json new file mode 100644 index 00000000..1ac7866f --- /dev/null +++ b/authored-skills/docking-validation/references/index.json @@ -0,0 +1,40 @@ +{ + "schema_version": 1, + "skill": "docking-validation", + "default_policy": "load-minimum-required", + "references": [ + { + "id": "benchmark-design", + "path": "references/benchmark-design.md", + "purpose": "Define state manifests, information boundaries, leakage-aware splits, controls, and prediction units.", + "read_when": ["building a docking benchmark", "defining a pocket", "creating train/test splits"], + "required_before": ["benchmark-construction", "pocket-definition", "split-construction"], + "search_patterns": ["## Information boundary", "## Splits", "## Controls"], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Defines benchmark validity; it does not show that a docking engine performs well." + }, + { + "id": "metric-and-acceptance", + "path": "references/metric-and-acceptance.md", + "purpose": "Prespecify pose, screening, calibration, uncertainty, threshold, and failure metrics.", + "read_when": ["computing docking metrics", "setting acceptance thresholds", "calibrating scores"], + "required_before": ["metric-computation", "acceptance-decision", "score-calibration"], + "search_patterns": ["## Pose metrics", "## Screening metrics", "## Thresholds"], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Metric definitions do not validate data splits, state preparation, or biological claims." + }, + { + "id": "inference-boundaries", + "path": "references/inference-boundaries.md", + "purpose": "Keep pose, confidence, ranking, affinity, mechanism, and translational claims distinct.", + "read_when": ["writing docking conclusions", "claiming affinity or mechanism", "defining applicability or abstention"], + "required_before": ["docking-claim-synthesis", "affinity-claim", "mechanism-claim"], + "search_patterns": ["## Distinct outputs", "## Required external evidence", "## Claim downgrade"], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Provides inference limits and does not supply assay or mechanistic evidence." + } + ] +} diff --git a/authored-skills/docking-validation/references/inference-boundaries.md b/authored-skills/docking-validation/references/inference-boundaries.md new file mode 100644 index 00000000..804cf648 --- /dev/null +++ b/authored-skills/docking-validation/references/inference-boundaries.md @@ -0,0 +1,30 @@ +# Docking inference boundaries + +Read before writing pose, ranking, affinity, selectivity, mechanism, or translational claims. + +## Distinct outputs + +Keep separate: + +- generated pose; +- pose confidence; +- docking or rescoring value; +- ranking performance; +- predicted affinity; +- calibrated binding probability; +- experimental affinity; +- cellular or in vivo effect. + +A favorable docking score is not experimental affinity. A plausible pose is not a mechanism. A benchmark average is not evidence for one target–ligand pair outside the benchmark domain. + +## Required external evidence + +Affinity claims require assay-aware evidence with units, relation qualifiers, endpoint, conditions, and uncertainty. Mechanism claims require orthogonal structural, biochemical, mutational, kinetic, or cellular evidence as appropriate. + +## Applicability + +Report target family, receptor states, ligand classes, microstates, covalent or metal chemistry, size and flexibility range, and training-overlap state. Keep unsupported cases exploratory or abstain. + +## Claim downgrade + +When essential support is absent, downgrade from mechanistic to pose hypothesis, from affinity to ranking hypothesis, from generalization to within-benchmark performance, or from supported to inconclusive. diff --git a/authored-skills/docking-validation/references/metric-and-acceptance.md b/authored-skills/docking-validation/references/metric-and-acceptance.md new file mode 100644 index 00000000..9583737c --- /dev/null +++ b/authored-skills/docking-validation/references/metric-and-acceptance.md @@ -0,0 +1,23 @@ +# Docking metrics and acceptance + +Read before computing metrics, fixing thresholds, aggregating poses, or declaring a benchmark pass. + +## Pose metrics + +Report top-1 and top-k symmetry-aware heavy-atom RMSD, complete distributions, missing poses, and physical or chemical plausibility. Interaction recovery complements RMSD but does not replace geometry. + +## Screening metrics + +Use PR-AUC and prespecified enrichment metrics as primary measures when actives are rare. Report active/decoy counts, grouping, bootstrap intervals, and per-target results. ROC-AUC is secondary when class imbalance is material. + +## Calibration and abstention + +A score becomes a probability only after calibration on an appropriate held-out domain. Report reliability, Brier or log loss when relevant, subgroup calibration, and an abstention rule for unsupported chemical or target space. + +## Thresholds + +Fix metric, direction, threshold, confidence interval method, aggregation, exclusions, and subgroup requirements before inspecting held-out outcomes. Process completion is not an acceptance pass. + +## Failure taxonomy + +Distinguish preparation failure, unsupported chemistry, engine failure, no pose, invalid pose, geometric failure, interaction failure, ranking failure, calibration failure, and leakage invalidation. diff --git a/authored-skills/literature-review/SKILL.md b/authored-skills/literature-review/SKILL.md index 749ef5c5..4151b679 100644 --- a/authored-skills/literature-review/SKILL.md +++ b/authored-skills/literature-review/SKILL.md @@ -8,6 +8,12 @@ license: MIT Use this conductor when the deliverable is an auditable body-of-evidence synthesis rather than a citation list or list of papers. Discovery tools retrieve records; this skill owns the protocol, eligibility, study identity, extraction, dependency graph, synthesis boundary, update diff, and review handoff. +## Reference usage + +Before protocol freeze, study deduplication, or replication assessment, read [protocol and study identity](references/protocol-and-study-identity.md). Before structured extraction, risk-of-bias assessment, or claim-level evidence assertions, read [extraction and bias](references/extraction-and-bias.md). Before advancing a cutoff or replacing a reviewed snapshot, read [living update](references/living-update.md). + +Do not infer identifier relationships, extraction fields, bias judgments, or update invalidation rules from a record title. Save material reference use and SHA-256 values in a `reference-use-ledger`. + ## Decision contract Before retrieval, record: diff --git a/authored-skills/literature-review/references/extraction-and-bias.md b/authored-skills/literature-review/references/extraction-and-bias.md new file mode 100644 index 00000000..a402f28f --- /dev/null +++ b/authored-skills/literature-review/references/extraction-and-bias.md @@ -0,0 +1,45 @@ +# Structured extraction and risk of bias + +Read before constructing the study table, effect extraction, risk-of-bias record, or claim-level evidence assertion. + +## Minimum study row + +Record, where applicable: + +- namespaced identifiers and study family; +- publication state, funding, and conflicts; +- design, setting, population or system, sample unit, sample size, and eligibility; +- intervention or exposure, comparator, endpoint, follow-up, and attrition; +- assay, preprocessing, model, covariates, missing-data handling, multiplicity correction, and protocol deviations; +- effect measure, estimate, interval, unit, denominator, and exact source locator; +- prespecified versus post-hoc status; +- supported and contradicted claim IDs; +- independence group and source dependencies. + +Do not infer missing methods, denominators, estimates, or uncertainty from an abstract or portal summary. + +## Risk-of-bias record + +Each record contains: + +```json +{ + "schema_version": 1, + "study_id": "study-1", + "instrument": "RoB 2 / ROBINS-I / domain-specific instrument", + "domains": [ + { + "name": "selection", + "judgment": "low | some-concerns | high | unclear | not-applicable", + "rationale": "source-grounded reason" + } + ], + "overall_judgment": "..." +} +``` + +A numeric summary score cannot replace domain judgments. The overall judgment cannot be more favorable than an essential high-risk domain without an explicit rationale. + +## Evidence assertion + +A material assertion should identify polarity, effect direction, effect measure, estimate and interval, population or system, exact source locator, independence group, and risk-of-bias reference. Citation presence alone is not sufficient. diff --git a/authored-skills/literature-review/references/index.json b/authored-skills/literature-review/references/index.json new file mode 100644 index 00000000..54191bcb --- /dev/null +++ b/authored-skills/literature-review/references/index.json @@ -0,0 +1,40 @@ +{ + "schema_version": 1, + "skill": "literature-review", + "default_policy": "load-minimum-required", + "references": [ + { + "id": "protocol-and-study-identity", + "path": "references/protocol-and-study-identity.md", + "purpose": "Freeze the review protocol, normalize identifiers, resolve publication versions, and determine evidence independence.", + "read_when": ["freezing eligibility", "deduplicating studies", "resolving preprints, corrections, or registries"], + "required_before": ["protocol-freeze", "study-deduplication", "replication-assessment"], + "search_patterns": ["## Protocol freeze", "## Study-family resolution", "## Independence"], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Defines review procedure; actual study records remain the evidence." + }, + { + "id": "extraction-and-bias", + "path": "references/extraction-and-bias.md", + "purpose": "Construct study rows, effect assertions, source locators, and explicit risk-of-bias records.", + "read_when": ["extracting studies", "grading risk of bias", "writing claim-level evidence assertions"], + "required_before": ["structured-extraction", "risk-of-bias-assessment", "evidence-assertion"], + "search_patterns": ["## Minimum study row", "## Risk-of-bias record", "## Evidence assertion"], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Does not supply missing study values or replace primary-source verification." + }, + { + "id": "living-update", + "path": "references/living-update.md", + "purpose": "Advance a literature snapshot without overwriting prior evidence or reusing stale review receipts.", + "read_when": ["running living-update mode", "changing an evidence cutoff", "replacing a reviewed synthesis"], + "required_before": ["living-review-update", "review-snapshot-replacement"], + "search_patterns": ["## Immutable snapshots", "## Update diff", "## Invalidation"], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Controls update lineage; it does not establish that new evidence changes a claim." + } + ] +} diff --git a/authored-skills/literature-review/references/living-update.md b/authored-skills/literature-review/references/living-update.md new file mode 100644 index 00000000..88415fa1 --- /dev/null +++ b/authored-skills/literature-review/references/living-update.md @@ -0,0 +1,33 @@ +# Living review updates + +Read before advancing an evidence cutoff, replacing a reviewed synthesis, or reusing a prior review receipt. + +## Immutable snapshots + +Preserve the prior protocol, exact queries, source releases, included and excluded studies, study-family mapping, claim register, and reviewed report. Create a new snapshot rather than editing the old one. + +## Update diff + +Compare: + +- query additions, removals, and changes; +- source availability or release changes; +- added, removed, and modified studies; +- publication-version, correction, and retraction changes; +- eligibility decisions; +- risk-of-bias changes; +- claim status, confidence, and applicability changes. + +Use: + +```bash +python scripts/diff_literature_review.py previous.json current.json --output diff.json +``` + +## Invalidation + +Invalidate the previous review receipt when covered queries, source snapshots, study table, evidence graph, extraction, claim register, or report bytes change. A narrative that appears unchanged still requires re-review when its evidence dependencies changed. + +## Stop rule + +Stop the update when every source has reached the declared cutoff, pagination is complete or explicitly bounded, unresolved study identities are visible, and claim-level changes have been reviewed. Source failure is unavailable evidence, not a negative study. diff --git a/authored-skills/literature-review/references/protocol-and-study-identity.md b/authored-skills/literature-review/references/protocol-and-study-identity.md new file mode 100644 index 00000000..6c721a55 --- /dev/null +++ b/authored-skills/literature-review/references/protocol-and-study-identity.md @@ -0,0 +1,45 @@ +# Protocol and study identity + +Read before freezing eligibility criteria, deduplicating records, linking publication versions, or claiming independent replication. + +## Protocol freeze + +Record the question framework, population or system, intervention or exposure, comparator, outcomes, designs, publication state, language, time window, source set, evidence cutoff, permitted inference, and stopping rule before reviewing results. + +A change to eligibility, primary outcome, evidence cutoff, or permitted inference creates a protocol revision. Preserve the prior protocol and explain the change. + +## Namespaced identifiers + +Use namespaced persistent identifiers: + +```text +doi:10.xxxx/... +pmid:12345678 +pmcid:PMC123456 +arxiv:2607.12345 +nct:NCT01234567 +``` + +Do not compare bare identifier strings across namespaces. + +## Study-family resolution + +One underlying study may have: + +- registry and protocol records; +- conference abstract; +- preprint; +- accepted manuscript; +- peer-reviewed article; +- correction or retraction; +- secondary analysis. + +Union records through shared identifiers and explicit relationships. Prefer the corrected peer-reviewed version as canonical, but retain every version and its publication state. + +## Independence + +Record shared cohort, sample, site, database, portal, author group, and analysis pipeline. Multiple papers, endpoints, or portal views from one cohort are not independent replication. + +## Exclusion records + +Every exclusion includes stage, reviewer, reason, and source record ID. Do not silently discard near matches, superseded versions, null results, or inaccessible full text. diff --git a/authored-skills/remote-scientific-compute/SKILL.md b/authored-skills/remote-scientific-compute/SKILL.md index 779b9084..d798cd12 100644 --- a/authored-skills/remote-scientific-compute/SKILL.md +++ b/authored-skills/remote-scientific-compute/SKILL.md @@ -8,6 +8,12 @@ description: "Run reproducible scientific workloads through an existing SSH host Move an approved scientific workload to existing remote infrastructure while protecting credentials, login nodes, private data, budgets, and reproducibility. +## Reference usage + +Read [the backend checklists](references/backends.md) for the selected SSH, Slurm/HPC, cloud GPU, or object-storage route before staging, submitting, allocating, or writing remotely. Do not infer scheduler flags, cost controls, storage semantics, or cleanup procedures from a backend name. + +Record the reference version and SHA-256 in the run when it controls a submission or transfer. The backend reference governs execution safety; it is not evidence for the scientific result. + ## Inspect without changing state 1. Use only an existing host alias, provider profile, project, and account. Never diff --git a/authored-skills/remote-scientific-compute/references/index.json b/authored-skills/remote-scientific-compute/references/index.json new file mode 100644 index 00000000..03e613ba --- /dev/null +++ b/authored-skills/remote-scientific-compute/references/index.json @@ -0,0 +1,18 @@ +{ + "schema_version": 1, + "skill": "remote-scientific-compute", + "default_policy": "load-minimum-required", + "references": [ + { + "id": "backend-checklists", + "path": "references/backends.md", + "purpose": "Backend-specific SSH, Slurm/HPC, cloud GPU, and object-storage preflight, execution, monitoring, and cleanup rules.", + "read_when": ["selecting a remote backend", "submitting a Slurm job", "allocating cloud GPU", "transferring object storage data"], + "required_before": ["remote-stage", "remote-submit", "paid-allocation", "object-storage-write"], + "search_patterns": ["## SSH", "## Slurm/HPC", "## Cloud GPU", "## Object storage"], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Controls remote execution safety; it does not validate the scientific method or result." + } + ] +} diff --git a/catalog/inventory.json b/catalog/inventory.json index 7d341cde..d222bb3b 100644 --- a/catalog/inventory.json +++ b/catalog/inventory.json @@ -426,7 +426,7 @@ { "description": "Validate docking, pose-prediction, rescoring, and virtual-screening workflows with leakage-aware controls and prespecified metrics. Use before interpreting AutoDock Vina, GNINA, DiffDock, or related docking outputs.", "executable_count": 0, - "instruction_line_count": 30, + "instruction_line_count": 36, "license": "MIT", "name": "cx-docking-validation", "path": "authored-skills/docking-validation", @@ -762,7 +762,7 @@ { "description": "Run a protocol-driven, updateable literature review with exact multi-source queries, study-level deduplication, structured extraction, evidence dependency tracking, bounded synthesis, provenance, and independent review. Use for systematic, scoping, rapid, living, or evidence-landscape reviews where discovery alone is insufficient.", "executable_count": 0, - "instruction_line_count": 93, + "instruction_line_count": 99, "license": "MIT", "name": "cx-literature-review", "path": "authored-skills/literature-review", @@ -1314,7 +1314,7 @@ { "description": "Run reproducible scientific workloads through an existing SSH host, Slurm/HPC cluster, cloud GPU provider, or remote object storage. Use for remote execution, job submission and monitoring, checkpointed long runs, GPU allocation, data staging, result retrieval, queue diagnosis, or resuming work that can outlive the current Codex task.", "executable_count": 0, - "instruction_line_count": 70, + "instruction_line_count": 76, "license": "Apache-2.0", "name": "cx-remote-scientific-compute", "path": "authored-skills/remote-scientific-compute", diff --git a/docs/NATIVE_SKILL_STANDARD.md b/docs/NATIVE_SKILL_STANDARD.md index 6d239e6b..5b4a0450 100644 --- a/docs/NATIVE_SKILL_STANDARD.md +++ b/docs/NATIVE_SKILL_STANDARD.md @@ -286,3 +286,11 @@ When editing an authored skill: 4. run `./scripts/check.sh fast`; 5. run `./scripts/check.sh public` only for explicit live-source validation; 6. describe what was verified, what was not, and which scientific claims remain unvalidated. + +## Progressive reference contract + +Keep the decision-bearing workflow, outputs, stop rules, and inference boundary in `SKILL.md`. Move source-specific operations, command signatures, large schemas, benchmark details, backend procedures, and worked examples into `references/`. + +A skill with detailed references should provide `references/index.json` and a `## Reference usage` section. The index declares stable reference IDs, purposes, `read_when` routes, mandatory `required_before` operations, search patterns, authority, version, and evidence boundary. The agent must read the selected reference before executing a controlled operation and must not guess arguments or schemas that exist only in the reference. + +When a reference materially controls a query, command, transformation, threshold, or claim, record a hash-bound `reference-use-ledger`. Reference documents guide procedure; they are not scientific evidence by themselves. Run `python scripts/audit_skill_references.py --require-clean` before treating a migrated core skill as complete. diff --git a/docs/PLATFORM_CONTRACTS.md b/docs/PLATFORM_CONTRACTS.md index 01fcd124..c2befaa6 100644 --- a/docs/PLATFORM_CONTRACTS.md +++ b/docs/PLATFORM_CONTRACTS.md @@ -286,3 +286,15 @@ The workbench is a local offline HTML view of validated claims, findings, annota - A deterministic SBDD fixture is not an affinity experiment. - Artifact or model changes invalidate covered receipts. - Fresh install and managed update activation require the complete candidate contract. + +## Progressive skill references + +Core skills use `references/index.json` to load the minimum detailed instructions needed for the selected route. Mandatory references must be read before their `required_before` operation, and material use is recorded in a hash-bound `reference-use-ledger`. See `docs/REFERENCE_CONTRACT.md`. + +## Connector Contract v3 + +`science_query_source_v3` adds true source pagination, bounded retries, response-size limits, ETag and Last-Modified capture, rate-limit state, page-level request and response hashes, explicit completeness, and replayable raw page snapshots. Initial v3 operations cover PubMed, Europe PMC, ChEMBL molecule search, and RCSB PDB search. Sources not yet migrated remain available through v2 with their maturity visible. + +## Large artifact protocol + +Artifact validation uses streaming SHA-256 for files and deterministic Merkle roots for directories. Chunked descriptors support resumable verification, while a local content-addressed store reuses immutable verified files. Large scientific objects are never required to be loaded with `read_bytes()`. diff --git a/docs/REFERENCE_CONTRACT.md b/docs/REFERENCE_CONTRACT.md new file mode 100644 index 00000000..d5432fdd --- /dev/null +++ b/docs/REFERENCE_CONTRACT.md @@ -0,0 +1,172 @@ +# Scientific skill reference contract + +Codex Science uses progressive disclosure: `SKILL.md` contains the decision-bearing procedure, while `references/` contains detailed source schemas, command matrices, benchmark rules, and worked examples that are loaded only for the selected route. + +This contract follows the strongest pattern in Google DeepMind Science Skills: a skill lists its reference groups, states when each group is mandatory, and forbids guessing arguments or schemas that live only in a reference. Codex Science adds machine-readable indexing, use receipts, hash invalidation, and repository-wide validation. + +## Directory contract + +```text +skill-name/ +├── SKILL.md +└── references/ + ├── index.json + ├── source-query.md + └── output-schema.md +``` + +Keep reference files one level below `references/`. A reference may link to an authoritative external source, but the indexed local file must state what the source is used for, its evidence boundary, and any version or access requirement. + +## `references/index.json` + +```json +{ + "schema_version": 1, + "skill": "skill-name", + "default_policy": "load-minimum-required", + "references": [ + { + "id": "source-query", + "path": "references/source-query.md", + "purpose": "Exact source operations, filters, pagination, and output fields.", + "read_when": [ + "retrieving records from the source", + "debugging a schema or pagination change" + ], + "required_before": [ + "source-search", + "source-fetch" + ], + "search_patterns": [ + "## search", + "## pagination", + "## output schema" + ], + "authority": "official-source-contract", + "version": "2026-07", + "evidence_boundary": "Describes retrieval behavior; it does not establish the scientific truth of returned records." + } + ] +} +``` + +### Required fields + +- `id`: stable identifier used in receipts and tests. +- `path`: safe path exactly one level below `references/`. +- `purpose`: what decision or operation this reference informs. +- `read_when`: natural-language routes that make the reference relevant. +- `required_before`: operation IDs that cannot be executed correctly without reading it. + +### Optional fields + +- `search_patterns`: headings or terms to locate in a long reference. Required by policy for large files. +- `authority`: repository contract, official source documentation, published standard, or another explicit authority class. +- `version`: reference or source-contract version. +- `evidence_boundary`: claims the reference itself cannot support. + +## `SKILL.md` reference usage section + +An indexed core skill contains `## Reference usage` or `## Reference map` and links every indexed file. The section must say: + +1. when to read the file; +2. which operation requires it before execution; +3. what not to infer without it; +4. whether a use receipt is required. + +Example: + +```markdown +## Reference usage + +Before calling any source operation, inspect `references/index.json` and load only the entries selected for the route. + +- Read [source query operations](references/source-query.md) before `source-search` or `source-fetch`. Do not infer argument names, filters, pagination, or output fields from an operation name. +- Read [output schema](references/output-schema.md) before writing a claim-bearing extraction table. + +For a material claim or executed command, save the loaded reference hash and reason in a `reference-use-ledger` artifact. +``` + +## Runtime loading procedure + +1. Load the selected skill contract. +2. If `references/index.json` exists, validate it before execution. +3. Select the minimum entries whose `read_when` or `required_before` match the route. +4. Read mandatory entries before the corresponding operation. Do not guess missing arguments, schemas, thresholds, source semantics, or licenses. +5. For a long file, search the indexed patterns first, then read the smallest contiguous section that resolves the route. +6. Record the reference file SHA-256 when it materially controls a query, command, threshold, transformation, or claim. +7. If the reference hash changes, invalidate receipts and reviews that depended on the previous bytes. + +Use: + +```bash +python scripts/reference_lookup.py authored-skills/literature-review \ + --route study-deduplication \ + --query "preprint correction registry" \ + --receipt-dir artifacts/run/reference-uses \ + --claim C1 +``` + +## Reference use ledger + +```json +{ + "schema_version": 1, + "skill": "literature-review", + "uses": [ + { + "schema_version": 1, + "skill": "literature-review", + "reference_id": "protocol-and-study-identity", + "path": "references/protocol-and-study-identity.md", + "sha256": "...", + "read_reason": "study-deduplication", + "used_for": ["claim-C1", "study-table"], + "sections": ["Study identity"], + "search_terms": ["correction", "preprint"], + "loaded_at": "2026-07-20T00:00:00Z" + } + ] +} +``` + +Attach the ledger to the run manifest with `kind: reference-use-ledger`. It records procedural grounding, not scientific evidence by itself. + +## What belongs where + +### Keep in `SKILL.md` + +- trigger and non-trigger boundary; +- decision contract; +- ordered workflow; +- mandatory outputs; +- stop and approval rules; +- core inference boundary; +- reference selection instructions. + +### Move to `references/` + +- source-specific query syntax and field maps; +- detailed command signatures; +- benchmark split and metric definitions; +- large schemas; +- long troubleshooting matrices; +- worked examples; +- standards and reporting checklists; +- backend-specific procedures. + +### Keep in `scripts/` + +- deterministic operations intended to execute; +- validators and converters; +- small CLIs with explicit dependencies and error handling. + +A reference is read for reasoning. A script is executed for a deterministic operation. `SKILL.md` must not leave this distinction ambiguous. + +## Quality gate + +```bash +python scripts/audit_skill_references.py --require-clean +``` + +The audit checks indexed paths, one-level layout, duplicate IDs, missing files, unindexed files, links from strict core skills, required read conditions, and search patterns for large references. Non-migrated first-party skills are reported for progressive cleanup; strict core skills fail the gate. diff --git a/pyproject.toml b/pyproject.toml index 30712fec..b501aa34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "codex-science" -version = "0.3.0" +version = "0.4.0" description = "Audited scientific research workflows and public-source tools for Codex" readme = "README.md" authors = [ diff --git a/release/manifest.json b/release/manifest.json index f724a17d..ed8d737c 100644 --- a/release/manifest.json +++ b/release/manifest.json @@ -1,8 +1,8 @@ { "schema_version": 1, - "package_version": "0.3.0", - "plugin_version": "0.3.0+codex.20260720000100", - "mcp_version": "0.3.0", + "package_version": "0.4.0", + "plugin_version": "0.4.0+codex.20260721000100", + "mcp_version": "0.4.0", "runtime_affecting_prefixes": [ ".codex-plugin/", ".mcp.json", diff --git a/scripts/audit_skill_references.py b/scripts/audit_skill_references.py new file mode 100755 index 00000000..07cef43e --- /dev/null +++ b/scripts/audit_skill_references.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Audit progressive-disclosure reference contracts for first-party skills.""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) +from codex_science.reference_contract import audit_reference_roots # noqa: E402 + +STRICT_SKILLS = { + "codex-science", + "science-provenance", + "science-review", + "literature-review", + "docking-validation", + "remote-scientific-compute", +} + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path) + parser.add_argument("--require-clean", action="store_true") + args = parser.parse_args() + root = args.root.resolve() + report = audit_reference_roots( + [root / "skills", root / "authored-skills"], + strict_names=STRICT_SKILLS, + ) + text = json.dumps(report, indent=2, sort_keys=True) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(text, encoding="utf-8") + else: + print(text, end="") + summary = report["summary"] + print( + "reference audit: " + f"skills={summary['skills']} indexed={summary['indexed_skills']} " + f"references={summary['references']} findings={summary['findings']} " + f"major={summary['major_findings']}", + file=sys.stderr, + ) + return 1 if args.require_clean and summary["major_findings"] else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/candidate_contract_check.py b/scripts/candidate_contract_check.py index 7258473c..0de48433 100755 --- a/scripts/candidate_contract_check.py +++ b/scripts/candidate_contract_check.py @@ -31,6 +31,7 @@ def main() -> int: run([python, "scripts/validate_release.py", "--root", str(root)], cwd=root) run([python, "scripts/validate_connector_contracts.py", "--root", str(root)], cwd=root) + run([python, "scripts/audit_skill_references.py", "--root", str(root), "--require-clean"], cwd=root) run([python, "-m", "compileall", "-q", "src", "scripts"], cwd=root) with tempfile.TemporaryDirectory() as tempdir: diff --git a/scripts/check.sh b/scripts/check.sh index 9023295b..509b20be 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -37,6 +37,7 @@ run_science_contracts() ( uv run python scripts/validate_release.py uv run python scripts/validate_connector_contracts.py + uv run python scripts/audit_skill_references.py --require-clean uv run python scripts/validate_models.py uv run python scripts/validate_model_registry_v2.py uv run python scripts/run_reviewer_benchmark.py --output "$benchmark_tmp" --require-safe diff --git a/scripts/describe_artifact.py b/scripts/describe_artifact.py new file mode 100755 index 00000000..7240f78d --- /dev/null +++ b/scripts/describe_artifact.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""Create or verify streaming/Merkle descriptors for large scientific artifacts.""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) +from codex_science.artifact_store import ( # noqa: E402 + ContentAddressedStore, + describe_directory, + describe_file, + validate_descriptor, + write_descriptor, +) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("path", type=Path) + parser.add_argument("--output", type=Path) + parser.add_argument("--verify", type=Path, help="Existing descriptor to validate against path") + parser.add_argument("--chunk-size", type=int) + parser.add_argument("--media-type") + parser.add_argument("--store", type=Path, help="Add a file to a content-addressed store") + args = parser.parse_args() + target = args.path.resolve() + if args.verify: + payload = json.loads(args.verify.read_text(encoding="utf-8")) + descriptor = validate_descriptor(payload, target) + elif target.is_dir(): + descriptor = describe_directory(target, media_type=args.media_type) + else: + descriptor = describe_file(target, media_type=args.media_type, chunk_size=args.chunk_size) + if args.output: + write_descriptor(args.output.resolve(), descriptor) + else: + print(json.dumps(descriptor.to_dict(), indent=2, sort_keys=True)) + if args.store: + if not target.is_file(): + raise SystemExit("--store currently accepts files only") + digest, stored = ContentAddressedStore(args.store.resolve()).add_file(target) + print(json.dumps({"sha256": digest, "stored_path": str(stored)}, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/query_source_v3.py b/scripts/query_source_v3.py new file mode 100755 index 00000000..83534d07 --- /dev/null +++ b/scripts/query_source_v3.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Execute a true-paginated Connector Contract v3 source query.""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) +from codex_science.connector_contract import QueryRequest # noqa: E402 +from codex_science.source_operations_v3 import SOURCE_OPERATIONS_V3 # noqa: E402 +from codex_science.transport_v3 import execute_paginated # noqa: E402 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("source", choices=sorted(SOURCE_OPERATIONS_V3)) + parser.add_argument("query") + parser.add_argument("--page-size", type=int, default=25) + parser.add_argument("--max-pages", type=int, default=1) + parser.add_argument("--evidence-cutoff") + parser.add_argument("--snapshot-dir", type=Path) + parser.add_argument("--output", type=Path) + args = parser.parse_args() + request = QueryRequest( + source=args.source, + operation="search", + parameters={"query": args.query}, + page_size=args.page_size, + max_pages=args.max_pages, + evidence_cutoff=args.evidence_cutoff, + source_contract_version="3", + ) + result = execute_paginated( + SOURCE_OPERATIONS_V3[args.source], + request, + snapshot_dir=None if args.snapshot_dir is None else args.snapshot_dir.resolve(), + ) + text = json.dumps(result.to_dict(), indent=2, sort_keys=True) + "\n" + if args.output: + args.output.write_text(text, encoding="utf-8") + else: + print(text, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/reference_lookup.py b/scripts/reference_lookup.py new file mode 100755 index 00000000..d21b8786 --- /dev/null +++ b/scripts/reference_lookup.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +"""Select the minimum required skill references and optionally emit use receipts.""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) +from codex_science.reference_contract import ( # noqa: E402 + build_reference_use_receipt, + load_reference_index, + select_references, +) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("skill_dir", type=Path) + parser.add_argument("--route", default="") + parser.add_argument("--query", default="") + parser.add_argument("--limit", type=int) + parser.add_argument("--receipt-dir", type=Path) + parser.add_argument("--claim", action="append", default=[]) + args = parser.parse_args() + skill_dir = args.skill_dir.resolve() + index = load_reference_index(skill_dir) + if index is None: + raise SystemExit(f"skill has no references/index.json: {skill_dir}") + selected = select_references(index, route=args.route, query=args.query, limit=args.limit) + payload: dict[str, object] = { + "schema_version": 1, + "skill": index.skill, + "selected": [entry.to_dict() for entry in selected], + } + if args.receipt_dir: + args.receipt_dir.mkdir(parents=True, exist_ok=True) + receipts = [] + for entry in selected: + receipt = build_reference_use_receipt( + skill_dir, + entry, + read_reason=args.route or args.query or "selected by reference lookup", + used_for=args.claim or ["workflow-route"], + search_terms=[args.query] if args.query else [], + ) + path = args.receipt_dir / f"{index.skill}--{entry.id}.json" + path.write_text(json.dumps(receipt.to_dict(), indent=2, sort_keys=True) + "\n", encoding="utf-8") + receipts.append(path.as_posix()) + payload["receipt_paths"] = receipts + print(json.dumps(payload, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/replay_source_v3.py b/scripts/replay_source_v3.py new file mode 100755 index 00000000..191310bb --- /dev/null +++ b/scripts/replay_source_v3.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Verify and replay raw pages saved by Connector Contract v3.""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) +from codex_science.transport_v3 import replay_snapshot_directory # noqa: E402 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("snapshot_dir", type=Path) + parser.add_argument("query_id") + parser.add_argument("--output", type=Path) + args = parser.parse_args() + pages = replay_snapshot_directory(args.snapshot_dir.resolve(), args.query_id) + report = { + "schema_version": 1, + "query_id": args.query_id, + "page_count": len(pages), + "pages": pages, + } + text = json.dumps(report, indent=2, sort_keys=True) + "\n" + if args.output: + args.output.write_text(text, encoding="utf-8") + else: + print(text, end="") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/validate_connector_contracts.py b/scripts/validate_connector_contracts.py index aab7b124..f70a1228 100755 --- a/scripts/validate_connector_contracts.py +++ b/scripts/validate_connector_contracts.py @@ -1,10 +1,13 @@ #!/usr/bin/env python3 -"""Validate Connector Contract v2, source registry, and offline replay semantics.""" +"""Validate Connector Contracts v2/v3, source registries, pagination, and replay.""" from __future__ import annotations import argparse +import json import sys +import tempfile from pathlib import Path +from typing import Any ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "src")) @@ -13,9 +16,37 @@ from codex_science.connector_sources import SOURCE_BY_KEY, SOURCE_BY_TOOL, SOURCE_SPECS # noqa: E402 from codex_science.connectors import PubMedConnector # noqa: E402 from codex_science.mcp_server import CONNECTOR_SPECS # noqa: E402 +from codex_science.source_operations_v3 import PubMedSearchV3, SOURCE_OPERATIONS_V3 # noqa: E402 +from codex_science.transport_v3 import PageRequest, TransportResponse, execute_paginated, replay_snapshot_directory # noqa: E402 from codex_science.typed_connectors import ClinVarConnector, GnomADConnector, VersionedSnapshotConnector # noqa: E402 +class FixtureTransport: + def __init__(self, payloads: list[dict[str, Any]]) -> None: + self.payloads = list(payloads) + self.requests: list[PageRequest] = [] + + def send(self, request: PageRequest) -> TransportResponse: + self.requests.append(request) + payload = self.payloads.pop(0) + body = json.dumps(payload, separators=(",", ":")).encode("utf-8") + return TransportResponse( + status_code=200, + url=request.url, + headers={ + "content-type": "application/json", + "etag": f'"fixture-{len(self.requests)}"', + "last-modified": "Sun, 19 Jul 2026 00:00:00 GMT", + "x-ratelimit-limit": "10", + "x-ratelimit-remaining": "9", + }, + body=body, + media_type="application/json", + retrieved_at="2026-07-19T00:00:00Z", + attempts=1, + ) + + def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--root", type=Path, default=ROOT) @@ -30,6 +61,8 @@ def main() -> int: raise SystemExit("legacy connector registry must preserve 34 entries") if set(keys) != set(SOURCE_BY_KEY) or set(tools) != set(SOURCE_BY_TOOL): raise SystemExit("connector source registry indexes are stale") + if set(SOURCE_OPERATIONS_V3) != {"pubmed", "europepmc", "chembl", "pdb"}: + raise SystemExit("connector v3 operation registry is stale") def pubmed_payload(_url: str) -> dict: return { @@ -47,9 +80,36 @@ def pubmed_payload(_url: str) -> dict: snapshot = result.snapshot() replayed = replay_snapshot(snapshot) if replayed.records != result.records: - raise SystemExit("connector snapshot replay changed normalized records") + raise SystemExit("connector v2 snapshot replay changed normalized records") if classify_drift(snapshot, snapshot)["drift_types"] != ["none"]: - raise SystemExit("identical connector snapshots must not drift") + raise SystemExit("identical connector v2 snapshots must not drift") + + v3_request = QueryRequest( + "pubmed", + "search", + {"query": "protein folding"}, + page_size=2, + max_pages=3, + source_contract_version="3", + ) + fixture = FixtureTransport( + [ + {"esearchresult": {"count": "3", "retstart": "0", "idlist": ["1", "2"]}}, + {"esearchresult": {"count": "3", "retstart": "2", "idlist": ["3"]}}, + ] + ) + with tempfile.TemporaryDirectory() as tempdir: + snapshot_dir = Path(tempdir) + v3_result = execute_paginated( + PubMedSearchV3(), v3_request, transport=fixture, snapshot_dir=snapshot_dir + ) + if v3_result.receipt.status != "complete" or len(v3_result.records) != 3: + raise SystemExit("connector v3 true pagination failed") + if v3_result.receipt.pages[0].etag != '"fixture-1"': + raise SystemExit("connector v3 did not preserve ETag") + replayed_pages = replay_snapshot_directory(snapshot_dir, v3_request.query_id) + if len(replayed_pages) != 2: + raise SystemExit("connector v3 raw snapshot replay failed") clinvar = ClinVarConnector(fetch_json=lambda _url: {"esearchresult": {"idlist": ["42"]}}) if clinvar.search("BRCA1", limit=1)[0]["id"] != "42": @@ -73,7 +133,10 @@ def pubmed_payload(_url: str) -> dict: if not release: raise SystemExit("versioned eQTL release snapshot is unavailable") - print(f"connector contracts: valid ({len(SOURCE_SPECS)} sources, 34 legacy tools)") + print( + f"connector contracts: valid ({len(SOURCE_SPECS)} sources, " + "34 legacy tools, 4 v3 true-pagination operations)" + ) return 0 diff --git a/skills/codex-science/SKILL.md b/skills/codex-science/SKILL.md index 85a66e2d..2ca40068 100644 --- a/skills/codex-science/SKILL.md +++ b/skills/codex-science/SKILL.md @@ -36,6 +36,12 @@ While this mode is active, prefer Codex Science MCP tools and `status: active` c Fall back to general-purpose tools or skills only when no suitable Science capability exists, the Science capability is unavailable or fails, or a higher-priority instruction requires another route. This preference does not override the activation gate, the approved plan, user instructions, project rules, or safety policy; never select an inactive skill silently. +## Reference usage + +After selecting a skill, inspect its `references/index.json` when present and load only the entries required by the active route. Read [the reference-loading protocol](references/reference-loading.md) before any reference-controlled operation; do not infer arguments, output schemas, source semantics, thresholds, or licenses from names alone. Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run. + +When a reference materially controls a query, command, transformation, threshold, or claim, record its path, SHA-256, read reason, and affected claim or artifact in a `reference-use-ledger`. A reference explains procedure; the actual source record or computation remains the scientific evidence. + ## Research contract and evidence graph For every non-trivial run, create a compact contract before retrieval or execution: diff --git a/skills/codex-science/references/index.json b/skills/codex-science/references/index.json new file mode 100644 index 00000000..6f88712c --- /dev/null +++ b/skills/codex-science/references/index.json @@ -0,0 +1,50 @@ +{ + "schema_version": 1, + "skill": "codex-science", + "default_policy": "load-minimum-required", + "references": [ + { + "id": "reference-loading", + "path": "references/reference-loading.md", + "purpose": "Select, load, and receipt detailed skill references without flooding context or guessing undocumented arguments.", + "read_when": [ + "a selected skill contains references/index.json", + "a route depends on detailed source, command, benchmark, or backend semantics" + ], + "required_before": [ + "selected-skill-reference-loading", + "material-reference-controlled-operation" + ], + "search_patterns": [ + "## 2. Load the minimum required material", + "## 3. Read before acting", + "## 5. Record material use" + ], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Controls procedural grounding; it is not scientific evidence." + }, + { + "id": "goal-loop-contract", + "path": "references/goal-loop-contract.md", + "purpose": "Operate native Goal mode and checkpoint lifecycle without creating conflicting continuation loops.", + "read_when": [ + "starting or resuming a native Goal run", + "explaining completion_pending or Goal confirmation" + ], + "required_before": [ + "native-goal-start", + "native-goal-resume", + "native-goal-completion" + ], + "search_patterns": [ + "completion_pending", + "confirm-goal-complete", + "Stop" + ], + "authority": "repository-contract", + "version": "4", + "evidence_boundary": "Controls task lifecycle only; it does not attest scientific completion." + } + ] +} diff --git a/skills/codex-science/references/reference-loading.md b/skills/codex-science/references/reference-loading.md new file mode 100644 index 00000000..df83ec95 --- /dev/null +++ b/skills/codex-science/references/reference-loading.md @@ -0,0 +1,54 @@ +# Reference loading protocol + +Use this protocol after the coordinator selects a skill and before the selected skill executes a route controlled by detailed reference material. + +## 1. Inspect the reference index + +Look for `/references/index.json`. + +- When absent, follow the complete contract in `SKILL.md`; do not search the repository for undocumented instructions and do not invent a reference. +- When present, validate the index and select the minimum entries matching the route. +- Treat `required_before` as a hard procedural dependency. + +## 2. Load the minimum required material + +Rank entries by: + +1. exact `required_before` operation match; +2. `read_when` route match; +3. query terms matching purpose or indexed search patterns. + +Do not load every reference in a broad skill. This preserves context for primary evidence and actual execution records. + +## 3. Read before acting + +A mandatory reference is read before: + +- constructing an API or CLI call whose arguments are documented there; +- applying a source-specific filter or pagination rule; +- interpreting a field, score, confidence value, unit, or missingness state; +- choosing a benchmark split, threshold, or acceptance metric; +- making a write, compute, or approval decision controlled by a backend guide. + +Do not infer argument order, output schema, source semantics, or acceptance thresholds from names alone. + +## 4. Search long references deliberately + +Use the index `search_patterns` to find the relevant heading or field. Read enough contiguous context to capture prerequisites, exceptions, and output semantics. A matching sentence without its surrounding boundary is not sufficient. + +## 5. Record material use + +Create a reference-use receipt when the reference controls a material query, command, transformation, threshold, or claim. Record: + +- skill and reference ID; +- relative path and SHA-256; +- reason for reading; +- claim, artifact, or operation informed; +- sections and search terms used; +- load time. + +Aggregate receipts into a `reference-use-ledger` artifact. A changed reference hash invalidates the procedural assumption and any review receipt that explicitly covered it. + +## 6. Preserve evidence boundaries + +References describe how to use a source, model, method, or schema. They are not evidence that a returned biological, chemical, or clinical claim is true. Link the actual source record or computation separately in the evidence graph. diff --git a/skills/science-provenance/SKILL.md b/skills/science-provenance/SKILL.md index f42a69ee..13385b56 100644 --- a/skills/science-provenance/SKILL.md +++ b/skills/science-provenance/SKILL.md @@ -9,6 +9,12 @@ Create `artifacts//manifest.json` in the user's current research project For a non-trivial active run, keep `checkpoint.json` beside the manifest as mutable control state. Schema v4 binds the run to an activation-generation key; records structured done criteria, hashed evidence and review receipts, continuation budget, native Goal task binding and phase, external waits, failed attempts, gates, and blockers; and prevents a second nonterminal run for the same owner across nested artifact roots. Update it with `/scripts/science_checkpoint.py`. Save a run-local progress record before `heartbeat --progress-ref`; do not cite the mutable checkpoint as scientific evidence or add it to the manifest. +## Reference usage + +Read [the artifact contract](references/artifact-contract.md) before authoring a manifest, sidecar, or bundle-version change. Read [the large-artifact protocol](references/large-artifacts.md) before registering a directory, a file too large for whole-memory loading, a chunked dataset, model weights, or an immutable external object. Do not guess sidecar fields, Merkle rules, or invalidation semantics. + +When a reference controls artifact registration or review, save a `reference-use-ledger` with the reference hash. A changed reference hash invalidates a receipt that explicitly covered its prior procedural assumptions. + ## Run contract 1. Use a stable run ID. Create a new run when the question, estimand, baseline, metric, data split, success threshold, or intended decision changes. diff --git a/skills/science-provenance/references/artifact-contract.md b/skills/science-provenance/references/artifact-contract.md index 99b6dc8f..536d7616 100644 --- a/skills/science-provenance/references/artifact-contract.md +++ b/skills/science-provenance/references/artifact-contract.md @@ -73,3 +73,9 @@ Claim status and computation status are separate axes. A claim may be planned, s Create a new run when the research question, estimand, baseline, primary metric, data split, success threshold, intended decision, or material input identity changes. Keep revisions inside one run only when they answer the same contract and preserve original files, snapshot diffs, findings, and prior receipts. A review or acceptance receipt becomes stale when a covered claim, artifact hash, source snapshot, query, code revision, model contract, weight, database, configuration, input, or acceptance bundle changes. Retain failed and inconclusive manifests. Never rewrite a failed run into a successful one, detach counterevidence, silently transfer an annotation, or reuse a receipt for changed bytes. + +## Progressive references and large artifacts + +Use `reference-use-ledger` for hash-bound records of detailed skill references that controlled a material query, command, threshold, transformation, or claim. Use `artifact-descriptor` for streaming, chunked, directory-Merkle, or immutable external artifact metadata. + +Manifest schema version `1` remains stable. A local artifact may optionally declare `artifact_type` as `file`, `chunked-file`, or `directory-tree`, together with `size_bytes`, `entry_count`, `media_type`, and `descriptor_path`. Directory artifacts use the deterministic `sha256-merkle-v1` root described in [large-artifacts.md](large-artifacts.md). Changing referenced instructions, descriptor bytes, directory entries, or chunk hashes invalidates dependent review receipts. diff --git a/skills/science-provenance/references/index.json b/skills/science-provenance/references/index.json new file mode 100644 index 00000000..c1cbbba0 --- /dev/null +++ b/skills/science-provenance/references/index.json @@ -0,0 +1,53 @@ +{ + "schema_version": 1, + "skill": "science-provenance", + "default_policy": "load-minimum-required", + "references": [ + { + "id": "artifact-contract", + "path": "references/artifact-contract.md", + "purpose": "Manifest fields, validated sidecars, evidence links, versioning, and review invalidation.", + "read_when": [ + "creating or changing a manifest", + "adding a sidecar", + "reviewing bundle versioning or invalidation" + ], + "required_before": [ + "manifest-authoring", + "sidecar-authoring", + "bundle-versioning" + ], + "search_patterns": [ + "## Validated sidecars", + "## Bundle validation", + "## Versioning and invalidation" + ], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Defines packaging and validation; it does not validate domain-specific scientific claims." + }, + { + "id": "large-artifacts", + "path": "references/large-artifacts.md", + "purpose": "Streaming, chunked, Merkle-tree, external-reference, and content-addressed handling for large scientific outputs.", + "read_when": [ + "an artifact is a directory", + "a file is too large for whole-file memory loading", + "a run uses trajectories, AnnData, BAM/CRAM, Zarr, Parquet, weights, or remote immutable objects" + ], + "required_before": [ + "large-artifact-registration", + "directory-artifact-registration", + "content-addressed-cache-insertion" + ], + "search_patterns": [ + "## Artifact types", + "## Content-addressed cache", + "## Validation and failure semantics" + ], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Validates artifact identity and transfer semantics, not the scientific meaning of the artifact." + } + ] +} diff --git a/skills/science-provenance/references/large-artifacts.md b/skills/science-provenance/references/large-artifacts.md new file mode 100644 index 00000000..d3b25304 --- /dev/null +++ b/skills/science-provenance/references/large-artifacts.md @@ -0,0 +1,62 @@ +# Large artifact protocol + +Use this protocol for files or directories that should not be loaded wholly into memory, including trajectories, HDF5 or AnnData files, BAM/CRAM, bigWig, Zarr stores, Parquet datasets, model weights, and large image collections. + +## Artifact types + +### `file` + +A regular local file. Compute SHA-256 with streaming reads. + +### `chunked-file` + +A regular file with a descriptor containing ordered chunk offsets, sizes, and SHA-256 values. The root SHA-256 remains the full-file digest. Chunk records support resumable verification and transfer. + +### `directory-tree` + +A directory represented by a deterministic list of relative file paths, sizes, and SHA-256 values. The root digest is: + +```text +sha256(canonical_json({"algorithm":"sha256-merkle-v1","entries":[...]})) +``` + +Symbolic links are rejected by default. A directory entry cannot escape the artifact root. + +### `external-reference` + +An immutable remote or database object described by URI or accession, version, SHA-256, size when known, media type, and license. It is not considered locally verified until bytes are materialized and checked. + +## Commands + +```bash +python scripts/describe_artifact.py trajectory/ \ + --media-type application/x-zarr \ + --output trajectory.descriptor.json + +python scripts/describe_artifact.py model.safetensors \ + --chunk-size 8388608 \ + --output model.descriptor.json + +python scripts/describe_artifact.py model.safetensors \ + --verify model.descriptor.json +``` + +Add a descriptor with `kind: artifact-descriptor`. For a directory artifact, add the directory itself to the manifest with `artifact_type: directory-tree`, the descriptor root SHA-256, total bytes, and entry count. + +## Content-addressed cache + +A local cache stores immutable files at: + +```text +/objects/sha256// +``` + +Insertion computes the digest before and after copy, writes through a temporary file, marks the object read-only, and reuses an existing verified object. Cache presence is not proof that a source license permits redistribution. + +## Validation and failure semantics + +- Reject missing files, digest changes, path traversal, escaping links, non-contiguous chunks, and descriptor size mismatches. +- Preserve incomplete downloads outside the immutable object path. +- Distinguish `downloaded`, `partially-downloaded`, `locally-verified`, and `externally-referenced` states. +- Do not hash a multi-gigabyte file with `read_bytes()`. +- Do not copy private or licensed data into a run bundle merely to make it self-contained. diff --git a/skills/science-review/SKILL.md b/skills/science-review/SKILL.md index 423b8a9c..f2f3c5ec 100644 --- a/skills/science-review/SKILL.md +++ b/skills/science-review/SKILL.md @@ -7,6 +7,12 @@ description: Independently review a scientific run by checking claims against th Review the record adversarially; do not assume the producing agent's conclusion, selected method, or confidence is correct. +## Reference usage + +Read [the review checklist](references/review-checklist.md) before assigning severity, deciding whether a review passes, or closing a material finding. Use the indexed headings rather than loading unrelated reference material. Do not infer a pass rule or downgrade a finding from prose convenience. + +If the checklist materially controls a review receipt, include its SHA-256 in the run's `reference-use-ledger`. The checklist defines review procedure; it does not prove the reviewed conclusion true. + ## Inputs and review mode Require the decision contract, approved plan, artifact manifest, claim register, query and execution logs, outputs, environment, lane receipts, cited sources, and prior findings. Report missing evidence rather than filling gaps from memory. diff --git a/skills/science-review/references/index.json b/skills/science-review/references/index.json new file mode 100644 index 00000000..5c0efe76 --- /dev/null +++ b/skills/science-review/references/index.json @@ -0,0 +1,29 @@ +{ + "schema_version": 1, + "skill": "science-review", + "default_policy": "load-minimum-required", + "references": [ + { + "id": "review-checklist", + "path": "references/review-checklist.md", + "purpose": "Claim-level checks, severity definitions, finding fields, and the review pass rule.", + "read_when": [ + "assigning finding severity", + "reviewing a material claim", + "deciding whether a review passes" + ], + "required_before": [ + "finding-severity-assignment", + "review-pass-decision" + ], + "search_patterns": [ + "## Severity", + "## Claim-level checks", + "## Pass rule" + ], + "authority": "repository-contract", + "version": "1", + "evidence_boundary": "Defines review procedure; reviewer success does not establish scientific truth." + } + ] +} diff --git a/src/codex_science/advanced_sidecars.py b/src/codex_science/advanced_sidecars.py index 95e0e88f..2d23cf4e 100644 --- a/src/codex_science/advanced_sidecars.py +++ b/src/codex_science/advanced_sidecars.py @@ -5,13 +5,18 @@ from pathlib import Path from typing import Any +from codex_science.artifact_store import validate_descriptor from codex_science.collaboration import stale_annotations, validate_annotation from codex_science.evidence_graph_v2 import independent_support_groups, validate_graph_payload from codex_science.literature_v2 import validate_risk_of_bias from codex_science.model_registry_v2 import load_registry_v2, validate_model_receipt_v2 +from codex_science.reference_contract import validate_reference_use_ledger from codex_science.review_receipts import review_receipt_findings, validate_review_receipt -ADVANCED_KINDS = {"evidence-graph-v2", "review-receipt", "review-receipt-v2", "annotation", "model-receipt-v2", "risk-of-bias"} +ADVANCED_KINDS = { + "evidence-graph-v2", "review-receipt", "review-receipt-v2", "annotation", + "model-receipt-v2", "risk-of-bias", "reference-use-ledger", "artifact-descriptor", +} def _load(path: Path, label: str) -> dict[str, Any]: @@ -28,9 +33,20 @@ def validate_advanced_sidecars( manifest: dict[str, Any], run_dir: Path, verified: dict[str, Path], *, base_sidecars: dict[str, Any] | None = None ) -> dict[str, Any]: result: dict[str, Any] = { - "graph_v2": None, "graph_v2_nodes": {}, "graph_v2_edges": [], "graph_v2_findings": [], - "review_receipts": [], "annotations": [], "model_receipts_v2": [], "risk_of_bias": [], - "artifact_hashes": {str(item["path"]): str(item["sha256"]).lower() for item in manifest.get("artifacts", [])}, + "graph_v2": None, + "graph_v2_nodes": {}, + "graph_v2_edges": [], + "graph_v2_findings": [], + "review_receipts": [], + "annotations": [], + "model_receipts_v2": [], + "risk_of_bias": [], + "reference_use_ledgers": [], + "artifact_descriptors": [], + "artifact_hashes": { + str(item["path"]): str(item["sha256"]).lower() + for item in manifest.get("artifacts", []) + }, "advanced_paths": {}, } seen_graph = False @@ -39,10 +55,10 @@ def validate_advanced_sidecars( if kind not in ADVANCED_KINDS: continue path = verified[relative] + if not path.is_file(): + raise ValueError(f"advanced sidecar must be a file: {relative}") payload = _load(path, kind) if kind == "review-receipt" and not payload.get("review_id"): - # Legacy receipt from schema-v1 bundles: keep it hashed and navigable, - # but do not reinterpret it as the new hash-covered receipt contract. continue result["advanced_paths"].setdefault(kind, []).append(relative) if kind == "evidence-graph-v2": @@ -50,7 +66,12 @@ def validate_advanced_sidecars( raise ValueError("only one evidence-graph-v2 artifact is allowed") seen_graph = True nodes, edges, findings = validate_graph_payload(payload) - result.update({"graph_v2": payload, "graph_v2_nodes": nodes, "graph_v2_edges": edges, "graph_v2_findings": findings}) + result.update({ + "graph_v2": payload, + "graph_v2_nodes": nodes, + "graph_v2_edges": edges, + "graph_v2_findings": findings, + }) elif kind in {"review-receipt", "review-receipt-v2"}: validate_review_receipt(payload) result["review_receipts"].append(payload) @@ -62,6 +83,11 @@ def validate_advanced_sidecars( elif kind == "risk-of-bias": validate_risk_of_bias(payload) result["risk_of_bias"].append(payload) + elif kind == "reference-use-ledger": + validate_reference_use_ledger(payload) + result["reference_use_ledgers"].append(payload) + elif kind == "artifact-descriptor": + result["artifact_descriptors"].append(validate_descriptor(payload).to_dict()) result["annotations"] = stale_annotations(result["annotations"], result["artifact_hashes"]) if base_sidecars: result["base_sidecars"] = base_sidecars @@ -82,23 +108,45 @@ def review_advanced_sidecars(sidecars: dict[str, Any], *, registry_path: Path | findings.extend(review_receipt_findings(receipt, artifact_hashes, registry_sha256=registry_digest)) if receipt.get("status") != "passed": unresolved = [ - item for item in receipt.get("findings", []) + item + for item in receipt.get("findings", []) if item.get("resolution_status", "open") != "resolved" ] if unresolved: findings.extend(unresolved) else: - findings.append({"code": "review-receipt-not-passed", "severity": "major", "message": f"Review receipt {receipt['review_id']} is {receipt.get('status')}."}) + findings.append({ + "code": "review-receipt-not-passed", + "severity": "major", + "message": f"Review receipt {receipt['review_id']} is {receipt.get('status')}.", + }) for receipt in sidecars.get("model_receipts_v2", []): findings.extend(validate_model_receipt_v2(receipt, registry)) for annotation in sidecars.get("annotations", []): if annotation.get("status") == "stale-anchor": - findings.append({"code": "stale-annotation-anchor", "severity": "minor", "message": f"Annotation {annotation['annotation_id']} points to changed artifact bytes."}) + findings.append({ + "code": "stale-annotation-anchor", + "severity": "minor", + "message": f"Annotation {annotation['annotation_id']} points to changed artifact bytes.", + }) base = sidecars.get("base_sidecars", {}) claims = base.get("claim_by_id", {}) if isinstance(base, dict) else {} nodes, edges = sidecars.get("graph_v2_nodes", {}), sidecars.get("graph_v2_edges", []) for claim_id, claim in claims.items(): if claim.get("status") == "replicated" and len(independent_support_groups(claim_id, nodes, edges)) < 2: - findings.append({"code": "dependent-evidence-v2", "severity": "major", "claim_id": claim_id, "message": f"Claim {claim_id} is replicated without two independent evidence components."}) - unique = {(item["code"], item.get("claim_id", ""), item["message"]): item for item in findings} - return sorted(unique.values(), key=lambda item: (item.get("severity", ""), item["code"], item.get("claim_id", ""), item["message"])) + findings.append({ + "code": "dependent-evidence-v2", + "severity": "major", + "claim_id": claim_id, + "message": f"Claim {claim_id} is replicated without two independent evidence components.", + }) + unique = { + (item["code"], item.get("claim_id", ""), item["message"]): item + for item in findings + } + return sorted( + unique.values(), + key=lambda item: ( + item.get("severity", ""), item["code"], item.get("claim_id", ""), item["message"] + ), + ) diff --git a/src/codex_science/artifact_store.py b/src/codex_science/artifact_store.py new file mode 100644 index 00000000..a6b23ea0 --- /dev/null +++ b/src/codex_science/artifact_store.py @@ -0,0 +1,305 @@ +"""Streaming and Merkle-hashed descriptors for large scientific artifacts.""" +from __future__ import annotations + +import hashlib +import json +import os +import shutil +import tempfile +from dataclasses import asdict, dataclass +from pathlib import Path, PurePosixPath +from typing import Any, Iterator, Mapping + +DEFAULT_CHUNK_SIZE = 8 * 1024 * 1024 +ARTIFACT_TYPES = {"file", "chunked-file", "directory-tree", "external-reference"} + + +def _hex_sha256(value: Any, label: str) -> str: + text = str(value).lower().strip() + if len(text) != 64 or any(character not in "0123456789abcdef" for character in text): + raise ValueError(f"{label} must be a SHA-256 digest") + return text + + +def stream_sha256(path: Path, *, block_size: int = 1024 * 1024) -> tuple[str, int]: + if block_size <= 0: + raise ValueError("block_size must be positive") + digest = hashlib.sha256() + size = 0 + with path.open("rb") as handle: + while chunk := handle.read(block_size): + digest.update(chunk) + size += len(chunk) + return digest.hexdigest(), size + + +def iter_chunk_hashes(path: Path, *, chunk_size: int = DEFAULT_CHUNK_SIZE) -> Iterator[dict[str, Any]]: + if chunk_size <= 0: + raise ValueError("chunk_size must be positive") + offset = 0 + index = 0 + with path.open("rb") as handle: + while chunk := handle.read(chunk_size): + yield { + "index": index, + "offset": offset, + "size": len(chunk), + "sha256": hashlib.sha256(chunk).hexdigest(), + } + index += 1 + offset += len(chunk) + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _validate_relative(value: str) -> str: + pure = PurePosixPath(value) + if not value or pure.is_absolute() or ".." in pure.parts: + raise ValueError(f"artifact entry must be a safe relative path: {value}") + return pure.as_posix() + + +@dataclass(frozen=True) +class ArtifactDescriptor: + schema_version: int + artifact_type: str + root_sha256: str + total_bytes: int + entry_count: int + media_type: str | None + entries: tuple[dict[str, Any], ...] + external: dict[str, Any] | None = None + + def to_dict(self) -> dict[str, Any]: + payload = asdict(self) + payload["entries"] = [dict(item) for item in self.entries] + return payload + + +def describe_file( + path: Path, + *, + media_type: str | None = None, + chunk_size: int | None = None, +) -> ArtifactDescriptor: + if not path.is_file(): + raise ValueError(f"not a file: {path}") + digest, size = stream_sha256(path) + if chunk_size is None: + entries = ({"path": path.name, "size": size, "sha256": digest},) + artifact_type = "file" + else: + chunks = tuple(iter_chunk_hashes(path, chunk_size=chunk_size)) + entries = ( + { + "path": path.name, + "size": size, + "sha256": digest, + "chunk_size": chunk_size, + "chunks": list(chunks), + }, + ) + artifact_type = "chunked-file" + return ArtifactDescriptor(1, artifact_type, digest, size, 1, media_type, entries) + + +def _directory_entries(root: Path, *, follow_symlinks: bool) -> tuple[dict[str, Any], ...]: + entries: list[dict[str, Any]] = [] + for path in sorted(root.rglob("*"), key=lambda item: item.relative_to(root).as_posix()): + relative = path.relative_to(root).as_posix() + if path.is_symlink(): + if not follow_symlinks: + raise ValueError(f"symbolic links are forbidden in directory artifacts: {relative}") + target = path.resolve() + if not target.is_relative_to(root.resolve()): + raise ValueError(f"symbolic link escapes directory artifact: {relative}") + if path.is_dir(): + continue + if not path.is_file(): + raise ValueError(f"unsupported directory artifact entry: {relative}") + digest, size = stream_sha256(path) + entries.append({"path": _validate_relative(relative), "size": size, "sha256": digest}) + return tuple(entries) + + +def describe_directory( + root: Path, + *, + media_type: str | None = None, + follow_symlinks: bool = False, +) -> ArtifactDescriptor: + if not root.is_dir(): + raise ValueError(f"not a directory: {root}") + entries = _directory_entries(root, follow_symlinks=follow_symlinks) + material = {"algorithm": "sha256-merkle-v1", "entries": list(entries)} + root_digest = hashlib.sha256(_canonical_bytes(material)).hexdigest() + return ArtifactDescriptor( + 1, + "directory-tree", + root_digest, + sum(int(item["size"]) for item in entries), + len(entries), + media_type, + entries, + ) + + +def describe_external_reference( + *, + uri: str, + version: str, + sha256: str, + size_bytes: int | None = None, + media_type: str | None = None, + license: str | None = None, +) -> ArtifactDescriptor: + if not uri.strip() or not version.strip(): + raise ValueError("external reference requires uri and version") + digest = _hex_sha256(sha256, "sha256") + external = { + "uri": uri, + "version": version, + "sha256": digest, + "size_bytes": size_bytes, + "license": license, + } + return ArtifactDescriptor( + 1, + "external-reference", + digest, + int(size_bytes or 0), + 0, + media_type, + (), + external, + ) + + +def validate_descriptor(payload: Mapping[str, Any], path: Path | None = None) -> ArtifactDescriptor: + if payload.get("schema_version") != 1: + raise ValueError("unsupported artifact descriptor schema") + artifact_type = str(payload.get("artifact_type", "")) + if artifact_type not in ARTIFACT_TYPES: + raise ValueError(f"unsupported artifact type: {artifact_type}") + root_sha256 = _hex_sha256(payload.get("root_sha256"), "root_sha256") + total_bytes = int(payload.get("total_bytes", 0)) + entry_count = int(payload.get("entry_count", 0)) + entries_raw = payload.get("entries", []) + if not isinstance(entries_raw, (list, tuple)): + raise ValueError("entries must be a list") + entries = tuple(dict(item) for item in entries_raw if isinstance(item, Mapping)) + if len(entries) != len(entries_raw): + raise ValueError("every descriptor entry must be an object") + for item in entries: + _validate_relative(str(item.get("path", ""))) + _hex_sha256(item.get("sha256"), "entry sha256") + if int(item.get("size", -1)) < 0: + raise ValueError("entry size must be non-negative") + chunks = item.get("chunks") + if chunks is not None: + if not isinstance(chunks, list): + raise ValueError("chunks must be a list") + expected_offset = 0 + for index, chunk in enumerate(chunks): + if not isinstance(chunk, Mapping): + raise ValueError("chunk record must be an object") + if int(chunk.get("index", -1)) != index: + raise ValueError("chunk indexes must be contiguous") + if int(chunk.get("offset", -1)) != expected_offset: + raise ValueError("chunk offsets must be contiguous") + size = int(chunk.get("size", -1)) + if size <= 0: + raise ValueError("chunk size must be positive") + _hex_sha256(chunk.get("sha256"), "chunk sha256") + expected_offset += size + if expected_offset != int(item["size"]): + raise ValueError("chunk sizes do not sum to entry size") + descriptor = ArtifactDescriptor( + 1, + artifact_type, + root_sha256, + total_bytes, + entry_count, + None if payload.get("media_type") is None else str(payload.get("media_type")), + entries, + None if payload.get("external") is None else dict(payload.get("external")), + ) + if artifact_type == "external-reference": + external = descriptor.external + if not isinstance(external, dict): + raise ValueError("external-reference requires external metadata") + if _hex_sha256(external.get("sha256"), "external sha256") != root_sha256: + raise ValueError("external reference digest does not match root_sha256") + if not str(external.get("uri", "")).strip() or not str(external.get("version", "")).strip(): + raise ValueError("external reference requires uri and version") + if entries or entry_count != 0: + raise ValueError("external reference cannot contain local entries") + external_size = external.get("size_bytes") + if external_size is not None and int(external_size) != total_bytes: + raise ValueError("external reference size does not match total_bytes") + else: + if len(entries) != entry_count: + raise ValueError("entry_count does not match entries") + if sum(int(item.get("size", 0)) for item in entries) != total_bytes: + raise ValueError("total_bytes does not match entries") + if path is not None: + if artifact_type == "directory-tree": + actual = describe_directory(path, media_type=descriptor.media_type) + elif artifact_type in {"file", "chunked-file"}: + actual = describe_file( + path, + media_type=descriptor.media_type, + chunk_size=int(entries[0].get("chunk_size")) if artifact_type == "chunked-file" else None, + ) + else: + actual = descriptor + if actual.root_sha256 != descriptor.root_sha256 or actual.entries != descriptor.entries: + raise ValueError("artifact bytes do not match descriptor") + return descriptor + + +def write_descriptor(path: Path, descriptor: ArtifactDescriptor) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(descriptor.to_dict(), indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +class ContentAddressedStore: + def __init__(self, root: Path) -> None: + self.root = root + self.objects = root / "objects" / "sha256" + + def object_path(self, digest: str) -> Path: + digest = _hex_sha256(digest, "digest") + return self.objects / digest[:2] / digest[2:] + + def add_file(self, source: Path) -> tuple[str, Path]: + digest, _size = stream_sha256(source) + destination = self.object_path(digest) + if destination.is_file(): + existing, _ = stream_sha256(destination) + if existing != digest: + raise ValueError("content-addressed store corruption") + return digest, destination + destination.parent.mkdir(parents=True, exist_ok=True) + descriptor, temporary_name = tempfile.mkstemp(prefix=".incoming-", dir=destination.parent) + os.close(descriptor) + temporary = Path(temporary_name) + try: + shutil.copyfile(source, temporary) + copied, _ = stream_sha256(temporary) + if copied != digest: + raise ValueError("copied object digest mismatch") + temporary.chmod(0o444) + temporary.replace(destination) + finally: + temporary.unlink(missing_ok=True) + return digest, destination + + def verify(self, digest: str) -> bool: + path = self.object_path(digest) + if not path.is_file(): + return False + actual, _ = stream_sha256(path) + return actual == digest diff --git a/src/codex_science/artifacts.py b/src/codex_science/artifacts.py index a0afc038..7281947c 100644 --- a/src/codex_science/artifacts.py +++ b/src/codex_science/artifacts.py @@ -1,18 +1,35 @@ """Structured, reproducible analysis artifact manifests and bundle validation.""" from __future__ import annotations -import hashlib import json from pathlib import Path, PurePosixPath from typing import Any -REQUIRED_FIELDS = {"schema_version", "run_id", "question", "plan", "inputs", "code", "executions", "environment", "artifacts", "claims", "review"} +from codex_science.artifact_store import describe_directory, stream_sha256, validate_descriptor + +REQUIRED_FIELDS = { + "schema_version", "run_id", "question", "plan", "inputs", "code", + "executions", "environment", "artifacts", "claims", "review", +} +LOCAL_ARTIFACT_TYPES = {"file", "chunked-file", "directory-tree"} def new_manifest(run_id: str, question: str, plan: list[dict[str, Any]]) -> dict[str, Any]: if not run_id.strip() or not question.strip(): raise ValueError("run_id and question are required") - return {"schema_version": 1, "run_id": run_id, "question": question, "plan": plan, "inputs": [], "code": [], "executions": [], "environment": {}, "artifacts": [], "claims": [], "review": {"status": "pending", "findings": []}} + return { + "schema_version": 1, + "run_id": run_id, + "question": question, + "plan": plan, + "inputs": [], + "code": [], + "executions": [], + "environment": {}, + "artifacts": [], + "claims": [], + "review": {"status": "pending", "findings": []}, + } def _validate_relative_path(value: str) -> None: @@ -25,13 +42,46 @@ def _valid_sha256(value: str) -> bool: return len(value) == 64 and all(character in "0123456789abcdefABCDEF" for character in value) -def add_artifact(manifest: dict[str, Any], path: str, *, kind: str, sha256: str) -> None: +def add_artifact( + manifest: dict[str, Any], + path: str, + *, + kind: str, + sha256: str, + artifact_type: str = "file", + size_bytes: int | None = None, + entry_count: int | None = None, + media_type: str | None = None, + descriptor_path: str | None = None, +) -> None: _validate_relative_path(path) if not _valid_sha256(sha256): raise ValueError("sha256 must be a 64-character hexadecimal digest") if not kind.strip(): raise ValueError("artifact kind is required") - manifest["artifacts"].append({"path": path, "kind": kind, "sha256": sha256.lower()}) + if artifact_type not in LOCAL_ARTIFACT_TYPES: + raise ValueError(f"unsupported local artifact_type: {artifact_type}") + record: dict[str, Any] = { + "path": path, + "kind": kind, + "sha256": sha256.lower(), + } + if artifact_type != "file": + record["artifact_type"] = artifact_type + if size_bytes is not None: + if size_bytes < 0: + raise ValueError("size_bytes must be non-negative") + record["size_bytes"] = size_bytes + if entry_count is not None: + if entry_count < 0: + raise ValueError("entry_count must be non-negative") + record["entry_count"] = entry_count + if media_type is not None: + record["media_type"] = media_type + if descriptor_path is not None: + _validate_relative_path(descriptor_path) + record["descriptor_path"] = descriptor_path + manifest["artifacts"].append(record) def validate_manifest(manifest: dict[str, Any]) -> None: @@ -64,6 +114,14 @@ def validate_manifest(manifest: dict[str, Any]) -> None: raise ValueError(f"Artifact kind is required: {path}") if not _valid_sha256(str(artifact.get("sha256", ""))): raise ValueError("Invalid artifact sha256") + artifact_type = str(artifact.get("artifact_type", "file")) + if artifact_type not in LOCAL_ARTIFACT_TYPES: + raise ValueError(f"Unsupported local artifact_type: {artifact_type}") + for field in ("size_bytes", "entry_count"): + if field in artifact and (isinstance(artifact[field], bool) or int(artifact[field]) < 0): + raise ValueError(f"Artifact {field} must be a non-negative integer: {path}") + if "descriptor_path" in artifact: + _validate_relative_path(str(artifact["descriptor_path"])) claim_ids: set[str] = set() for claim in manifest.get("claims", []): if not isinstance(claim, dict): @@ -76,6 +134,21 @@ def validate_manifest(manifest: dict[str, Any]) -> None: claim_ids.add(claim_id) +def _validate_descriptor_path(record: dict[str, Any], run_dir: Path, target: Path) -> None: + descriptor_relative = record.get("descriptor_path") + if descriptor_relative is None: + return + descriptor_path = run_dir / str(descriptor_relative) + if not descriptor_path.is_file(): + raise ValueError(f"Artifact descriptor is missing: {descriptor_relative}") + payload = json.loads(descriptor_path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"Artifact descriptor must be an object: {descriptor_relative}") + descriptor = validate_descriptor(payload, target) + if descriptor.root_sha256 != str(record["sha256"]).lower(): + raise ValueError(f"Artifact descriptor root mismatch: {record['path']}") + + def verify_bundle_artifacts(manifest: dict[str, Any], run_dir: Path) -> dict[str, Path]: validate_manifest(manifest) resolved_run = run_dir.resolve() @@ -83,14 +156,30 @@ def verify_bundle_artifacts(manifest: dict[str, Any], run_dir: Path) -> dict[str for record in manifest["artifacts"]: relative = str(record["path"]) path = run_dir / relative - if not path.is_file(): + artifact_type = str(record.get("artifact_type", "file")) + if artifact_type == "directory-tree": + if not path.is_dir(): + raise ValueError(f"Artifact directory is missing: {relative}") + elif not path.is_file(): raise ValueError(f"Artifact file is missing: {relative}") resolved = path.resolve() if not resolved.is_relative_to(resolved_run): raise ValueError(f"Artifact resolves outside the run bundle: {relative}") - digest = hashlib.sha256(path.read_bytes()).hexdigest() + if artifact_type == "directory-tree": + descriptor = describe_directory(path, media_type=record.get("media_type")) + digest = descriptor.root_sha256 + size_bytes = descriptor.total_bytes + entry_count = descriptor.entry_count + else: + digest, size_bytes = stream_sha256(path) + entry_count = 1 if digest != str(record["sha256"]).lower(): raise ValueError(f"Artifact digest mismatch: {relative}") + if "size_bytes" in record and int(record["size_bytes"]) != size_bytes: + raise ValueError(f"Artifact size mismatch: {relative}") + if "entry_count" in record and int(record["entry_count"]) != entry_count: + raise ValueError(f"Artifact entry count mismatch: {relative}") + _validate_descriptor_path(record, run_dir, path) verified[relative] = path return verified diff --git a/src/codex_science/mcp_server.py b/src/codex_science/mcp_server.py index bf8ef740..5287c5ca 100644 --- a/src/codex_science/mcp_server.py +++ b/src/codex_science/mcp_server.py @@ -10,6 +10,8 @@ from codex_science.connector_contract import QueryRequest, execute_connector from codex_science.connector_sources import SOURCE_BY_KEY, SOURCE_BY_TOOL, SOURCE_SPECS from codex_science.life_science import plan_life_science_research +from codex_science.source_operations_v3 import SOURCE_OPERATIONS_V3 +from codex_science.transport_v3 import execute_paginated from codex_science.version import MCP_VERSION PROTOCOL_VERSION = "2025-06-18" @@ -30,7 +32,6 @@ } ) LEGACY_SOURCE_SPECS = tuple(spec for spec in SOURCE_SPECS if spec.tool_name in LEGACY_TOOL_NAMES) -# Public compatibility contract used by existing tests and downstream integrations. CONNECTOR_SPECS = tuple((spec.tool_name, spec.description, spec.factory) for spec in LEGACY_SOURCE_SPECS) @@ -54,7 +55,7 @@ def _legacy_tool(name: str, description: str) -> dict[str, Any]: def _v2_tool() -> dict[str, Any]: return { "name": "science_query_source_v2", - "description": "Execute a bounded typed public-source query and return normalized records plus a canonical replay receipt.", + "description": "Execute a bounded normalized public-source query and return records plus a canonical v2 receipt.", "inputSchema": { "type": "object", "properties": { @@ -73,10 +74,31 @@ def _v2_tool() -> dict[str, Any]: } +def _v3_tool() -> dict[str, Any]: + return { + "name": "science_query_source_v3", + "description": "Execute a true-paginated source operation with HTTP headers, retries, rate-limit state, page hashes, and explicit completeness semantics.", + "inputSchema": { + "type": "object", + "properties": { + "source": {"type": "string", "enum": sorted(SOURCE_OPERATIONS_V3)}, + "operation": {"type": "string", "enum": ["search"], "default": "search"}, + "parameters": {"type": "object", "additionalProperties": True}, + "page_size": {"type": "integer", "minimum": 1, "maximum": 100, "default": 25}, + "max_pages": {"type": "integer", "minimum": 1, "maximum": 100, "default": 1}, + "evidence_cutoff": {"type": ["string", "null"]}, + }, + "required": ["source", "parameters"], + "additionalProperties": False, + }, + "annotations": {"readOnlyHint": True, "destructiveHint": False, "openWorldHint": True}, + } + + def _contracts_tool() -> dict[str, Any]: return { "name": "science_list_source_contracts", - "description": "List public-source operations, query semantics, and maturity states.", + "description": "List public-source operations, query semantics, maturity states, and v3 transport availability.", "inputSchema": {"type": "object", "properties": {}, "additionalProperties": False}, "annotations": {"readOnlyHint": True, "destructiveHint": False, "openWorldHint": False}, } @@ -87,6 +109,7 @@ def _contracts_tool() -> dict[str, Any]: *(_legacy_tool(spec.tool_name, spec.description) for spec in SOURCE_SPECS), _legacy_tool("science_plan_life_science_research", "Plan bounded normalization, evidence lanes, provenance, and synthesis."), _v2_tool(), + _v3_tool(), _contracts_tool(), ) TOOL_NAMES = frozenset(tool["name"] for tool in TOOLS) @@ -95,9 +118,7 @@ def _contracts_tool() -> dict[str, Any]: class CodexScienceMCP: def __init__(self, inventory_path: Path) -> None: self.inventory_path = inventory_path - # Keep the historical tool-name-keyed registry stable. self.connectors = {name: factory() for name, _description, factory in CONNECTOR_SPECS} - # Use a distinct source-key registry for typed v2 and newly cataloged adapters. self.sources = {spec.key: spec.factory() for spec in SOURCE_SPECS} @staticmethod @@ -128,7 +149,7 @@ def handle(self, request: Any) -> dict[str, Any] | None: "protocolVersion": PROTOCOL_VERSION, "capabilities": {"tools": {"listChanged": False}}, "serverInfo": {"name": "codex-science", "version": MCP_VERSION}, - "instructions": "Read-only scientific catalog and public-source queries. Prefer science_query_source_v2 for material evidence and replay receipts.", + "instructions": "Read-only scientific catalog and public-source queries. Prefer science_query_source_v3 when available for material evidence; use v2 for sources not yet migrated.", }, ) if method == "ping": @@ -149,7 +170,22 @@ def _call_tool(self, request_id: Any, params: Any) -> dict[str, Any]: if name == "science_list_source_contracts": if arguments: raise ValueError("science_list_source_contracts takes no arguments") - payload = [spec.public_contract() for spec in SOURCE_SPECS] + payload = [] + for spec in SOURCE_SPECS: + record = spec.public_contract() + record["v3_operations"] = ["search"] if spec.key in SOURCE_OPERATIONS_V3 else [] + payload.append(record) + elif name == "science_query_source_v3": + extra = set(arguments) - { + "source", "operation", "parameters", "page_size", "max_pages", "evidence_cutoff", + } + if extra: + raise ValueError(f"Unexpected arguments: {', '.join(sorted(extra))}") + source = arguments.get("source") + if source not in SOURCE_OPERATIONS_V3: + raise ValueError(f"Source has no v3 operation: {source}") + request = QueryRequest.from_payload({**arguments, "source_contract_version": "3"}) + payload = execute_paginated(SOURCE_OPERATIONS_V3[str(source)], request).to_dict() elif name == "science_query_source_v2": extra = set(arguments) - { "source", "operation", "parameters", "page_size", "max_pages", diff --git a/src/codex_science/reference_contract.py b/src/codex_science/reference_contract.py new file mode 100644 index 00000000..c55ee62b --- /dev/null +++ b/src/codex_science/reference_contract.py @@ -0,0 +1,368 @@ +"""Progressive-disclosure reference contracts for scientific Agent Skills.""" +from __future__ import annotations + +import hashlib +import json +import re +from dataclasses import asdict, dataclass +from datetime import datetime, timezone +from pathlib import Path, PurePosixPath +from typing import Any, Iterable, Mapping + +INDEX_NAME = "index.json" +REFERENCE_SECTION_RE = re.compile(r"^##\s+Reference (?:usage|map)\s*$", re.I | re.M) +MARKDOWN_LINK_RE = re.compile(r"\[[^\]]+\]\((references/[^)]+)\)") + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + while chunk := handle.read(1024 * 1024): + digest.update(chunk) + return digest.hexdigest() + + +def _text(value: Any, label: str) -> str: + text = str(value).strip() + if not text: + raise ValueError(f"{label} is required") + return text + + +def _string_list(value: Any, label: str, *, required: bool = False) -> tuple[str, ...]: + if value is None and not required: + return () + if not isinstance(value, list): + raise ValueError(f"{label} must be a list") + result = tuple(_text(item, f"{label} item") for item in value) + if required and not result: + raise ValueError(f"{label} must be non-empty") + return result + + +@dataclass(frozen=True) +class ReferenceEntry: + id: str + path: str + purpose: str + read_when: tuple[str, ...] + required_before: tuple[str, ...] + search_patterns: tuple[str, ...] = () + authority: str = "repository-contract" + version: str = "1" + evidence_boundary: str = "" + + @classmethod + def from_payload(cls, payload: Mapping[str, Any], *, index: int) -> "ReferenceEntry": + path = _text(payload.get("path"), f"references[{index}].path") + pure = PurePosixPath(path) + if pure.is_absolute() or ".." in pure.parts or len(pure.parts) != 2 or pure.parts[0] != "references": + raise ValueError( + f"references[{index}].path must be one level below references/: {path}" + ) + if pure.name == INDEX_NAME: + raise ValueError("references/index.json cannot index itself") + return cls( + id=_text(payload.get("id"), f"references[{index}].id"), + path=path, + purpose=_text(payload.get("purpose"), f"references[{index}].purpose"), + read_when=_string_list(payload.get("read_when"), f"references[{index}].read_when", required=True), + required_before=_string_list( + payload.get("required_before"), + f"references[{index}].required_before", + required=True, + ), + search_patterns=_string_list( + payload.get("search_patterns"), f"references[{index}].search_patterns" + ), + authority=_text( + payload.get("authority", "repository-contract"), + f"references[{index}].authority", + ), + version=_text(payload.get("version", "1"), f"references[{index}].version"), + evidence_boundary=str(payload.get("evidence_boundary", "")).strip(), + ) + + def to_dict(self) -> dict[str, Any]: + payload = asdict(self) + for field in ("read_when", "required_before", "search_patterns"): + payload[field] = list(payload[field]) + return payload + + +@dataclass(frozen=True) +class ReferenceIndex: + schema_version: int + skill: str + references: tuple[ReferenceEntry, ...] + default_policy: str = "load-minimum-required" + + @classmethod + def from_payload(cls, payload: Mapping[str, Any]) -> "ReferenceIndex": + if payload.get("schema_version") != 1: + raise ValueError("unsupported reference index schema") + raw = payload.get("references") + if not isinstance(raw, list) or not raw: + raise ValueError("reference index requires a non-empty references list") + references = tuple( + ReferenceEntry.from_payload(item, index=index) + for index, item in enumerate(raw) + if isinstance(item, Mapping) + ) + if len(references) != len(raw): + raise ValueError("each reference index entry must be an object") + ids = [item.id for item in references] + paths = [item.path for item in references] + if len(ids) != len(set(ids)): + raise ValueError("reference IDs must be unique") + if len(paths) != len(set(paths)): + raise ValueError("reference paths must be unique") + return cls( + schema_version=1, + skill=_text(payload.get("skill"), "skill"), + references=references, + default_policy=_text( + payload.get("default_policy", "load-minimum-required"), "default_policy" + ), + ) + + def to_dict(self) -> dict[str, Any]: + return { + "schema_version": self.schema_version, + "skill": self.skill, + "default_policy": self.default_policy, + "references": [item.to_dict() for item in self.references], + } + + +@dataclass(frozen=True) +class ReferenceUseReceipt: + schema_version: int + skill: str + reference_id: str + path: str + sha256: str + read_reason: str + used_for: tuple[str, ...] + sections: tuple[str, ...] + search_terms: tuple[str, ...] + loaded_at: str + + def to_dict(self) -> dict[str, Any]: + payload = asdict(self) + for field in ("used_for", "sections", "search_terms"): + payload[field] = list(payload[field]) + return payload + + +def load_reference_index(skill_dir: Path) -> ReferenceIndex | None: + path = skill_dir / "references" / INDEX_NAME + if not path.is_file(): + return None + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError("reference index must be a JSON object") + return ReferenceIndex.from_payload(payload) + + +def validate_reference_index(skill_dir: Path, *, strict: bool = False) -> dict[str, Any]: + skill_path = skill_dir / "SKILL.md" + if not skill_path.is_file(): + raise ValueError(f"missing SKILL.md: {skill_dir}") + skill_text = skill_path.read_text(encoding="utf-8") + references_dir = skill_dir / "references" + files = sorted( + path + for path in references_dir.glob("*") + if path.is_file() and path.name != INDEX_NAME and not path.name.startswith(".") + ) if references_dir.is_dir() else [] + index = load_reference_index(skill_dir) + findings: list[dict[str, str]] = [] + if files and index is None: + findings.append({ + "code": "missing-reference-index", + "severity": "major" if strict else "minor", + "message": f"{skill_dir.name} has reference files but no references/index.json", + }) + return {"skill": skill_dir.name, "index": None, "findings": findings, "references": []} + if index is None: + return {"skill": skill_dir.name, "index": None, "findings": [], "references": []} + if index.skill != skill_dir.name: + findings.append({ + "code": "reference-skill-mismatch", + "severity": "major", + "message": f"reference index skill {index.skill} does not match {skill_dir.name}", + }) + if not REFERENCE_SECTION_RE.search(skill_text): + findings.append({ + "code": "missing-reference-usage-section", + "severity": "major", + "message": f"{skill_dir.name} must contain a '## Reference usage' or '## Reference map' section", + }) + linked = set(MARKDOWN_LINK_RE.findall(skill_text)) + indexed_paths = {item.path for item in index.references} + actual_paths = {path.relative_to(skill_dir).as_posix() for path in files} + for path in sorted(indexed_paths - actual_paths): + findings.append({ + "code": "missing-reference-file", + "severity": "major", + "message": f"indexed reference is missing: {path}", + }) + for path in sorted(actual_paths - indexed_paths): + findings.append({ + "code": "unindexed-reference-file", + "severity": "major" if strict else "minor", + "message": f"reference file is not indexed: {path}", + }) + for entry in index.references: + path = skill_dir / entry.path + if path.is_file() and len(path.read_text(encoding="utf-8")) > 12000 and not entry.search_patterns: + findings.append({ + "code": "large-reference-without-search-patterns", + "severity": "minor", + "message": f"large reference needs search_patterns: {entry.path}", + }) + if entry.path not in linked: + findings.append({ + "code": "reference-not-linked-from-skill", + "severity": "major" if strict else "minor", + "message": f"SKILL.md does not link indexed reference: {entry.path}", + }) + return { + "skill": skill_dir.name, + "index": index.to_dict(), + "findings": findings, + "references": [ + { + **entry.to_dict(), + "sha256": _sha256(skill_dir / entry.path) if (skill_dir / entry.path).is_file() else None, + } + for entry in index.references + ], + } + + +def select_references( + index: ReferenceIndex, + *, + route: str = "", + query: str = "", + limit: int | None = None, +) -> list[ReferenceEntry]: + tokens = set(re.findall(r"[a-z0-9_-]+", f"{route} {query}".lower())) + ranked: list[tuple[int, str, ReferenceEntry]] = [] + for entry in index.references: + haystack = " ".join( + [entry.id, entry.purpose, *entry.read_when, *entry.required_before, *entry.search_patterns] + ).lower() + score = sum(3 for token in tokens if token and token in haystack) + if route and any(route.lower() in item.lower() for item in entry.required_before): + score += 20 + if route and any(route.lower() in item.lower() for item in entry.read_when): + score += 10 + if not tokens: + score = 1 + if score > 0: + ranked.append((score, entry.id, entry)) + ranked.sort(key=lambda item: (-item[0], item[1])) + entries = [item[2] for item in ranked] + return entries if limit is None else entries[:limit] + + +def build_reference_use_receipt( + skill_dir: Path, + entry: ReferenceEntry, + *, + read_reason: str, + used_for: Iterable[str], + sections: Iterable[str] = (), + search_terms: Iterable[str] = (), + loaded_at: str | None = None, +) -> ReferenceUseReceipt: + path = skill_dir / entry.path + if not path.is_file(): + raise ValueError(f"reference file is missing: {entry.path}") + return ReferenceUseReceipt( + schema_version=1, + skill=skill_dir.name, + reference_id=entry.id, + path=entry.path, + sha256=_sha256(path), + read_reason=_text(read_reason, "read_reason"), + used_for=tuple(_text(item, "used_for item") for item in used_for), + sections=tuple(str(item).strip() for item in sections if str(item).strip()), + search_terms=tuple(str(item).strip() for item in search_terms if str(item).strip()), + loaded_at=loaded_at or _now(), + ) + + +def audit_reference_roots( + roots: Iterable[Path], + *, + strict_names: Iterable[str] = (), +) -> dict[str, Any]: + strict_set = set(strict_names) + records: list[dict[str, Any]] = [] + for root in roots: + if not root.is_dir(): + continue + for skill_dir in sorted(path for path in root.iterdir() if path.is_dir()): + if not (skill_dir / "SKILL.md").is_file(): + continue + records.append(validate_reference_index(skill_dir, strict=skill_dir.name in strict_set)) + findings = [ + {"skill": record["skill"], **finding} + for record in records + for finding in record["findings"] + ] + return { + "schema_version": 1, + "summary": { + "skills": len(records), + "indexed_skills": sum(record["index"] is not None for record in records), + "references": sum(len(record["references"]) for record in records), + "findings": len(findings), + "major_findings": sum(item["severity"] == "major" for item in findings), + }, + "skills": records, + "findings": findings, + } + + +def validate_reference_use_receipt(payload: Mapping[str, Any]) -> None: + if payload.get("schema_version") != 1: + raise ValueError("unsupported reference use receipt schema") + for field in ("skill", "reference_id", "path", "sha256", "read_reason", "loaded_at"): + _text(payload.get(field), field) + reference_path = PurePosixPath(str(payload["path"])) + if reference_path.is_absolute() or ".." in reference_path.parts or reference_path.parts[0] != "references": + raise ValueError("reference use receipt path must stay under references/") + digest = str(payload["sha256"]).lower() + if len(digest) != 64 or any(character not in "0123456789abcdef" for character in digest): + raise ValueError("reference use receipt sha256 is invalid") + for field in ("used_for", "sections", "search_terms"): + value = payload.get(field, []) + if not isinstance(value, (list, tuple)) or not all(isinstance(item, str) for item in value): + raise ValueError(f"reference use receipt {field} must be strings") + + +def validate_reference_use_ledger(payload: Mapping[str, Any]) -> None: + if payload.get("schema_version") != 1: + raise ValueError("unsupported reference use ledger schema") + _text(payload.get("skill"), "skill") + uses = payload.get("uses") + if not isinstance(uses, list) or not uses: + raise ValueError("reference use ledger requires a non-empty uses list") + seen: set[tuple[str, str]] = set() + for item in uses: + if not isinstance(item, Mapping): + raise ValueError("reference use ledger entries must be objects") + validate_reference_use_receipt(item) + key = (str(item["reference_id"]), str(item["sha256"])) + if key in seen: + raise ValueError(f"duplicate reference use receipt: {item['reference_id']}") + seen.add(key) diff --git a/src/codex_science/source_operations_v3.py b/src/codex_science/source_operations_v3.py new file mode 100644 index 00000000..d211dccf --- /dev/null +++ b/src/codex_science/source_operations_v3.py @@ -0,0 +1,239 @@ +"""Source-specific true-pagination operations for Connector Contract v3.""" +from __future__ import annotations + +import json +import urllib.parse +from dataclasses import dataclass +from typing import Any, Mapping + +from codex_science.connector_contract import QueryRequest +from codex_science.transport_v3 import PageRequest + + +def _query(request: QueryRequest) -> str: + value = request.parameters.get("query") + if not isinstance(value, str) or not value.strip(): + raise ValueError("parameters.query is required") + return value.strip() + + +def _integer(value: Any, label: str, *, minimum: int = 0) -> int: + result = int(value) + if result < minimum: + raise ValueError(f"{label} must be >= {minimum}") + return result + + +@dataclass(frozen=True) +class PubMedSearchV3: + source: str = "pubmed" + operation: str = "search" + + def first_request(self, request: QueryRequest) -> PageRequest: + params: dict[str, Any] = { + "db": "pubmed", + "term": _query(request), + "retmode": "json", + "retstart": 0, + "retmax": request.page_size, + "usehistory": "y", + } + if request.evidence_cutoff: + params.update({"datetype": "pdat", "maxdate": request.evidence_cutoff.replace("-", "/")}) + for key in ("mindate", "maxdate", "datetype", "sort"): + if request.parameters.get(key) not in (None, ""): + params[key] = request.parameters[key] + url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?" + urllib.parse.urlencode(params) + return PageRequest("GET", url, {}, cursor="0") + + def next_request(self, request: QueryRequest, page_request: PageRequest, payload: Any, page_index: int) -> PageRequest | None: + result = payload.get("esearchresult") + if not isinstance(result, dict): + raise ValueError("missing esearchresult") + count = _integer(result.get("count", 0), "count") + current = _integer(result.get("retstart", page_index * request.page_size), "retstart") + returned = len(result.get("idlist") or []) + next_start = current + returned + if returned == 0 or next_start >= count: + return None + parsed = urllib.parse.urlsplit(page_request.url) + params = dict(urllib.parse.parse_qsl(parsed.query, keep_blank_values=True)) + params["retstart"] = str(next_start) + url = urllib.parse.urlunsplit((parsed.scheme, parsed.netloc, parsed.path, urllib.parse.urlencode(params), parsed.fragment)) + return PageRequest("GET", url, {}, cursor=str(next_start)) + + def normalize(self, payload: Any) -> list[dict[str, Any]]: + result = payload.get("esearchresult") + if not isinstance(result, dict): + raise ValueError("missing esearchresult") + return [ + {"id": str(identifier), "title": "", "url": f"https://pubmed.ncbi.nlm.nih.gov/{identifier}/"} + for identifier in result.get("idlist", []) + ] + + def source_release(self, payload: Any, headers: Mapping[str, str]) -> str | None: + return headers.get("last-modified") or headers.get("date") + + +@dataclass(frozen=True) +class EuropePMCSearchV3: + source: str = "europepmc" + operation: str = "search" + + def first_request(self, request: QueryRequest) -> PageRequest: + query = _query(request) + if request.evidence_cutoff: + query = f"({query}) AND FIRST_PDATE:[* TO {request.evidence_cutoff}]" + params = { + "query": query, + "pageSize": request.page_size, + "format": "json", + "cursorMark": "*", + "resultType": str(request.parameters.get("result_type", "core")), + } + url = "https://www.ebi.ac.uk/europepmc/webservices/rest/search?" + urllib.parse.urlencode(params) + return PageRequest("GET", url, {}, cursor="*") + + def next_request(self, request: QueryRequest, page_request: PageRequest, payload: Any, page_index: int) -> PageRequest | None: + cursor = payload.get("nextCursorMark") + returned = payload.get("resultList", {}).get("result", []) if isinstance(payload.get("resultList"), dict) else [] + if not isinstance(cursor, str) or not cursor or not returned or cursor == page_request.cursor: + return None + parsed = urllib.parse.urlsplit(page_request.url) + params = dict(urllib.parse.parse_qsl(parsed.query, keep_blank_values=True)) + params["cursorMark"] = cursor + url = urllib.parse.urlunsplit((parsed.scheme, parsed.netloc, parsed.path, urllib.parse.urlencode(params), parsed.fragment)) + return PageRequest("GET", url, {}, cursor=cursor) + + def normalize(self, payload: Any) -> list[dict[str, Any]]: + result_list = payload.get("resultList") + if not isinstance(result_list, dict): + raise ValueError("missing resultList") + records = [] + for item in result_list.get("result", []): + identifier = str(item.get("id") or "") + source = str(item.get("source") or "") + if identifier: + records.append({ + "id": identifier, + "title": str(item.get("title") or identifier), + "source": source, + "doi": str(item.get("doi") or ""), + "publication_date": str(item.get("firstPublicationDate") or ""), + "url": f"https://europepmc.org/article/{source}/{identifier}", + }) + return records + + def source_release(self, payload: Any, headers: Mapping[str, str]) -> str | None: + return headers.get("last-modified") or headers.get("date") + + +@dataclass(frozen=True) +class ChEMBLMoleculeSearchV3: + source: str = "chembl" + operation: str = "search" + + def first_request(self, request: QueryRequest) -> PageRequest: + params = {"q": _query(request), "limit": request.page_size, "offset": 0} + url = "https://www.ebi.ac.uk/chembl/api/data/molecule/search.json?" + urllib.parse.urlencode(params) + return PageRequest("GET", url, {}, cursor="0") + + def next_request(self, request: QueryRequest, page_request: PageRequest, payload: Any, page_index: int) -> PageRequest | None: + page_meta = payload.get("page_meta") + if not isinstance(page_meta, dict): + raise ValueError("missing page_meta") + next_url = page_meta.get("next") + if not isinstance(next_url, str) or not next_url: + return None + if next_url.startswith("/"): + next_url = "https://www.ebi.ac.uk" + next_url + parsed = urllib.parse.urlsplit(next_url) + cursor = dict(urllib.parse.parse_qsl(parsed.query)).get("offset") + return PageRequest("GET", next_url, {}, cursor=cursor) + + def normalize(self, payload: Any) -> list[dict[str, Any]]: + molecules = payload.get("molecules") + if not isinstance(molecules, list): + raise ValueError("missing molecules") + records = [] + for item in molecules: + identifier = str(item.get("molecule_chembl_id") or "") + if not identifier: + continue + structures = item.get("molecule_structures") or {} + records.append({ + "id": identifier, + "title": str(item.get("pref_name") or identifier), + "canonical_smiles": str(structures.get("canonical_smiles") or ""), + "url": f"https://www.ebi.ac.uk/chembl/explore/compound/{identifier}", + }) + return records + + def source_release(self, payload: Any, headers: Mapping[str, str]) -> str | None: + return headers.get("last-modified") or headers.get("date") + + +@dataclass(frozen=True) +class PDBSearchV3: + source: str = "pdb" + operation: str = "search" + + def _body(self, request: QueryRequest, start: int) -> bytes: + payload = { + "query": {"type": "terminal", "service": "full_text", "parameters": {"value": _query(request)}}, + "return_type": "entry", + "request_options": {"paginate": {"start": start, "rows": request.page_size}}, + } + return json.dumps(payload, separators=(",", ":")).encode("utf-8") + + def first_request(self, request: QueryRequest) -> PageRequest: + return PageRequest( + "POST", + "https://search.rcsb.org/rcsbsearch/v2/query", + {"Content-Type": "application/json"}, + body=self._body(request, 0), + cursor="0", + ) + + def next_request(self, request: QueryRequest, page_request: PageRequest, payload: Any, page_index: int) -> PageRequest | None: + result_set = payload.get("result_set") + if not isinstance(result_set, list): + raise ValueError("missing result_set") + total = _integer(payload.get("total_count", len(result_set)), "total_count") + start = _integer(page_request.cursor or 0, "cursor") + next_start = start + len(result_set) + if not result_set or next_start >= total: + return None + return PageRequest( + "POST", + page_request.url, + {"Content-Type": "application/json"}, + body=self._body(request, next_start), + cursor=str(next_start), + ) + + def normalize(self, payload: Any) -> list[dict[str, Any]]: + result_set = payload.get("result_set") + if not isinstance(result_set, list): + raise ValueError("missing result_set") + return [ + { + "id": str(item["identifier"]), + "title": "", + "score": item.get("score"), + "url": f"https://www.rcsb.org/structure/{item['identifier']}", + } + for item in result_set + if item.get("identifier") + ] + + def source_release(self, payload: Any, headers: Mapping[str, str]) -> str | None: + return headers.get("last-modified") or headers.get("date") + + +SOURCE_OPERATIONS_V3 = { + "pubmed": PubMedSearchV3(), + "europepmc": EuropePMCSearchV3(), + "chembl": ChEMBLMoleculeSearchV3(), + "pdb": PDBSearchV3(), +} diff --git a/src/codex_science/transport_v3.py b/src/codex_science/transport_v3.py new file mode 100644 index 00000000..5b6a1c58 --- /dev/null +++ b/src/codex_science/transport_v3.py @@ -0,0 +1,426 @@ +"""Bounded HTTP transport, true pagination, and replay receipts for public science sources.""" +from __future__ import annotations + +import hashlib +import json +import time +import urllib.error +import urllib.parse +import urllib.request +from dataclasses import asdict, dataclass +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Mapping, Protocol + +from codex_science.connector_contract import QueryRequest, sha256_json + +SECRET_KEYS = {"authorization", "proxy-authorization", "x-api-key", "api-key", "token", "key"} +TERMINAL_STATUSES = { + "complete", + "bounded-by-user", + "negative", + "filtered-empty", + "partial-next-cursor", + "partial-rate-limit", + "source-unavailable", + "authentication-required", + "schema-drift", + "normalization-failed", +} + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _redact_url(url: str) -> str: + parsed = urllib.parse.urlsplit(url) + pairs = [] + for key, value in urllib.parse.parse_qsl(parsed.query, keep_blank_values=True): + pairs.append((key, "[REDACTED]" if key.lower() in SECRET_KEYS else value)) + return urllib.parse.urlunsplit( + (parsed.scheme, parsed.netloc, parsed.path, urllib.parse.urlencode(pairs), parsed.fragment) + ) + + +def _safe_headers(headers: Mapping[str, str]) -> dict[str, str]: + return { + str(key).lower(): "[REDACTED]" if str(key).lower() in SECRET_KEYS else str(value) + for key, value in headers.items() + } + + +@dataclass(frozen=True) +class TransportPolicy: + timeout_seconds: float = 30.0 + max_attempts: int = 3 + backoff_seconds: tuple[float, ...] = (0.0, 0.5, 1.5) + max_response_bytes: int = 50 * 1024 * 1024 + user_agent: str = "codex-science/0.4 (+https://github.com/eightmm/codex-science)" + + def validate(self) -> None: + if self.timeout_seconds <= 0 or self.max_attempts < 1 or self.max_response_bytes < 1: + raise ValueError("transport policy values must be positive") + if len(self.backoff_seconds) < self.max_attempts: + raise ValueError("backoff_seconds must cover every attempt") + + +@dataclass(frozen=True) +class PageRequest: + method: str + url: str + headers: dict[str, str] + body: bytes | None = None + cursor: str | None = None + + @property + def request_sha256(self) -> str: + material = { + "method": self.method.upper(), + "url": _redact_url(self.url), + "headers": _safe_headers(self.headers), + "body_sha256": None if self.body is None else hashlib.sha256(self.body).hexdigest(), + "cursor": self.cursor, + } + return sha256_json(material) + + +@dataclass(frozen=True) +class TransportResponse: + status_code: int + url: str + headers: dict[str, str] + body: bytes + media_type: str + retrieved_at: str + attempts: int + + @property + def body_sha256(self) -> str: + return hashlib.sha256(self.body).hexdigest() + + def json(self) -> Any: + return json.loads(self.body.decode("utf-8")) + + +class Transport(Protocol): + def send(self, request: PageRequest) -> TransportResponse: ... + + +class TransportError(RuntimeError): + def __init__(self, message: str, *, status_code: int | None = None, retryable: bool = False) -> None: + super().__init__(message) + self.status_code = status_code + self.retryable = retryable + + +class UrllibTransport: + def __init__(self, policy: TransportPolicy | None = None) -> None: + self.policy = policy or TransportPolicy() + self.policy.validate() + + def send(self, request: PageRequest) -> TransportResponse: + last_error: BaseException | None = None + for attempt in range(1, self.policy.max_attempts + 1): + delay = self.policy.backoff_seconds[attempt - 1] + if delay: + time.sleep(delay) + headers = {"User-Agent": self.policy.user_agent, "Accept": "application/json", **request.headers} + urllib_request = urllib.request.Request( + request.url, + data=request.body, + headers=headers, + method=request.method.upper(), + ) + try: + with urllib.request.urlopen(urllib_request, timeout=self.policy.timeout_seconds) as response: + body = response.read(self.policy.max_response_bytes + 1) + if len(body) > self.policy.max_response_bytes: + raise TransportError("response exceeded max_response_bytes") + response_headers = {str(key).lower(): str(value) for key, value in response.headers.items()} + media_type = response_headers.get("content-type", "application/octet-stream").split(";", 1)[0] + return TransportResponse( + status_code=int(response.status), + url=_redact_url(response.geturl()), + headers=_safe_headers(response_headers), + body=body, + media_type=media_type, + retrieved_at=_now(), + attempts=attempt, + ) + except urllib.error.HTTPError as error: + last_error = error + retryable = error.code == 429 or 500 <= error.code < 600 + if retryable and attempt < self.policy.max_attempts: + continue + raise TransportError( + f"HTTP {error.code} for {_redact_url(request.url)}", + status_code=error.code, + retryable=retryable, + ) from error + except (urllib.error.URLError, TimeoutError, OSError) as error: + last_error = error + if attempt < self.policy.max_attempts: + continue + raise TransportError( + f"transport failed for {_redact_url(request.url)}: {error}", + retryable=True, + ) from error + raise TransportError(f"transport failed: {last_error}", retryable=True) + + +class SourceOperation(Protocol): + source: str + operation: str + + def first_request(self, request: QueryRequest) -> PageRequest: ... + def next_request( + self, + request: QueryRequest, + page_request: PageRequest, + payload: Any, + page_index: int, + ) -> PageRequest | None: ... + def normalize(self, payload: Any) -> list[dict[str, Any]]: ... + def source_release(self, payload: Any, headers: Mapping[str, str]) -> str | None: ... + + +@dataclass(frozen=True) +class PageReceiptV3: + page_index: int + request_sha256: str + method: str + request_url: str + request_body_sha256: str | None + cursor_in: str | None + cursor_out: str | None + status_code: int + response_sha256: str + response_bytes: int + media_type: str + etag: str | None + last_modified: str | None + rate_limit_limit: str | None + rate_limit_remaining: str | None + rate_limit_reset: str | None + retrieved_at: str + attempts: int + normalized_record_count: int + snapshot_body_path: str | None + snapshot_metadata_path: str | None + + +@dataclass(frozen=True) +class QueryReceiptV3: + schema_version: int + query_id: str + canonical_request_sha256: str + source: str + operation: str + source_contract_version: str + source_release: str | None + status: str + completeness: str + requested_max_pages: int + fetched_pages: int + records_returned: int + returned_record_ids: tuple[str, ...] + normalized_records_sha256: str + missingness: str + warnings: tuple[str, ...] + pages: tuple[PageReceiptV3, ...] + + def to_dict(self) -> dict[str, Any]: + return asdict(self) + + +@dataclass(frozen=True) +class QueryResultV3: + request: QueryRequest + records: tuple[dict[str, Any], ...] + receipt: QueryReceiptV3 + + def to_dict(self) -> dict[str, Any]: + return {"records": [dict(item) for item in self.records], "receipt": self.receipt.to_dict()} + + +def _snapshot_page( + snapshot_dir: Path | None, + *, + query_id: str, + page_index: int, + page_request: PageRequest, + response: TransportResponse, +) -> tuple[str | None, str | None]: + if snapshot_dir is None: + return None, None + query_dir = snapshot_dir / query_id + query_dir.mkdir(parents=True, exist_ok=True) + body_name = f"page-{page_index:04d}.body" + meta_name = f"page-{page_index:04d}.json" + body_path = query_dir / body_name + meta_path = query_dir / meta_name + body_path.write_bytes(response.body) + metadata = { + "schema_version": 1, + "request": { + "method": page_request.method, + "url": _redact_url(page_request.url), + "headers": _safe_headers(page_request.headers), + "body_sha256": None if page_request.body is None else hashlib.sha256(page_request.body).hexdigest(), + "cursor": page_request.cursor, + "request_sha256": page_request.request_sha256, + }, + "response": { + "status_code": response.status_code, + "url": response.url, + "headers": response.headers, + "body_sha256": response.body_sha256, + "body_bytes": len(response.body), + "media_type": response.media_type, + "retrieved_at": response.retrieved_at, + "attempts": response.attempts, + "body_path": body_name, + }, + } + meta_path.write_text(json.dumps(metadata, indent=2, sort_keys=True) + "\n", encoding="utf-8") + return (body_path.relative_to(snapshot_dir).as_posix(), meta_path.relative_to(snapshot_dir).as_posix()) + + +def _status_from_error(error: TransportError) -> str: + if error.status_code in {401, 403}: + return "authentication-required" + if error.status_code == 429: + return "partial-rate-limit" + return "source-unavailable" + + +def execute_paginated( + operation: SourceOperation, + request: QueryRequest, + *, + transport: Transport | None = None, + snapshot_dir: Path | None = None, +) -> QueryResultV3: + request.validate() + if request.source != operation.source or request.operation != operation.operation: + raise ValueError( + f"operation mismatch: request={request.source}.{request.operation}, " + f"handler={operation.source}.{operation.operation}" + ) + transport = transport or UrllibTransport() + page_request = operation.first_request(request) + records: list[dict[str, Any]] = [] + receipts: list[PageReceiptV3] = [] + warnings: list[str] = [] + source_release: str | None = None + next_exists = False + terminal_status = "complete" + + for page_index in range(request.max_pages): + try: + response = transport.send(page_request) + except TransportError as error: + terminal_status = _status_from_error(error) + warnings.append(str(error)) + break + try: + payload = response.json() + except (UnicodeDecodeError, json.JSONDecodeError) as error: + raise ValueError(f"schema-drift: source returned invalid JSON on page {page_index}") from error + try: + page_records = operation.normalize(payload) + except (KeyError, TypeError, ValueError) as error: + raise ValueError(f"normalization-failed on page {page_index}: {error}") from error + if not isinstance(page_records, list) or not all(isinstance(item, dict) for item in page_records): + raise ValueError("normalization-failed: operation must return record objects") + next_request = operation.next_request(request, page_request, payload, page_index) + next_exists = next_request is not None + source_release = source_release or operation.source_release(payload, response.headers) + body_path, metadata_path = _snapshot_page( + snapshot_dir, + query_id=request.query_id, + page_index=page_index, + page_request=page_request, + response=response, + ) + receipts.append( + PageReceiptV3( + page_index=page_index, + request_sha256=page_request.request_sha256, + method=page_request.method.upper(), + request_url=_redact_url(page_request.url), + request_body_sha256=None if page_request.body is None else hashlib.sha256(page_request.body).hexdigest(), + cursor_in=page_request.cursor, + cursor_out=None if next_request is None else next_request.cursor, + status_code=response.status_code, + response_sha256=response.body_sha256, + response_bytes=len(response.body), + media_type=response.media_type, + etag=response.headers.get("etag"), + last_modified=response.headers.get("last-modified"), + rate_limit_limit=response.headers.get("x-ratelimit-limit"), + rate_limit_remaining=response.headers.get("x-ratelimit-remaining"), + rate_limit_reset=response.headers.get("x-ratelimit-reset"), + retrieved_at=response.retrieved_at, + attempts=response.attempts, + normalized_record_count=len(page_records), + snapshot_body_path=body_path, + snapshot_metadata_path=metadata_path, + ) + ) + records.extend(dict(item) for item in page_records) + if next_request is None: + next_exists = False + break + page_request = next_request + + if terminal_status == "complete": + if next_exists and len(receipts) >= request.max_pages: + terminal_status = "bounded-by-user" + elif not records: + terminal_status = "negative" + completeness = terminal_status + missingness = "negative" if not records and terminal_status in {"complete", "negative"} else ( + "unavailable" if terminal_status in {"source-unavailable", "authentication-required"} else + "partial" if terminal_status.startswith("partial") or terminal_status == "bounded-by-user" else + "observed" + ) + record_ids = tuple(str(item.get("id") or f"record-{index}") for index, item in enumerate(records)) + receipt = QueryReceiptV3( + schema_version=3, + query_id=request.query_id, + canonical_request_sha256=request.request_sha256, + source=request.source, + operation=request.operation, + source_contract_version="3", + source_release=source_release, + status=terminal_status, + completeness=completeness, + requested_max_pages=request.max_pages, + fetched_pages=len(receipts), + records_returned=len(records), + returned_record_ids=record_ids, + normalized_records_sha256=sha256_json(records), + missingness=missingness, + warnings=tuple(warnings), + pages=tuple(receipts), + ) + return QueryResultV3(request, tuple(records), receipt) + + +def replay_snapshot_directory(snapshot_dir: Path, query_id: str) -> list[dict[str, Any]]: + query_dir = snapshot_dir / query_id + if not query_dir.is_dir(): + raise ValueError(f"snapshot query directory not found: {query_id}") + pages: list[dict[str, Any]] = [] + for metadata_path in sorted(query_dir.glob("page-*.json")): + metadata = json.loads(metadata_path.read_text(encoding="utf-8")) + body_path = query_dir / metadata["response"]["body_path"] + body = body_path.read_bytes() + if hashlib.sha256(body).hexdigest() != metadata["response"]["body_sha256"]: + raise ValueError(f"snapshot body digest mismatch: {body_path.name}") + pages.append({"metadata": metadata, "payload": json.loads(body.decode("utf-8"))}) + if not pages: + raise ValueError(f"no snapshot pages found for query: {query_id}") + return pages diff --git a/src/codex_science/version.py b/src/codex_science/version.py index 28d0762a..4d59d756 100644 --- a/src/codex_science/version.py +++ b/src/codex_science/version.py @@ -1,8 +1,8 @@ """Single source of truth for Codex Science release identities.""" from __future__ import annotations -PACKAGE_VERSION = "0.3.0" -PLUGIN_VERSION = "0.3.0+codex.20260720000100" +PACKAGE_VERSION = "0.4.0" +PLUGIN_VERSION = "0.4.0+codex.20260721000100" MCP_VERSION = PACKAGE_VERSION RELEASE_SCHEMA_VERSION = 1 RUNTIME_AFFECTING_PREFIXES = ( diff --git a/tests/test_artifact_store.py b/tests/test_artifact_store.py new file mode 100644 index 00000000..5f62bd84 --- /dev/null +++ b/tests/test_artifact_store.py @@ -0,0 +1,93 @@ +import os +import tempfile +import unittest +from pathlib import Path + +from codex_science.artifact_store import ( + ContentAddressedStore, + describe_directory, + describe_external_reference, + describe_file, + validate_descriptor, +) +from codex_science.artifacts import add_artifact, new_manifest, verify_bundle_artifacts + + +class ArtifactStoreTests(unittest.TestCase): + def test_streaming_chunks_and_merkle_directory_are_stable(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) / "dataset" + root.mkdir() + (root / "a.bin").write_bytes(b"abcdef") + (root / "nested").mkdir() + (root / "nested" / "b.bin").write_bytes(b"ghijkl") + first = describe_directory(root, media_type="application/x-directory") + second = describe_directory(root, media_type="application/x-directory") + self.assertEqual(first.root_sha256, second.root_sha256) + self.assertEqual(2, first.entry_count) + validate_descriptor(first.to_dict(), root) + + chunked = describe_file(root / "a.bin", chunk_size=2) + self.assertEqual(3, len(chunked.entries[0]["chunks"])) + validate_descriptor(chunked.to_dict(), root / "a.bin") + + def test_manifest_validates_directory_without_whole_file_reads(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + run = Path(tempdir) + dataset = run / "trajectory" + dataset.mkdir() + (dataset / "frame-0001.bin").write_bytes(b"frame-one") + descriptor = describe_directory(dataset) + manifest = new_manifest("run-1", "Validate a directory artifact", []) + add_artifact( + manifest, + "trajectory", + kind="trajectory", + sha256=descriptor.root_sha256, + artifact_type="directory-tree", + size_bytes=descriptor.total_bytes, + entry_count=descriptor.entry_count, + ) + verified = verify_bundle_artifacts(manifest, run) + self.assertEqual(dataset, verified["trajectory"]) + (dataset / "frame-0001.bin").write_bytes(b"changed") + with self.assertRaisesRegex(ValueError, "digest mismatch"): + verify_bundle_artifacts(manifest, run) + + + def test_external_reference_preserves_immutable_identity(self) -> None: + descriptor = describe_external_reference( + uri="s3://private-bucket/object", + version="version-7", + sha256="a" * 64, + size_bytes=123, + media_type="application/octet-stream", + license="internal-restricted", + ) + validated = validate_descriptor(descriptor.to_dict()) + self.assertEqual("external-reference", validated.artifact_type) + self.assertEqual(123, validated.total_bytes) + + def test_symlink_escape_is_rejected_and_store_is_content_addressed(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + source = root / "source.bin" + source.write_bytes(b"payload") + store = ContentAddressedStore(root / "cache") + digest, first = store.add_file(source) + digest2, second = store.add_file(source) + self.assertEqual((digest, first), (digest2, second)) + self.assertTrue(store.verify(digest)) + + directory = root / "dir" + directory.mkdir() + try: + os.symlink(source, directory / "escape") + except (OSError, NotImplementedError): + return + with self.assertRaisesRegex(ValueError, "symbolic links"): + describe_directory(directory) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_mcp_v3.py b/tests/test_mcp_v3.py new file mode 100644 index 00000000..62d6eef2 --- /dev/null +++ b/tests/test_mcp_v3.py @@ -0,0 +1,45 @@ +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.mcp_server import CodexScienceMCP + + +class MCPV3Tests(unittest.TestCase): + def test_tool_and_source_contract_advertise_true_pagination(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + inventory = Path(tempdir) / "inventory.json" + inventory.write_text( + json.dumps( + { + "schema_version": 1, + "source": {"commit": "fixture"}, + "summary": {"total": 0, "active": 0, "inactive": 0}, + "skills": [], + } + ), + encoding="utf-8", + ) + server = CodexScienceMCP(inventory) + listed = server.handle({"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}) + names = {item["name"] for item in listed["result"]["tools"]} + self.assertIn("science_query_source_v3", names) + + contracts = server.handle( + { + "jsonrpc": "2.0", + "id": 2, + "method": "tools/call", + "params": {"name": "science_list_source_contracts", "arguments": {}}, + } + ) + payload = json.loads(contracts["result"]["content"][0]["text"]) + by_source = {item["source"]: item for item in payload} + for source in ("pubmed", "europepmc", "chembl", "pdb"): + self.assertEqual(["search"], by_source[source]["v3_operations"]) + self.assertEqual([], by_source["gnomad"]["v3_operations"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_reference_contract.py b/tests/test_reference_contract.py new file mode 100644 index 00000000..7b713e89 --- /dev/null +++ b/tests/test_reference_contract.py @@ -0,0 +1,87 @@ +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.reference_contract import ( + audit_reference_roots, + build_reference_use_receipt, + load_reference_index, + select_references, + validate_reference_index, + validate_reference_use_ledger, +) + + +class ReferenceContractTests(unittest.TestCase): + def _skill(self, root: Path) -> Path: + skill = root / "literature-review" + references = skill / "references" + references.mkdir(parents=True) + (skill / "SKILL.md").write_text( + "---\nname: literature-review\ndescription: test\n---\n" + "## Reference usage\n" + "Read [study identity](references/study-identity.md) before deduplication.\n", + encoding="utf-8", + ) + (references / "study-identity.md").write_text( + "# Study identity\n\nUse namespaced persistent identifiers.\n", encoding="utf-8" + ) + (references / "index.json").write_text( + json.dumps( + { + "schema_version": 1, + "skill": "literature-review", + "references": [ + { + "id": "study-identity", + "path": "references/study-identity.md", + "purpose": "Resolve publication versions into study families.", + "read_when": ["deduplicating studies", "resolving corrections"], + "required_before": ["study-deduplication"], + "search_patterns": ["namespaced persistent identifiers"], + } + ], + } + ), + encoding="utf-8", + ) + return skill + + def test_index_selection_and_use_receipt(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + skill = self._skill(Path(tempdir)) + report = validate_reference_index(skill, strict=True) + self.assertEqual([], report["findings"]) + index = load_reference_index(skill) + assert index is not None + selected = select_references(index, route="study-deduplication") + self.assertEqual(["study-identity"], [item.id for item in selected]) + receipt = build_reference_use_receipt( + skill, + selected[0], + read_reason="study-deduplication", + used_for=["claim-C1"], + sections=["Study identity"], + ) + ledger = {"schema_version": 1, "skill": skill.name, "uses": [receipt.to_dict()]} + validate_reference_use_ledger(ledger) + + def test_unindexed_reference_is_reported(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + skill = self._skill(Path(tempdir)) + (skill / "references" / "extra.md").write_text("# Extra\n", encoding="utf-8") + report = validate_reference_index(skill, strict=True) + self.assertIn("unindexed-reference-file", {item["code"] for item in report["findings"]}) + + def test_root_audit_only_blocks_strict_major_findings(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) / "authored-skills" + skill = self._skill(root) + report = audit_reference_roots([root], strict_names=[skill.name]) + self.assertEqual(0, report["summary"]["major_findings"]) + self.assertEqual(1, report["summary"]["indexed_skills"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_transport_v3.py b/tests/test_transport_v3.py new file mode 100644 index 00000000..c3a93e29 --- /dev/null +++ b/tests/test_transport_v3.py @@ -0,0 +1,85 @@ +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.connector_contract import QueryRequest +from codex_science.source_operations_v3 import EuropePMCSearchV3, PubMedSearchV3 +from codex_science.transport_v3 import ( + PageRequest, + TransportError, + TransportResponse, + execute_paginated, + replay_snapshot_directory, +) + + +class FakeTransport: + def __init__(self, responses): + self.responses = list(responses) + self.requests = [] + + def send(self, request: PageRequest) -> TransportResponse: + self.requests.append(request) + response = self.responses.pop(0) + if isinstance(response, Exception): + raise response + payload, headers = response + return TransportResponse( + 200, + request.url, + headers, + json.dumps(payload, separators=(",", ":")).encode("utf-8"), + "application/json", + "2026-07-20T00:00:00Z", + 1, + ) + + +class TransportV3Tests(unittest.TestCase): + def test_pubmed_fetches_all_pages_and_replays_raw_bodies(self) -> None: + request = QueryRequest( + "pubmed", "search", {"query": "protein folding"}, + page_size=2, max_pages=3, source_contract_version="3", + ) + transport = FakeTransport( + [ + ({"esearchresult": {"count": "3", "retstart": "0", "idlist": ["1", "2"]}}, {"etag": '"one"', "last-modified": "date"}), + ({"esearchresult": {"count": "3", "retstart": "2", "idlist": ["3"]}}, {"etag": '"two"'}), + ] + ) + with tempfile.TemporaryDirectory() as tempdir: + snapshot_dir = Path(tempdir) + result = execute_paginated(PubMedSearchV3(), request, transport=transport, snapshot_dir=snapshot_dir) + self.assertEqual("complete", result.receipt.status) + self.assertEqual(["1", "2", "3"], [item["id"] for item in result.records]) + self.assertEqual('"one"', result.receipt.pages[0].etag) + self.assertEqual(2, len(replay_snapshot_directory(snapshot_dir, request.query_id))) + + def test_user_page_bound_is_not_source_completion(self) -> None: + request = QueryRequest( + "europepmc", "search", {"query": "BRCA1"}, + page_size=1, max_pages=1, source_contract_version="3", + ) + transport = FakeTransport( + [({"nextCursorMark": "cursor-2", "resultList": {"result": [{"id": "1", "source": "MED"}]}}, {})] + ) + result = execute_paginated(EuropePMCSearchV3(), request, transport=transport) + self.assertEqual("bounded-by-user", result.receipt.status) + self.assertEqual("partial", result.receipt.missingness) + + def test_error_statuses_remain_distinct(self) -> None: + request = QueryRequest( + "pubmed", "search", {"query": "x"}, source_contract_version="3" + ) + result = execute_paginated( + PubMedSearchV3(), request, + transport=FakeTransport([TransportError("rate", status_code=429, retryable=True)]), + ) + self.assertEqual("partial-rate-limit", result.receipt.status) + self.assertEqual("partial", result.receipt.missingness) + self.assertEqual(0, result.receipt.fetched_pages) + + +if __name__ == "__main__": + unittest.main() diff --git a/uv.lock b/uv.lock index a5abc83b..4df1b756 100644 --- a/uv.lock +++ b/uv.lock @@ -4,5 +4,5 @@ requires-python = ">=3.11" [[package]] name = "codex-science" -version = "0.3.0" +version = "0.4.0" source = { editable = "." } From 599e0a30fb8bce228f9acb2773c88423b8a41a63 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:50:25 +0900 Subject: [PATCH 04/87] Add reference runtime stage diagnostics --- .../reference-runtime-diagnostics.yml | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/reference-runtime-diagnostics.yml diff --git a/.github/workflows/reference-runtime-diagnostics.yml b/.github/workflows/reference-runtime-diagnostics.yml new file mode 100644 index 00000000..7621b1fc --- /dev/null +++ b/.github/workflows/reference-runtime-diagnostics.yml @@ -0,0 +1,42 @@ +name: Reference runtime diagnostics + +on: + pull_request: + +permissions: + contents: read + +jobs: + stage: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: compile + command: ./scripts/check.sh compile + - name: tests + command: ./scripts/check.sh tests + - name: inventory + command: ./scripts/check.sh inventory + - name: wrappers + command: ./scripts/check.sh wrappers + - name: reference-audit + command: uv run python scripts/audit_skill_references.py --require-clean + - name: connector-v3 + command: uv run python scripts/validate_connector_contracts.py + - name: scientific-contracts + command: ./scripts/check.sh contracts + - name: skill-validation + command: ./scripts/check.sh skills + - name: candidate-contract + command: uv run python scripts/candidate_contract_check.py + name: ${{ matrix.name }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + submodules: recursive + - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - run: ${{ matrix.command }} From 689f24f4f8c6d0436033850e51acd3c3b876e615 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:52:24 +0900 Subject: [PATCH 05/87] Capture reference audit findings --- .github/workflows/reference-audit-report.yml | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/reference-audit-report.yml diff --git a/.github/workflows/reference-audit-report.yml b/.github/workflows/reference-audit-report.yml new file mode 100644 index 00000000..576dfc0b --- /dev/null +++ b/.github/workflows/reference-audit-report.yml @@ -0,0 +1,30 @@ +name: Reference audit report + +on: + pull_request: + +permissions: + contents: write + +jobs: + report: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref }} + submodules: recursive + fetch-depth: 0 + - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - run: | + mkdir -p .ci + uv run python scripts/audit_skill_references.py --output .ci/reference-audit.json + - run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add .ci/reference-audit.json + git commit -m "Record reference audit diagnostics" + git push origin HEAD:${GITHUB_HEAD_REF} From e9a16a63f778f4ced722ee7738920540e118b499 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 00:52:38 +0000 Subject: [PATCH 06/87] Record reference audit diagnostics --- .ci/reference-audit.json | 1426 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1426 insertions(+) create mode 100644 .ci/reference-audit.json diff --git a/.ci/reference-audit.json b/.ci/reference-audit.json new file mode 100644 index 00000000..5c3e0320 --- /dev/null +++ b/.ci/reference-audit.json @@ -0,0 +1,1426 @@ +{ + "findings": [ + { + "code": "unindexed-reference-file", + "message": "reference file is not indexed: references/catalog-policy.md", + "severity": "major", + "skill": "codex-science" + }, + { + "code": "missing-reference-index", + "message": "spectroscopy-spectral-inference has reference files but no references/index.json", + "severity": "minor", + "skill": "spectroscopy-spectral-inference" + } + ], + "schema_version": 1, + "skills": [ + { + "findings": [ + { + "code": "unindexed-reference-file", + "message": "reference file is not indexed: references/catalog-policy.md", + "severity": "major" + } + ], + "index": { + "default_policy": "load-minimum-required", + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Controls procedural grounding; it is not scientific evidence.", + "id": "reference-loading", + "path": "references/reference-loading.md", + "purpose": "Select, load, and receipt detailed skill references without flooding context or guessing undocumented arguments.", + "read_when": [ + "a selected skill contains references/index.json", + "a route depends on detailed source, command, benchmark, or backend semantics" + ], + "required_before": [ + "selected-skill-reference-loading", + "material-reference-controlled-operation" + ], + "search_patterns": [ + "## 2. Load the minimum required material", + "## 3. Read before acting", + "## 5. Record material use" + ], + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Controls task lifecycle only; it does not attest scientific completion.", + "id": "goal-loop-contract", + "path": "references/goal-loop-contract.md", + "purpose": "Operate native Goal mode and checkpoint lifecycle without creating conflicting continuation loops.", + "read_when": [ + "starting or resuming a native Goal run", + "explaining completion_pending or Goal confirmation" + ], + "required_before": [ + "native-goal-start", + "native-goal-resume", + "native-goal-completion" + ], + "search_patterns": [ + "completion_pending", + "confirm-goal-complete", + "Stop" + ], + "version": "4" + } + ], + "schema_version": 1, + "skill": "codex-science" + }, + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Controls procedural grounding; it is not scientific evidence.", + "id": "reference-loading", + "path": "references/reference-loading.md", + "purpose": "Select, load, and receipt detailed skill references without flooding context or guessing undocumented arguments.", + "read_when": [ + "a selected skill contains references/index.json", + "a route depends on detailed source, command, benchmark, or backend semantics" + ], + "required_before": [ + "selected-skill-reference-loading", + "material-reference-controlled-operation" + ], + "search_patterns": [ + "## 2. Load the minimum required material", + "## 3. Read before acting", + "## 5. Record material use" + ], + "sha256": "a953ca5ec7b5535e83f9c2e9a24f285dbacd7579a7afd4aa2b6a9288dc30f2f4", + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Controls task lifecycle only; it does not attest scientific completion.", + "id": "goal-loop-contract", + "path": "references/goal-loop-contract.md", + "purpose": "Operate native Goal mode and checkpoint lifecycle without creating conflicting continuation loops.", + "read_when": [ + "starting or resuming a native Goal run", + "explaining completion_pending or Goal confirmation" + ], + "required_before": [ + "native-goal-start", + "native-goal-resume", + "native-goal-completion" + ], + "search_patterns": [ + "completion_pending", + "confirm-goal-complete", + "Stop" + ], + "sha256": "557400c3ea98c127c349b65d1cf5d04858b82580b19c9cab62056742eba76570", + "version": "4" + } + ], + "skill": "codex-science" + }, + { + "findings": [], + "index": { + "default_policy": "load-minimum-required", + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Defines packaging and validation; it does not validate domain-specific scientific claims.", + "id": "artifact-contract", + "path": "references/artifact-contract.md", + "purpose": "Manifest fields, validated sidecars, evidence links, versioning, and review invalidation.", + "read_when": [ + "creating or changing a manifest", + "adding a sidecar", + "reviewing bundle versioning or invalidation" + ], + "required_before": [ + "manifest-authoring", + "sidecar-authoring", + "bundle-versioning" + ], + "search_patterns": [ + "## Validated sidecars", + "## Bundle validation", + "## Versioning and invalidation" + ], + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Validates artifact identity and transfer semantics, not the scientific meaning of the artifact.", + "id": "large-artifacts", + "path": "references/large-artifacts.md", + "purpose": "Streaming, chunked, Merkle-tree, external-reference, and content-addressed handling for large scientific outputs.", + "read_when": [ + "an artifact is a directory", + "a file is too large for whole-file memory loading", + "a run uses trajectories, AnnData, BAM/CRAM, Zarr, Parquet, weights, or remote immutable objects" + ], + "required_before": [ + "large-artifact-registration", + "directory-artifact-registration", + "content-addressed-cache-insertion" + ], + "search_patterns": [ + "## Artifact types", + "## Content-addressed cache", + "## Validation and failure semantics" + ], + "version": "1" + } + ], + "schema_version": 1, + "skill": "science-provenance" + }, + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Defines packaging and validation; it does not validate domain-specific scientific claims.", + "id": "artifact-contract", + "path": "references/artifact-contract.md", + "purpose": "Manifest fields, validated sidecars, evidence links, versioning, and review invalidation.", + "read_when": [ + "creating or changing a manifest", + "adding a sidecar", + "reviewing bundle versioning or invalidation" + ], + "required_before": [ + "manifest-authoring", + "sidecar-authoring", + "bundle-versioning" + ], + "search_patterns": [ + "## Validated sidecars", + "## Bundle validation", + "## Versioning and invalidation" + ], + "sha256": "b762bdd86796642e0b098091c043efef750d0462761e09be45996d111897c73a", + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Validates artifact identity and transfer semantics, not the scientific meaning of the artifact.", + "id": "large-artifacts", + "path": "references/large-artifacts.md", + "purpose": "Streaming, chunked, Merkle-tree, external-reference, and content-addressed handling for large scientific outputs.", + "read_when": [ + "an artifact is a directory", + "a file is too large for whole-file memory loading", + "a run uses trajectories, AnnData, BAM/CRAM, Zarr, Parquet, weights, or remote immutable objects" + ], + "required_before": [ + "large-artifact-registration", + "directory-artifact-registration", + "content-addressed-cache-insertion" + ], + "search_patterns": [ + "## Artifact types", + "## Content-addressed cache", + "## Validation and failure semantics" + ], + "sha256": "f24b550a570aa343b25df92fce965de893670b681483a346844157e948947afb", + "version": "1" + } + ], + "skill": "science-provenance" + }, + { + "findings": [], + "index": { + "default_policy": "load-minimum-required", + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Defines review procedure; reviewer success does not establish scientific truth.", + "id": "review-checklist", + "path": "references/review-checklist.md", + "purpose": "Claim-level checks, severity definitions, finding fields, and the review pass rule.", + "read_when": [ + "assigning finding severity", + "reviewing a material claim", + "deciding whether a review passes" + ], + "required_before": [ + "finding-severity-assignment", + "review-pass-decision" + ], + "search_patterns": [ + "## Severity", + "## Claim-level checks", + "## Pass rule" + ], + "version": "1" + } + ], + "schema_version": 1, + "skill": "science-review" + }, + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Defines review procedure; reviewer success does not establish scientific truth.", + "id": "review-checklist", + "path": "references/review-checklist.md", + "purpose": "Claim-level checks, severity definitions, finding fields, and the review pass rule.", + "read_when": [ + "assigning finding severity", + "reviewing a material claim", + "deciding whether a review passes" + ], + "required_before": [ + "finding-severity-assignment", + "review-pass-decision" + ], + "search_patterns": [ + "## Severity", + "## Claim-level checks", + "## Pass rule" + ], + "sha256": "9b3957c88263433c16af8df46a4de29d186d8bc173b233767cc79167d6e73c46", + "version": "1" + } + ], + "skill": "science-review" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "alphafold-structure-analysis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "alphafold2-structure-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "alphafold3-structure-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "alphagenome-variant-analysis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "arxiv-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "asymptotic-perturbation-methods" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "autodock-vina-docking" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "bgee-expression-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "bindcraft-binder-design" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "biomedical-entity-normalization" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "biomedical-evidence-reconciliation" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "biorxiv-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "biostudies-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "boltz-structure-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "borzoi-regulatory-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "cancer-genomics-evidence" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "cbioportal-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "chai1-structure-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "chaos-nonlinear-dynamics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "chebi-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "chembl-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "chemical-structure-elucidation" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "chromatography-quantification" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "classical-mechanics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "clinical-trials-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "clinvar-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "complex-fourier-analysis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "computational-physics-validation" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "compute-environment" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "condensed-matter-solid-state" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "continuum-mechanics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "control-dynamical-systems" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "dbsnp-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "diffdock-docking" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "dimensional-analysis-units" + }, + { + "findings": [], + "index": { + "default_policy": "load-minimum-required", + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Defines benchmark validity; it does not show that a docking engine performs well.", + "id": "benchmark-design", + "path": "references/benchmark-design.md", + "purpose": "Define state manifests, information boundaries, leakage-aware splits, controls, and prediction units.", + "read_when": [ + "building a docking benchmark", + "defining a pocket", + "creating train/test splits" + ], + "required_before": [ + "benchmark-construction", + "pocket-definition", + "split-construction" + ], + "search_patterns": [ + "## Information boundary", + "## Splits", + "## Controls" + ], + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Metric definitions do not validate data splits, state preparation, or biological claims.", + "id": "metric-and-acceptance", + "path": "references/metric-and-acceptance.md", + "purpose": "Prespecify pose, screening, calibration, uncertainty, threshold, and failure metrics.", + "read_when": [ + "computing docking metrics", + "setting acceptance thresholds", + "calibrating scores" + ], + "required_before": [ + "metric-computation", + "acceptance-decision", + "score-calibration" + ], + "search_patterns": [ + "## Pose metrics", + "## Screening metrics", + "## Thresholds" + ], + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Provides inference limits and does not supply assay or mechanistic evidence.", + "id": "inference-boundaries", + "path": "references/inference-boundaries.md", + "purpose": "Keep pose, confidence, ranking, affinity, mechanism, and translational claims distinct.", + "read_when": [ + "writing docking conclusions", + "claiming affinity or mechanism", + "defining applicability or abstention" + ], + "required_before": [ + "docking-claim-synthesis", + "affinity-claim", + "mechanism-claim" + ], + "search_patterns": [ + "## Distinct outputs", + "## Required external evidence", + "## Claim downgrade" + ], + "version": "1" + } + ], + "schema_version": 1, + "skill": "docking-validation" + }, + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Defines benchmark validity; it does not show that a docking engine performs well.", + "id": "benchmark-design", + "path": "references/benchmark-design.md", + "purpose": "Define state manifests, information boundaries, leakage-aware splits, controls, and prediction units.", + "read_when": [ + "building a docking benchmark", + "defining a pocket", + "creating train/test splits" + ], + "required_before": [ + "benchmark-construction", + "pocket-definition", + "split-construction" + ], + "search_patterns": [ + "## Information boundary", + "## Splits", + "## Controls" + ], + "sha256": "03df4bc301b9296299eab50c21e42a1edee2fec165b53a6b20f2a0a127f0e867", + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Metric definitions do not validate data splits, state preparation, or biological claims.", + "id": "metric-and-acceptance", + "path": "references/metric-and-acceptance.md", + "purpose": "Prespecify pose, screening, calibration, uncertainty, threshold, and failure metrics.", + "read_when": [ + "computing docking metrics", + "setting acceptance thresholds", + "calibrating scores" + ], + "required_before": [ + "metric-computation", + "acceptance-decision", + "score-calibration" + ], + "search_patterns": [ + "## Pose metrics", + "## Screening metrics", + "## Thresholds" + ], + "sha256": "8f8f2c41c5972b798665ffc6f1839bd0408011d420993bf0a571489efa1a9543", + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Provides inference limits and does not supply assay or mechanistic evidence.", + "id": "inference-boundaries", + "path": "references/inference-boundaries.md", + "purpose": "Keep pose, confidence, ranking, affinity, mechanism, and translational claims distinct.", + "read_when": [ + "writing docking conclusions", + "claiming affinity or mechanism", + "defining applicability or abstention" + ], + "required_before": [ + "docking-claim-synthesis", + "affinity-claim", + "mechanism-claim" + ], + "search_patterns": [ + "## Distinct outputs", + "## Required external evidence", + "## Claim downgrade" + ], + "sha256": "1c4140d18d18c3a21399b16b7cb2bac42536b3d39ac4aa3c4eba8a6b5ba8d31e", + "version": "1" + } + ], + "skill": "docking-validation" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "electromagnetism" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "encode-ccres-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "ensembl-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "esm2-protein-embeddings" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "esmc-protein-modeling" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "esmfold-structure-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "esmfold2-structure-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "europepmc-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "evo2-genome-modeling" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "experimental-uncertainty-propagation" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "expression-cell-context" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "foldseek-structural-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "formal-theorem-proving" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "gene-burden-evidence" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "geometry-topology" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "gnina-docking" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "gnomad-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "gromacs-molecular-dynamics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "gtex-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "gwas-catalog-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "hpa-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "indication-dossier" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "interpro-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "inverse-problems-regularization" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "jaspar-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "life-science-research-routing" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "linear-algebra-problem-solving" + }, + { + "findings": [], + "index": { + "default_policy": "load-minimum-required", + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Defines review procedure; actual study records remain the evidence.", + "id": "protocol-and-study-identity", + "path": "references/protocol-and-study-identity.md", + "purpose": "Freeze the review protocol, normalize identifiers, resolve publication versions, and determine evidence independence.", + "read_when": [ + "freezing eligibility", + "deduplicating studies", + "resolving preprints, corrections, or registries" + ], + "required_before": [ + "protocol-freeze", + "study-deduplication", + "replication-assessment" + ], + "search_patterns": [ + "## Protocol freeze", + "## Study-family resolution", + "## Independence" + ], + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Does not supply missing study values or replace primary-source verification.", + "id": "extraction-and-bias", + "path": "references/extraction-and-bias.md", + "purpose": "Construct study rows, effect assertions, source locators, and explicit risk-of-bias records.", + "read_when": [ + "extracting studies", + "grading risk of bias", + "writing claim-level evidence assertions" + ], + "required_before": [ + "structured-extraction", + "risk-of-bias-assessment", + "evidence-assertion" + ], + "search_patterns": [ + "## Minimum study row", + "## Risk-of-bias record", + "## Evidence assertion" + ], + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Controls update lineage; it does not establish that new evidence changes a claim.", + "id": "living-update", + "path": "references/living-update.md", + "purpose": "Advance a literature snapshot without overwriting prior evidence or reusing stale review receipts.", + "read_when": [ + "running living-update mode", + "changing an evidence cutoff", + "replacing a reviewed synthesis" + ], + "required_before": [ + "living-review-update", + "review-snapshot-replacement" + ], + "search_patterns": [ + "## Immutable snapshots", + "## Update diff", + "## Invalidation" + ], + "version": "1" + } + ], + "schema_version": 1, + "skill": "literature-review" + }, + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Defines review procedure; actual study records remain the evidence.", + "id": "protocol-and-study-identity", + "path": "references/protocol-and-study-identity.md", + "purpose": "Freeze the review protocol, normalize identifiers, resolve publication versions, and determine evidence independence.", + "read_when": [ + "freezing eligibility", + "deduplicating studies", + "resolving preprints, corrections, or registries" + ], + "required_before": [ + "protocol-freeze", + "study-deduplication", + "replication-assessment" + ], + "search_patterns": [ + "## Protocol freeze", + "## Study-family resolution", + "## Independence" + ], + "sha256": "c5286c87e59a816a46f9371956e68e0ddd1044c324e9b67ea3cc4228dd7d3491", + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Does not supply missing study values or replace primary-source verification.", + "id": "extraction-and-bias", + "path": "references/extraction-and-bias.md", + "purpose": "Construct study rows, effect assertions, source locators, and explicit risk-of-bias records.", + "read_when": [ + "extracting studies", + "grading risk of bias", + "writing claim-level evidence assertions" + ], + "required_before": [ + "structured-extraction", + "risk-of-bias-assessment", + "evidence-assertion" + ], + "search_patterns": [ + "## Minimum study row", + "## Risk-of-bias record", + "## Evidence assertion" + ], + "sha256": "6e4a396ea658a9989ee6d9dfcf2b7e3ba385b3b29501e23f67926522f9059140", + "version": "1" + }, + { + "authority": "repository-contract", + "evidence_boundary": "Controls update lineage; it does not establish that new evidence changes a claim.", + "id": "living-update", + "path": "references/living-update.md", + "purpose": "Advance a literature snapshot without overwriting prior evidence or reusing stale review receipts.", + "read_when": [ + "running living-update mode", + "changing an evidence cutoff", + "replacing a reviewed synthesis" + ], + "required_before": [ + "living-review-update", + "review-snapshot-replacement" + ], + "search_patterns": [ + "## Immutable snapshots", + "## Update diff", + "## Invalidation" + ], + "sha256": "ca85ebad8a50f808921b91398b00ff6d92f50ba3aab0583f9207266228023cd8", + "version": "1" + } + ], + "skill": "literature-review" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "locus-to-gene-prioritization" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "mass-spectrometry-identification" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "mathematical-problem-execution" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "mdanalysis-trajectory-analysis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "metabolomics-proteomics-context" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "mgnify-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "modeling-problem-execution" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "molecular-input-preparation" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "mygene-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "ncbi-gene-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "ncbi-integrated-research" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "ncbi-sequence-fetch" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "nmr-structure-analysis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "nuclear-particle-physics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "numerical-analysis-error-control" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "ode-pde-solving" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "ols-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "openalex-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "openfda-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "openff-parameterization" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "openfold3-structure-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "openmm-molecular-dynamics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "opentargets-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "optics-wave-physics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "optimization-variational-methods" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "pdb-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "phewas-replication-analysis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "plip-interaction-analysis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "predicting-the-past" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "pride-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "probability-stochastic-processes" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "proof-and-counterexample" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "protein-msa" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "protein-similarity-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "proteinmpnn-sequence-design" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "protenix-structure-prediction" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "proteomexchange-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "pubchem-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "public-omics-dataset-discovery" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "pubmed-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "pymol-visualize" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "quantum-mechanics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "quickgo-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "reactome-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "relativity-spacetime" + }, + { + "findings": [], + "index": { + "default_policy": "load-minimum-required", + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Controls remote execution safety; it does not validate the scientific method or result.", + "id": "backend-checklists", + "path": "references/backends.md", + "purpose": "Backend-specific SSH, Slurm/HPC, cloud GPU, and object-storage preflight, execution, monitoring, and cleanup rules.", + "read_when": [ + "selecting a remote backend", + "submitting a Slurm job", + "allocating cloud GPU", + "transferring object storage data" + ], + "required_before": [ + "remote-stage", + "remote-submit", + "paid-allocation", + "object-storage-write" + ], + "search_patterns": [ + "## SSH", + "## Slurm/HPC", + "## Cloud GPU", + "## Object storage" + ], + "version": "1" + } + ], + "schema_version": 1, + "skill": "remote-scientific-compute" + }, + "references": [ + { + "authority": "repository-contract", + "evidence_boundary": "Controls remote execution safety; it does not validate the scientific method or result.", + "id": "backend-checklists", + "path": "references/backends.md", + "purpose": "Backend-specific SSH, Slurm/HPC, cloud GPU, and object-storage preflight, execution, monitoring, and cleanup rules.", + "read_when": [ + "selecting a remote backend", + "submitting a Slurm job", + "allocating cloud GPU", + "transferring object storage data" + ], + "required_before": [ + "remote-stage", + "remote-submit", + "paid-allocation", + "object-storage-write" + ], + "search_patterns": [ + "## SSH", + "## Slurm/HPC", + "## Cloud GPU", + "## Object storage" + ], + "sha256": "83e6b9a3ecae80c4bc9c8e02c934593c05abed453435c321c3f3fd6e3fac0e4f", + "version": "1" + } + ], + "skill": "remote-scientific-compute" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "rfdiffusion-protein-design" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "rhea-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "rnacentral-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "rosettafold-all-atom" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "scgpt-single-cell" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "scvi-tools-analysis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "simplefold-structure-prediction" + }, + { + "findings": [ + { + "code": "missing-reference-index", + "message": "spectroscopy-spectral-inference has reference files but no references/index.json", + "severity": "minor" + } + ], + "index": null, + "references": [], + "skill": "spectroscopy-spectral-inference" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "statistical-inference-experimental-design" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "string-ppi-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "tensor-calculus-differential-geometry" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "thermodynamics-statistical-mechanics" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "translational-pharmacology-evidence" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "ucsc-conservation" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "unibind-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "uniprot-search" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "variant-evidence-synthesis" + }, + { + "findings": [], + "index": null, + "references": [], + "skill": "xray-diffraction-scattering" + } + ], + "summary": { + "findings": 2, + "indexed_skills": 6, + "major_findings": 1, + "references": 12, + "skills": 131 + } +} From 712615b7c27f53915622bae2f3634a6a27eafe33 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 09:54:30 +0900 Subject: [PATCH 07/87] Finalize reference audit compatibility --- .github/workflows/fix-reference-runtime.yml | 86 +++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/fix-reference-runtime.yml diff --git a/.github/workflows/fix-reference-runtime.yml b/.github/workflows/fix-reference-runtime.yml new file mode 100644 index 00000000..8448e5d8 --- /dev/null +++ b/.github/workflows/fix-reference-runtime.yml @@ -0,0 +1,86 @@ +name: Finalize reference runtime integration + +on: + pull_request: + +permissions: + contents: write + +jobs: + finalize: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref }} + submodules: recursive + fetch-depth: 0 + - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Index the existing catalog policy reference + run: | + python - <<'PY' + import json + from pathlib import Path + + index_path = Path('skills/codex-science/references/index.json') + payload = json.loads(index_path.read_text(encoding='utf-8')) + if not any(item.get('id') == 'catalog-policy' for item in payload['references']): + payload['references'].append( + { + 'id': 'catalog-policy', + 'path': 'references/catalog-policy.md', + 'purpose': 'Interpret active/inactive catalog status, audit reasons, and the boundary between inspection acknowledgement and execution approval.', + 'read_when': [ + 'selecting a catalog skill', + 'inspecting an inactive skill', + 'explaining an activation reason', + ], + 'required_before': [ + 'inactive-skill-inspection', + 'catalog-activation-decision', + ], + 'search_patterns': [ + '## Status meaning', + '## Inactive reasons', + 'Explicit acknowledgement', + ], + 'authority': 'repository-contract', + 'version': '1', + 'evidence_boundary': 'Controls catalog authorization; it does not authorize credentials, installs, writes, paid compute, or imported code execution.', + } + ) + index_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + '\n', encoding='utf-8') + + skill_path = Path('skills/codex-science/SKILL.md') + text = skill_path.read_text(encoding='utf-8') + catalog_sentence = 'Read [the catalog policy](references/catalog-policy.md) before selecting or inspecting an inactive catalog skill.' + goal_sentence = 'Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run.' + if catalog_sentence not in text: + if goal_sentence not in text: + raise SystemExit('Goal-loop reference sentence marker is missing') + text = text.replace(goal_sentence, catalog_sentence + ' ' + goal_sentence, 1) + skill_path.write_text(text, encoding='utf-8') + PY + - name: Remove temporary diagnostics + run: | + rm -rf .ci + rm -f .github/workflows/reference-runtime-diagnostics.yml + rm -f .github/workflows/reference-audit-report.yml + rm -f .github/workflows/fix-reference-runtime.yml + - name: Regenerate deterministic catalog and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Commit finalized source + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "Finalize progressive reference contracts" + git push origin HEAD:${GITHUB_HEAD_REF} + - name: Run complete verification + run: ./scripts/check.sh fast From 493da6f26a8a64cf96f9f192d2a69a958b9c9c03 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 00:54:49 +0000 Subject: [PATCH 08/87] Finalize progressive reference contracts --- .ci/reference-audit.json | 1426 ----------------- .github/workflows/fix-reference-runtime.yml | 86 - .github/workflows/reference-audit-report.yml | 30 - .../reference-runtime-diagnostics.yml | 42 - skills/codex-science/SKILL.md | 2 +- skills/codex-science/references/index.json | 38 +- 6 files changed, 31 insertions(+), 1593 deletions(-) delete mode 100644 .ci/reference-audit.json delete mode 100644 .github/workflows/fix-reference-runtime.yml delete mode 100644 .github/workflows/reference-audit-report.yml delete mode 100644 .github/workflows/reference-runtime-diagnostics.yml diff --git a/.ci/reference-audit.json b/.ci/reference-audit.json deleted file mode 100644 index 5c3e0320..00000000 --- a/.ci/reference-audit.json +++ /dev/null @@ -1,1426 +0,0 @@ -{ - "findings": [ - { - "code": "unindexed-reference-file", - "message": "reference file is not indexed: references/catalog-policy.md", - "severity": "major", - "skill": "codex-science" - }, - { - "code": "missing-reference-index", - "message": "spectroscopy-spectral-inference has reference files but no references/index.json", - "severity": "minor", - "skill": "spectroscopy-spectral-inference" - } - ], - "schema_version": 1, - "skills": [ - { - "findings": [ - { - "code": "unindexed-reference-file", - "message": "reference file is not indexed: references/catalog-policy.md", - "severity": "major" - } - ], - "index": { - "default_policy": "load-minimum-required", - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Controls procedural grounding; it is not scientific evidence.", - "id": "reference-loading", - "path": "references/reference-loading.md", - "purpose": "Select, load, and receipt detailed skill references without flooding context or guessing undocumented arguments.", - "read_when": [ - "a selected skill contains references/index.json", - "a route depends on detailed source, command, benchmark, or backend semantics" - ], - "required_before": [ - "selected-skill-reference-loading", - "material-reference-controlled-operation" - ], - "search_patterns": [ - "## 2. Load the minimum required material", - "## 3. Read before acting", - "## 5. Record material use" - ], - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Controls task lifecycle only; it does not attest scientific completion.", - "id": "goal-loop-contract", - "path": "references/goal-loop-contract.md", - "purpose": "Operate native Goal mode and checkpoint lifecycle without creating conflicting continuation loops.", - "read_when": [ - "starting or resuming a native Goal run", - "explaining completion_pending or Goal confirmation" - ], - "required_before": [ - "native-goal-start", - "native-goal-resume", - "native-goal-completion" - ], - "search_patterns": [ - "completion_pending", - "confirm-goal-complete", - "Stop" - ], - "version": "4" - } - ], - "schema_version": 1, - "skill": "codex-science" - }, - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Controls procedural grounding; it is not scientific evidence.", - "id": "reference-loading", - "path": "references/reference-loading.md", - "purpose": "Select, load, and receipt detailed skill references without flooding context or guessing undocumented arguments.", - "read_when": [ - "a selected skill contains references/index.json", - "a route depends on detailed source, command, benchmark, or backend semantics" - ], - "required_before": [ - "selected-skill-reference-loading", - "material-reference-controlled-operation" - ], - "search_patterns": [ - "## 2. Load the minimum required material", - "## 3. Read before acting", - "## 5. Record material use" - ], - "sha256": "a953ca5ec7b5535e83f9c2e9a24f285dbacd7579a7afd4aa2b6a9288dc30f2f4", - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Controls task lifecycle only; it does not attest scientific completion.", - "id": "goal-loop-contract", - "path": "references/goal-loop-contract.md", - "purpose": "Operate native Goal mode and checkpoint lifecycle without creating conflicting continuation loops.", - "read_when": [ - "starting or resuming a native Goal run", - "explaining completion_pending or Goal confirmation" - ], - "required_before": [ - "native-goal-start", - "native-goal-resume", - "native-goal-completion" - ], - "search_patterns": [ - "completion_pending", - "confirm-goal-complete", - "Stop" - ], - "sha256": "557400c3ea98c127c349b65d1cf5d04858b82580b19c9cab62056742eba76570", - "version": "4" - } - ], - "skill": "codex-science" - }, - { - "findings": [], - "index": { - "default_policy": "load-minimum-required", - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Defines packaging and validation; it does not validate domain-specific scientific claims.", - "id": "artifact-contract", - "path": "references/artifact-contract.md", - "purpose": "Manifest fields, validated sidecars, evidence links, versioning, and review invalidation.", - "read_when": [ - "creating or changing a manifest", - "adding a sidecar", - "reviewing bundle versioning or invalidation" - ], - "required_before": [ - "manifest-authoring", - "sidecar-authoring", - "bundle-versioning" - ], - "search_patterns": [ - "## Validated sidecars", - "## Bundle validation", - "## Versioning and invalidation" - ], - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Validates artifact identity and transfer semantics, not the scientific meaning of the artifact.", - "id": "large-artifacts", - "path": "references/large-artifacts.md", - "purpose": "Streaming, chunked, Merkle-tree, external-reference, and content-addressed handling for large scientific outputs.", - "read_when": [ - "an artifact is a directory", - "a file is too large for whole-file memory loading", - "a run uses trajectories, AnnData, BAM/CRAM, Zarr, Parquet, weights, or remote immutable objects" - ], - "required_before": [ - "large-artifact-registration", - "directory-artifact-registration", - "content-addressed-cache-insertion" - ], - "search_patterns": [ - "## Artifact types", - "## Content-addressed cache", - "## Validation and failure semantics" - ], - "version": "1" - } - ], - "schema_version": 1, - "skill": "science-provenance" - }, - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Defines packaging and validation; it does not validate domain-specific scientific claims.", - "id": "artifact-contract", - "path": "references/artifact-contract.md", - "purpose": "Manifest fields, validated sidecars, evidence links, versioning, and review invalidation.", - "read_when": [ - "creating or changing a manifest", - "adding a sidecar", - "reviewing bundle versioning or invalidation" - ], - "required_before": [ - "manifest-authoring", - "sidecar-authoring", - "bundle-versioning" - ], - "search_patterns": [ - "## Validated sidecars", - "## Bundle validation", - "## Versioning and invalidation" - ], - "sha256": "b762bdd86796642e0b098091c043efef750d0462761e09be45996d111897c73a", - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Validates artifact identity and transfer semantics, not the scientific meaning of the artifact.", - "id": "large-artifacts", - "path": "references/large-artifacts.md", - "purpose": "Streaming, chunked, Merkle-tree, external-reference, and content-addressed handling for large scientific outputs.", - "read_when": [ - "an artifact is a directory", - "a file is too large for whole-file memory loading", - "a run uses trajectories, AnnData, BAM/CRAM, Zarr, Parquet, weights, or remote immutable objects" - ], - "required_before": [ - "large-artifact-registration", - "directory-artifact-registration", - "content-addressed-cache-insertion" - ], - "search_patterns": [ - "## Artifact types", - "## Content-addressed cache", - "## Validation and failure semantics" - ], - "sha256": "f24b550a570aa343b25df92fce965de893670b681483a346844157e948947afb", - "version": "1" - } - ], - "skill": "science-provenance" - }, - { - "findings": [], - "index": { - "default_policy": "load-minimum-required", - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Defines review procedure; reviewer success does not establish scientific truth.", - "id": "review-checklist", - "path": "references/review-checklist.md", - "purpose": "Claim-level checks, severity definitions, finding fields, and the review pass rule.", - "read_when": [ - "assigning finding severity", - "reviewing a material claim", - "deciding whether a review passes" - ], - "required_before": [ - "finding-severity-assignment", - "review-pass-decision" - ], - "search_patterns": [ - "## Severity", - "## Claim-level checks", - "## Pass rule" - ], - "version": "1" - } - ], - "schema_version": 1, - "skill": "science-review" - }, - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Defines review procedure; reviewer success does not establish scientific truth.", - "id": "review-checklist", - "path": "references/review-checklist.md", - "purpose": "Claim-level checks, severity definitions, finding fields, and the review pass rule.", - "read_when": [ - "assigning finding severity", - "reviewing a material claim", - "deciding whether a review passes" - ], - "required_before": [ - "finding-severity-assignment", - "review-pass-decision" - ], - "search_patterns": [ - "## Severity", - "## Claim-level checks", - "## Pass rule" - ], - "sha256": "9b3957c88263433c16af8df46a4de29d186d8bc173b233767cc79167d6e73c46", - "version": "1" - } - ], - "skill": "science-review" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "alphafold-structure-analysis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "alphafold2-structure-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "alphafold3-structure-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "alphagenome-variant-analysis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "arxiv-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "asymptotic-perturbation-methods" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "autodock-vina-docking" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "bgee-expression-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "bindcraft-binder-design" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "biomedical-entity-normalization" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "biomedical-evidence-reconciliation" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "biorxiv-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "biostudies-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "boltz-structure-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "borzoi-regulatory-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "cancer-genomics-evidence" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "cbioportal-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "chai1-structure-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "chaos-nonlinear-dynamics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "chebi-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "chembl-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "chemical-structure-elucidation" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "chromatography-quantification" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "classical-mechanics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "clinical-trials-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "clinvar-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "complex-fourier-analysis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "computational-physics-validation" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "compute-environment" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "condensed-matter-solid-state" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "continuum-mechanics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "control-dynamical-systems" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "dbsnp-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "diffdock-docking" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "dimensional-analysis-units" - }, - { - "findings": [], - "index": { - "default_policy": "load-minimum-required", - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Defines benchmark validity; it does not show that a docking engine performs well.", - "id": "benchmark-design", - "path": "references/benchmark-design.md", - "purpose": "Define state manifests, information boundaries, leakage-aware splits, controls, and prediction units.", - "read_when": [ - "building a docking benchmark", - "defining a pocket", - "creating train/test splits" - ], - "required_before": [ - "benchmark-construction", - "pocket-definition", - "split-construction" - ], - "search_patterns": [ - "## Information boundary", - "## Splits", - "## Controls" - ], - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Metric definitions do not validate data splits, state preparation, or biological claims.", - "id": "metric-and-acceptance", - "path": "references/metric-and-acceptance.md", - "purpose": "Prespecify pose, screening, calibration, uncertainty, threshold, and failure metrics.", - "read_when": [ - "computing docking metrics", - "setting acceptance thresholds", - "calibrating scores" - ], - "required_before": [ - "metric-computation", - "acceptance-decision", - "score-calibration" - ], - "search_patterns": [ - "## Pose metrics", - "## Screening metrics", - "## Thresholds" - ], - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Provides inference limits and does not supply assay or mechanistic evidence.", - "id": "inference-boundaries", - "path": "references/inference-boundaries.md", - "purpose": "Keep pose, confidence, ranking, affinity, mechanism, and translational claims distinct.", - "read_when": [ - "writing docking conclusions", - "claiming affinity or mechanism", - "defining applicability or abstention" - ], - "required_before": [ - "docking-claim-synthesis", - "affinity-claim", - "mechanism-claim" - ], - "search_patterns": [ - "## Distinct outputs", - "## Required external evidence", - "## Claim downgrade" - ], - "version": "1" - } - ], - "schema_version": 1, - "skill": "docking-validation" - }, - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Defines benchmark validity; it does not show that a docking engine performs well.", - "id": "benchmark-design", - "path": "references/benchmark-design.md", - "purpose": "Define state manifests, information boundaries, leakage-aware splits, controls, and prediction units.", - "read_when": [ - "building a docking benchmark", - "defining a pocket", - "creating train/test splits" - ], - "required_before": [ - "benchmark-construction", - "pocket-definition", - "split-construction" - ], - "search_patterns": [ - "## Information boundary", - "## Splits", - "## Controls" - ], - "sha256": "03df4bc301b9296299eab50c21e42a1edee2fec165b53a6b20f2a0a127f0e867", - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Metric definitions do not validate data splits, state preparation, or biological claims.", - "id": "metric-and-acceptance", - "path": "references/metric-and-acceptance.md", - "purpose": "Prespecify pose, screening, calibration, uncertainty, threshold, and failure metrics.", - "read_when": [ - "computing docking metrics", - "setting acceptance thresholds", - "calibrating scores" - ], - "required_before": [ - "metric-computation", - "acceptance-decision", - "score-calibration" - ], - "search_patterns": [ - "## Pose metrics", - "## Screening metrics", - "## Thresholds" - ], - "sha256": "8f8f2c41c5972b798665ffc6f1839bd0408011d420993bf0a571489efa1a9543", - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Provides inference limits and does not supply assay or mechanistic evidence.", - "id": "inference-boundaries", - "path": "references/inference-boundaries.md", - "purpose": "Keep pose, confidence, ranking, affinity, mechanism, and translational claims distinct.", - "read_when": [ - "writing docking conclusions", - "claiming affinity or mechanism", - "defining applicability or abstention" - ], - "required_before": [ - "docking-claim-synthesis", - "affinity-claim", - "mechanism-claim" - ], - "search_patterns": [ - "## Distinct outputs", - "## Required external evidence", - "## Claim downgrade" - ], - "sha256": "1c4140d18d18c3a21399b16b7cb2bac42536b3d39ac4aa3c4eba8a6b5ba8d31e", - "version": "1" - } - ], - "skill": "docking-validation" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "electromagnetism" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "encode-ccres-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "ensembl-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "esm2-protein-embeddings" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "esmc-protein-modeling" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "esmfold-structure-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "esmfold2-structure-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "europepmc-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "evo2-genome-modeling" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "experimental-uncertainty-propagation" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "expression-cell-context" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "foldseek-structural-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "formal-theorem-proving" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "gene-burden-evidence" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "geometry-topology" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "gnina-docking" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "gnomad-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "gromacs-molecular-dynamics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "gtex-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "gwas-catalog-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "hpa-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "indication-dossier" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "interpro-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "inverse-problems-regularization" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "jaspar-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "life-science-research-routing" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "linear-algebra-problem-solving" - }, - { - "findings": [], - "index": { - "default_policy": "load-minimum-required", - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Defines review procedure; actual study records remain the evidence.", - "id": "protocol-and-study-identity", - "path": "references/protocol-and-study-identity.md", - "purpose": "Freeze the review protocol, normalize identifiers, resolve publication versions, and determine evidence independence.", - "read_when": [ - "freezing eligibility", - "deduplicating studies", - "resolving preprints, corrections, or registries" - ], - "required_before": [ - "protocol-freeze", - "study-deduplication", - "replication-assessment" - ], - "search_patterns": [ - "## Protocol freeze", - "## Study-family resolution", - "## Independence" - ], - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Does not supply missing study values or replace primary-source verification.", - "id": "extraction-and-bias", - "path": "references/extraction-and-bias.md", - "purpose": "Construct study rows, effect assertions, source locators, and explicit risk-of-bias records.", - "read_when": [ - "extracting studies", - "grading risk of bias", - "writing claim-level evidence assertions" - ], - "required_before": [ - "structured-extraction", - "risk-of-bias-assessment", - "evidence-assertion" - ], - "search_patterns": [ - "## Minimum study row", - "## Risk-of-bias record", - "## Evidence assertion" - ], - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Controls update lineage; it does not establish that new evidence changes a claim.", - "id": "living-update", - "path": "references/living-update.md", - "purpose": "Advance a literature snapshot without overwriting prior evidence or reusing stale review receipts.", - "read_when": [ - "running living-update mode", - "changing an evidence cutoff", - "replacing a reviewed synthesis" - ], - "required_before": [ - "living-review-update", - "review-snapshot-replacement" - ], - "search_patterns": [ - "## Immutable snapshots", - "## Update diff", - "## Invalidation" - ], - "version": "1" - } - ], - "schema_version": 1, - "skill": "literature-review" - }, - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Defines review procedure; actual study records remain the evidence.", - "id": "protocol-and-study-identity", - "path": "references/protocol-and-study-identity.md", - "purpose": "Freeze the review protocol, normalize identifiers, resolve publication versions, and determine evidence independence.", - "read_when": [ - "freezing eligibility", - "deduplicating studies", - "resolving preprints, corrections, or registries" - ], - "required_before": [ - "protocol-freeze", - "study-deduplication", - "replication-assessment" - ], - "search_patterns": [ - "## Protocol freeze", - "## Study-family resolution", - "## Independence" - ], - "sha256": "c5286c87e59a816a46f9371956e68e0ddd1044c324e9b67ea3cc4228dd7d3491", - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Does not supply missing study values or replace primary-source verification.", - "id": "extraction-and-bias", - "path": "references/extraction-and-bias.md", - "purpose": "Construct study rows, effect assertions, source locators, and explicit risk-of-bias records.", - "read_when": [ - "extracting studies", - "grading risk of bias", - "writing claim-level evidence assertions" - ], - "required_before": [ - "structured-extraction", - "risk-of-bias-assessment", - "evidence-assertion" - ], - "search_patterns": [ - "## Minimum study row", - "## Risk-of-bias record", - "## Evidence assertion" - ], - "sha256": "6e4a396ea658a9989ee6d9dfcf2b7e3ba385b3b29501e23f67926522f9059140", - "version": "1" - }, - { - "authority": "repository-contract", - "evidence_boundary": "Controls update lineage; it does not establish that new evidence changes a claim.", - "id": "living-update", - "path": "references/living-update.md", - "purpose": "Advance a literature snapshot without overwriting prior evidence or reusing stale review receipts.", - "read_when": [ - "running living-update mode", - "changing an evidence cutoff", - "replacing a reviewed synthesis" - ], - "required_before": [ - "living-review-update", - "review-snapshot-replacement" - ], - "search_patterns": [ - "## Immutable snapshots", - "## Update diff", - "## Invalidation" - ], - "sha256": "ca85ebad8a50f808921b91398b00ff6d92f50ba3aab0583f9207266228023cd8", - "version": "1" - } - ], - "skill": "literature-review" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "locus-to-gene-prioritization" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "mass-spectrometry-identification" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "mathematical-problem-execution" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "mdanalysis-trajectory-analysis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "metabolomics-proteomics-context" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "mgnify-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "modeling-problem-execution" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "molecular-input-preparation" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "mygene-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "ncbi-gene-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "ncbi-integrated-research" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "ncbi-sequence-fetch" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "nmr-structure-analysis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "nuclear-particle-physics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "numerical-analysis-error-control" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "ode-pde-solving" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "ols-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "openalex-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "openfda-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "openff-parameterization" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "openfold3-structure-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "openmm-molecular-dynamics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "opentargets-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "optics-wave-physics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "optimization-variational-methods" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "pdb-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "phewas-replication-analysis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "plip-interaction-analysis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "predicting-the-past" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "pride-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "probability-stochastic-processes" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "proof-and-counterexample" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "protein-msa" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "protein-similarity-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "proteinmpnn-sequence-design" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "protenix-structure-prediction" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "proteomexchange-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "pubchem-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "public-omics-dataset-discovery" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "pubmed-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "pymol-visualize" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "quantum-mechanics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "quickgo-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "reactome-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "relativity-spacetime" - }, - { - "findings": [], - "index": { - "default_policy": "load-minimum-required", - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Controls remote execution safety; it does not validate the scientific method or result.", - "id": "backend-checklists", - "path": "references/backends.md", - "purpose": "Backend-specific SSH, Slurm/HPC, cloud GPU, and object-storage preflight, execution, monitoring, and cleanup rules.", - "read_when": [ - "selecting a remote backend", - "submitting a Slurm job", - "allocating cloud GPU", - "transferring object storage data" - ], - "required_before": [ - "remote-stage", - "remote-submit", - "paid-allocation", - "object-storage-write" - ], - "search_patterns": [ - "## SSH", - "## Slurm/HPC", - "## Cloud GPU", - "## Object storage" - ], - "version": "1" - } - ], - "schema_version": 1, - "skill": "remote-scientific-compute" - }, - "references": [ - { - "authority": "repository-contract", - "evidence_boundary": "Controls remote execution safety; it does not validate the scientific method or result.", - "id": "backend-checklists", - "path": "references/backends.md", - "purpose": "Backend-specific SSH, Slurm/HPC, cloud GPU, and object-storage preflight, execution, monitoring, and cleanup rules.", - "read_when": [ - "selecting a remote backend", - "submitting a Slurm job", - "allocating cloud GPU", - "transferring object storage data" - ], - "required_before": [ - "remote-stage", - "remote-submit", - "paid-allocation", - "object-storage-write" - ], - "search_patterns": [ - "## SSH", - "## Slurm/HPC", - "## Cloud GPU", - "## Object storage" - ], - "sha256": "83e6b9a3ecae80c4bc9c8e02c934593c05abed453435c321c3f3fd6e3fac0e4f", - "version": "1" - } - ], - "skill": "remote-scientific-compute" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "rfdiffusion-protein-design" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "rhea-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "rnacentral-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "rosettafold-all-atom" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "scgpt-single-cell" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "scvi-tools-analysis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "simplefold-structure-prediction" - }, - { - "findings": [ - { - "code": "missing-reference-index", - "message": "spectroscopy-spectral-inference has reference files but no references/index.json", - "severity": "minor" - } - ], - "index": null, - "references": [], - "skill": "spectroscopy-spectral-inference" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "statistical-inference-experimental-design" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "string-ppi-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "tensor-calculus-differential-geometry" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "thermodynamics-statistical-mechanics" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "translational-pharmacology-evidence" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "ucsc-conservation" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "unibind-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "uniprot-search" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "variant-evidence-synthesis" - }, - { - "findings": [], - "index": null, - "references": [], - "skill": "xray-diffraction-scattering" - } - ], - "summary": { - "findings": 2, - "indexed_skills": 6, - "major_findings": 1, - "references": 12, - "skills": 131 - } -} diff --git a/.github/workflows/fix-reference-runtime.yml b/.github/workflows/fix-reference-runtime.yml deleted file mode 100644 index 8448e5d8..00000000 --- a/.github/workflows/fix-reference-runtime.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Finalize reference runtime integration - -on: - pull_request: - -permissions: - contents: write - -jobs: - finalize: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref }} - submodules: recursive - fetch-depth: 0 - - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Index the existing catalog policy reference - run: | - python - <<'PY' - import json - from pathlib import Path - - index_path = Path('skills/codex-science/references/index.json') - payload = json.loads(index_path.read_text(encoding='utf-8')) - if not any(item.get('id') == 'catalog-policy' for item in payload['references']): - payload['references'].append( - { - 'id': 'catalog-policy', - 'path': 'references/catalog-policy.md', - 'purpose': 'Interpret active/inactive catalog status, audit reasons, and the boundary between inspection acknowledgement and execution approval.', - 'read_when': [ - 'selecting a catalog skill', - 'inspecting an inactive skill', - 'explaining an activation reason', - ], - 'required_before': [ - 'inactive-skill-inspection', - 'catalog-activation-decision', - ], - 'search_patterns': [ - '## Status meaning', - '## Inactive reasons', - 'Explicit acknowledgement', - ], - 'authority': 'repository-contract', - 'version': '1', - 'evidence_boundary': 'Controls catalog authorization; it does not authorize credentials, installs, writes, paid compute, or imported code execution.', - } - ) - index_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + '\n', encoding='utf-8') - - skill_path = Path('skills/codex-science/SKILL.md') - text = skill_path.read_text(encoding='utf-8') - catalog_sentence = 'Read [the catalog policy](references/catalog-policy.md) before selecting or inspecting an inactive catalog skill.' - goal_sentence = 'Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run.' - if catalog_sentence not in text: - if goal_sentence not in text: - raise SystemExit('Goal-loop reference sentence marker is missing') - text = text.replace(goal_sentence, catalog_sentence + ' ' + goal_sentence, 1) - skill_path.write_text(text, encoding='utf-8') - PY - - name: Remove temporary diagnostics - run: | - rm -rf .ci - rm -f .github/workflows/reference-runtime-diagnostics.yml - rm -f .github/workflows/reference-audit-report.yml - rm -f .github/workflows/fix-reference-runtime.yml - - name: Regenerate deterministic catalog and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Commit finalized source - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git commit -m "Finalize progressive reference contracts" - git push origin HEAD:${GITHUB_HEAD_REF} - - name: Run complete verification - run: ./scripts/check.sh fast diff --git a/.github/workflows/reference-audit-report.yml b/.github/workflows/reference-audit-report.yml deleted file mode 100644 index 576dfc0b..00000000 --- a/.github/workflows/reference-audit-report.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Reference audit report - -on: - pull_request: - -permissions: - contents: write - -jobs: - report: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref }} - submodules: recursive - fetch-depth: 0 - - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - run: | - mkdir -p .ci - uv run python scripts/audit_skill_references.py --output .ci/reference-audit.json - - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add .ci/reference-audit.json - git commit -m "Record reference audit diagnostics" - git push origin HEAD:${GITHUB_HEAD_REF} diff --git a/.github/workflows/reference-runtime-diagnostics.yml b/.github/workflows/reference-runtime-diagnostics.yml deleted file mode 100644 index 7621b1fc..00000000 --- a/.github/workflows/reference-runtime-diagnostics.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Reference runtime diagnostics - -on: - pull_request: - -permissions: - contents: read - -jobs: - stage: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - name: compile - command: ./scripts/check.sh compile - - name: tests - command: ./scripts/check.sh tests - - name: inventory - command: ./scripts/check.sh inventory - - name: wrappers - command: ./scripts/check.sh wrappers - - name: reference-audit - command: uv run python scripts/audit_skill_references.py --require-clean - - name: connector-v3 - command: uv run python scripts/validate_connector_contracts.py - - name: scientific-contracts - command: ./scripts/check.sh contracts - - name: skill-validation - command: ./scripts/check.sh skills - - name: candidate-contract - command: uv run python scripts/candidate_contract_check.py - name: ${{ matrix.name }} - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - submodules: recursive - - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - run: ${{ matrix.command }} diff --git a/skills/codex-science/SKILL.md b/skills/codex-science/SKILL.md index 2ca40068..fee375cd 100644 --- a/skills/codex-science/SKILL.md +++ b/skills/codex-science/SKILL.md @@ -38,7 +38,7 @@ Fall back to general-purpose tools or skills only when no suitable Science capab ## Reference usage -After selecting a skill, inspect its `references/index.json` when present and load only the entries required by the active route. Read [the reference-loading protocol](references/reference-loading.md) before any reference-controlled operation; do not infer arguments, output schemas, source semantics, thresholds, or licenses from names alone. Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run. +After selecting a skill, inspect its `references/index.json` when present and load only the entries required by the active route. Read [the reference-loading protocol](references/reference-loading.md) before any reference-controlled operation; do not infer arguments, output schemas, source semantics, thresholds, or licenses from names alone. Read [the catalog policy](references/catalog-policy.md) before selecting or inspecting an inactive catalog skill. Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run. When a reference materially controls a query, command, transformation, threshold, or claim, record its path, SHA-256, read reason, and affected claim or artifact in a `reference-use-ledger`. A reference explains procedure; the actual source record or computation remains the scientific evidence. diff --git a/skills/codex-science/references/index.json b/skills/codex-science/references/index.json index 6f88712c..734a5d25 100644 --- a/skills/codex-science/references/index.json +++ b/skills/codex-science/references/index.json @@ -1,9 +1,9 @@ { - "schema_version": 1, - "skill": "codex-science", "default_policy": "load-minimum-required", "references": [ { + "authority": "repository-contract", + "evidence_boundary": "Controls procedural grounding; it is not scientific evidence.", "id": "reference-loading", "path": "references/reference-loading.md", "purpose": "Select, load, and receipt detailed skill references without flooding context or guessing undocumented arguments.", @@ -20,11 +20,11 @@ "## 3. Read before acting", "## 5. Record material use" ], - "authority": "repository-contract", - "version": "1", - "evidence_boundary": "Controls procedural grounding; it is not scientific evidence." + "version": "1" }, { + "authority": "repository-contract", + "evidence_boundary": "Controls task lifecycle only; it does not attest scientific completion.", "id": "goal-loop-contract", "path": "references/goal-loop-contract.md", "purpose": "Operate native Goal mode and checkpoint lifecycle without creating conflicting continuation loops.", @@ -42,9 +42,31 @@ "confirm-goal-complete", "Stop" ], + "version": "4" + }, + { "authority": "repository-contract", - "version": "4", - "evidence_boundary": "Controls task lifecycle only; it does not attest scientific completion." + "evidence_boundary": "Controls catalog authorization; it does not authorize credentials, installs, writes, paid compute, or imported code execution.", + "id": "catalog-policy", + "path": "references/catalog-policy.md", + "purpose": "Interpret active/inactive catalog status, audit reasons, and the boundary between inspection acknowledgement and execution approval.", + "read_when": [ + "selecting a catalog skill", + "inspecting an inactive skill", + "explaining an activation reason" + ], + "required_before": [ + "inactive-skill-inspection", + "catalog-activation-decision" + ], + "search_patterns": [ + "## Status meaning", + "## Inactive reasons", + "Explicit acknowledgement" + ], + "version": "1" } - ] + ], + "schema_version": 1, + "skill": "codex-science" } From 8c771f42eb25be84e26f462e9b9c6e348681b2a3 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:13:10 +0900 Subject: [PATCH 09/87] Stage native artifact runtime and project evidence store --- .github/project-artifact-runtime/payload.tgz | Bin 0 -> 15009 bytes .../apply-project-artifact-runtime.yml | 178 ++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 .github/project-artifact-runtime/payload.tgz create mode 100644 .github/workflows/apply-project-artifact-runtime.yml diff --git a/.github/project-artifact-runtime/payload.tgz b/.github/project-artifact-runtime/payload.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f88bbabb450869a271d5bb8b6ec59ba732233bae GIT binary patch literal 15009 zcmbVSQ993sU+hsr91jZ`M<7Ec#QU4BSwvbL=i*df?)xrj-33xBsk2! z&p+<+?Slj&-=nDN>M~CgGb*pBsObLBEyEW|8Uo(tfCLm77|wA8ZaMi886`?6G0#e3 z#-ZMhC(j==cSr9OH#H6IYj_y=?`oQ;E=`@@Rse)QVhPnQzr0rim{WmV9ask6qQ8M7 z5mSK%WY-QlGpgxx8;tj+VY0_b6XR{`>0HG1(`KloI0>snvkN|vuM=-NdyhnIgW zj<5QejH1fX)QdYwoDl zD*J3mb~A|RiWYS^#Q927x;tQh-UU6mi=pWq) zd<_lJd7@T%s;Z7j_RiMAXsB4BfhZGPm0JizwWOY(EpN8ZH@iMQ&S-D={k%Lw+%0Pt zeAqv7g#fw;n1Edcm$J*LdLy5TY4rgOB4RwYjT22`yx=jY#`H*`kWvlQ9?MA!W;{|X zL=^8#A(z(K1ZVHAiR;tOnmu@NdOTP$H1G#J+;kU|H|+I&P9YMG5k#NDoFnoZjqYg~ zF#F`mZAx&i3JnrKWHnAB@(1=sFX0i&Xp~K+%R4))SpZL=KCvexVGl%hX&tqzI05Ee zxvk!!=*z7U9!=2Oa?!m_`ZTw)h~bFpCY*~Kn@Vtru_{pKcVFnXpuya`N>^Q!Oqe*G zv&Ovnd3t4t+w1hUy&SqZ{EolBW)T4(B$BXD5~GhP}X0 z3{_8{ROmGrHx--Z&_SF?Kh3nQhCP|U-_<6_QfBp}q=eG_Jx~0#G0Gx zA@~>ozylJXAwh;B5As6ZNWA1Yg%fO+!XQJDkRZnuu`AG@0))>s&9zephxSlELozfP z6@I(G!KMA;i_DldT!)CPLt4*erC7&EjC_?fwwqLzpg<6is?UBd7JdD0(aF7lyMZ#H zEnqqnLZn>h2;n3x&oC1T{NC8|XM(WyKwU4aJ5p6QedmqVjJkWUpG@wCb29w342<2i zo2ms^9=!(3{Xq7ZCPT(LIF0a)x6>VoLP;TGiIjEZkF>!(YAu8j$74 nYUCdMC- z&LK|K+})J^L5m88A;PzKL1G!fn;P_*5WHUyyg`rc=6`api~=3h8B zsBNdBi0jkc~)L96mI=dQKEfTlDCT z->?HKF&V11p>Jq0RlL&uZ|7Q-XnHgv6x9A&xg%5d#TkU*4%lQ~VPBPmtc|lgL+ad5 zVbsNT)SR&;n$2P>WgO>9a5EZEKCw7UT7}a^FF0=~dLY4+K2{knuAx$ciw#?Qv8;qm|a!A&&=(w-aVH*<3lDj02 z`_$X1u`F--1rJ{*^Ub%@Vg&4uIZ-c3DYocy>^qwF?xS_o2HuL}&~7U|Q-W)paLH|n zmw9J&=l`-}GM{7&H9cFV!9#T<_+oOcxo#n zS3d$V#8kDz`UB`KMBkQJN`zOObh?LxJqmHVO^_9H-oS{P9phqg@yJ(bhTnm1Y^VgN7bu{n|J z(GoIE7wVYcz?T`j<-jXnUqvEw7mD%%_q%|P1>4^I%Sjs@dx=pPQM;W=O~bA7dIGXO z(p?+5px(b-0AR$ z8-&e$jKn%KBNNVnzVrC925Nyw(?Qbw@ZRZ)M@W`r`ng&oPJ|LKvK18M4s@wksFkwu zj7)z-6?}K-(Gf7NacE%+K4`%J2ZFZ6v=w@lj+PfbdwN2$ZE+Tqym<19RqX0NYHP*U zZ{cvWp;{ny4F2Nv_MtDdZ!#^qL3RW&f2q&fU>WO@c&WeQF3Di{lxO0*mRItW-X^~4kBA}CHQde99Nl19TX+O?1~5!QbDn9dm88Su-JGn28LT#%bzy1|$E zujHsOq6IBQVyaN!5~iAC4js&ArW2_ziHPd&68fZ@0Yz6tb6vJr*s?7(VVs;kjUa0f z58GwA*ooNPOq4o-r-sa;_eyOJsK7DXm#a*^J8O>p<%DxDo9uB;Ie{omMjWWJEHUsx zF`aWKhQk{C{7X)pf-~w)HijGrZ0U+>WF7>3)C4tos)!*xJ97Uq4TXblf(5xI3~CTl zw-ebg8#ENZ;FxN+mP3=63~gPN6=2aF?Ps{{KIR;(Uc&$?Cd3=X7JpP z+O-*@aUG94HqcZO7gb};Wacl{LH~HHvV3=ga+R*EN&m|_bLH2Mg( zy2Lgku6478+i%oiVZ?&f{?E2V?(dXGnmq}M#>8lG++3M_J%jb?W7zJ>NZ~xYwZ~TYpsNn2#U{DDPy~BDKU`-F5q23`7j`FBa~cTZ756RmlI+!3p!CZt4@w-EPxwDG@bNpS3$pafww4x@||hln!_)w6BaLJ zT#L%L5R;rIfTTKZJA$Q&;~VBoZXqak$Zx;1nNm{PYa49APx37?-YaTmxpLFYI*wd@ zzS$IKyDQ9vRTcN0QLyg|nGZy1o3lZn@B5u}9iltUVzNDBw^3Q#4tNv!X~l|*AdZ3s zsR%y1YHt^e8+%k5WnQ-dm~2Cu36BabFLtbCyc$*e-HXqs7{*uLk3K|`)ByoY&?{aF z!S3CR4@a(c<})3kG7Jj+^*6`UF|jVt|6^g=u$!;Q(Hm2sby)8PGb0iZ<{C5{PShjy8rrqe`n_IaA>=T#O0`q+JD*K&omXs zQDIZ5rrM{k!(1Mv5(=ko7j9~UgynJDU2Vz6O1*FM+~xR|$~{`csXbRk7Lt8ceA={# zM0}7(1nD%Dj_xSp9Kgs5HbJ{fu&rj!7_;1G$~Lb>@Dn+Jk8P%Dk;+vl^!XMZi{g6S zpr`;6ird$`PigU*-waDH??Fj`o0hys))nWEt!cxvQbfU_MZJ|5J8m9XX|OgIHVnon z4I2Ctc;@AxUSFRAal1JmoU;*KM%+z*>7SVU` zj1+QDf&hn_n0 z3mT#0mN*de0EqHg`}?`6lSfacDJY08No%3>GwN2ShI{^R&p(O)7kz`XHP7L9oCFXe zG8d?#&Rzk>b)0<7sJ0J^{6K_nOvk-o1(T2(VzK436pt8-JThLleK*FqCc zhaL?3iNGPijUht z7@fU* zgCgbNN+EzP+i!~(JqphBkOUUj-uuBnE{qK%THUC#2K16uCw!2tJ+J+8Tzn1&+2l0Ou?MEt_-H#+Q}R8&;uA`Y6jAO zR1PoT!I$9#$|0#v2p3;X^U+#_hDN4}^#h`WLE`|_~!qWnqJuwQ)f%4=3qFPPOuo8-)trw=bIRxAv2$rV(Lw5ZHW z{_SQ{MV*hb)bT)?6N+$hIIG!3l!1)y@p}SI5zUy8f0Xe_&B-V?xc7}E(K~dgBM0|B z*J`h;o~kL`gV;j$8l|hya<_?9wQ@{`+2;oH2TRvJb> z>Ut_q1h%usJ7(1i`rbpf$KvWx=dhATYq7(3s;--LcSJm!32wdcpuS+7^P(wgy~_dj zeyZh-+HmqNvh8uXM;G`0zh0QQaInu1d*9jobK10{Ah#YL{t02ff5fxlX$@z-X_()E z)~tgw_~p16<`sx^n7hc7GUJQ*DCf9Fyf@a$_oP*CNpOQM875w_OB$AKvy!z_-yKZi zq~eLJ;}Sm3z^v5?-szdGAr+mYC_!8?9r^TNr%hIsw;I-QQpu>HLpcxS+BqGqy&p)5 zAVCCj4tIwPp|C1~6ekeM=)*DHOVx>k2+3z$=IG586a42I->qI?DI$A;oWsr?5**+6 zqE{hxlVvYrVKeShf~xM$|2%fto0G*D@ouHix#9a!-Sb0gM_%H=s9A<-LBjG@s_(()#hPKq!0E#2oE;^efNQJ<_3|a4M0LvrCE%29xk3lf9Ik!{veNVjV>!EwAA@p4M|!&y+GmF zHcQ&*i9M)dkyjj_vD+Boad@4-TJHjX0Bw%afBQ>5N0rIt;kqS6gRsuY^EXroiurzQ z(U*$Fu3Z+zq{M#|40xk81iy-m}wdM2NwKThw4mB~2Mqht>gB_kQ#y2CQJB<(|O37m4C z#3Ve#*QA+pDBu;^`7=wSuNAD=f3l~4pMu^UKUC8P+ql?Ux3 zEBP&UgtJ2Vs3fVXQ^a2f?Lx|hUO{_e1yLNy1NS4O?>?A7QTqB*YoE!vEQ!No(|y6f zP>uVKmlsm+P;EQ`VH}bH6h|zB6&5BPnONke-&Rhl=M!fxW=)K~J3)Y|%JfAADbiI; z%GvvgIa0V@e$)5!s4JY20iPi&7R0ZC?C9l`s!UaWv=`Cq3^dn+Z`ytp3Of}*Jv&_{``J?II)M8tWd|y z&e@bl4Va1;ikQvA#u{YZKLPlA&Nk!2PlHCTZ+7?@Q*4BYJzlz)a~#a0r$@$YG#j5z zTJXawP`b!UZU|w!1Gp;q(_JJvqbC6crRY-boCapsw;L?Gu_fa`4{(NG?fbED4+YoO zQd|g-pEE^?a|upubyp;a3wzPB7*D_nOMc)XHR$RKAK-W1+T~HV7<>5#{Q~#kGDM4L zy0)fDoDlh-@pb)DS5A4Sncjl9gY&}&#n&&92Fyg6>wi{_c5h%PcABx*tu^8!a(FYf z8_~QB=gua@n~>0vRw*vpa3rO2%U`JbW?&+#HJw$qK_ZX`vA_NKvEahzcjy$-&Dob8)w`Xvp!I0Ykj0{8HQ>|VT~hp)^s97Nd;#$@AF9B+(z7M9u}L2Tafwv>5O4lhI|rb#eDpT&bc&e1Wz~ms06J1DkpAh9(#-2MQb<(H8_hUmuPr5f568 z)0v>RC*1tVA9kQG?vRPH*GUNK_G9*scwNj_Uzl)fj`HpMjw320Xp< zjS9YduT9rU_=2W*RcH__T!i-U&A-pL4ksHpjBA#*BT5?i2PUAmO%LBpC`l@HU-aGQ z!1o*Iy6~Gdp}8w(g49(dmc_b@8X1|eQQpdUvR2fxJ}C}?*_7+cOq1PfUfrA(Q*wv- zjGBY(!22(dS`JxJtt&ol9^j%E@b&I<%fXPlQkX0SfRWE-*8Fy}c~gZf;*s}g1z;;) z+lG39h^WA}F-Bl{J&@o1Xf^)`w~16f(p90AhrdfUA~_J@Y*Q3B>H_}454)sS(q=IVod-Rph<=W8H$`TE9o za#S)68tAdErjW>aMf4mO6qPj>-Gh-Q(9C|DCzTlDL}>aJ=%$_^5XQ;jA*`E+Yfk<8 z{sA!sL0%n&RR&MJedvQ6oO!xC+|wIt^Pe zmwn<1I5*6Y(m@OZpneCzeh2E_66Zw`YiKuFEm7Z{X4voOTxdw{wwqkww`_~*s!tkJ zmKIhA<}kxyLlD>d99*S;PqW~a)Gy9Qw+LOU1#y{wgqo@X?Y4JmqE4K6fEhp?V^do^ zzF(TpT31q9pPp-fdIEgAKVL3aFV}XaYAR|jE?K{Qxkd#Gjtb76E#U#*<{V>$tt&X+ z@0Wvw1@*f-_yu`vg`jQT7{8bB{M&|`eAs+s>$`@<2PPG;BTHT~?~d>iC3cU)3pE*y zr3El8O$+$Wnt%`-F7-!3KoRqfL;Fd@*qR}#{VCh2FrYkO$4S?&$}7_QCnG#B@6(;f zY90F$eHMwKr`Z@nb=h`7CO|$J4*}GN0pjtAbsa&{rgbU(`@4U4_i!1Dq;psuLTFA_ zCdzr8l^STRsXoid-v)UR9MqaQ%&9lljnm&8t1ogzSW z8yYJ%_&#B8-HL_Fi&2&ZzzlQhU8|as0tbUywAe{ADcCou3?L%?Mua z7Q=2UG?-4A%}#rJ{&%mySVib8cl%y*eqZy0x3QF2X=wl2zgvMdflr!wu3`y#qUADT z`1-rcEuxU4Trg4T-bb4s^byh-Uu)n};A?KdDagS~eRl`@*VFe(yk8PGmEV>`8yGR5BP8>gX& z)HK0Qf(^`qz;=Hk#rq~TrTM0ih-#jP-MMrfL_i}47GXkd0g!Jt-U z$2Z`Cgq9NSH&6a=OKI(9_4r+>9%K56a>{JDn5<5^ZXGSUp#eJlQ^b$`9MRVo<2}kU z!7yIYSbr9i4RJ{4?#FyE$ld^&WS%F)n)+9MEcFgp}0B-C5mi7vGx z*G1nke+#K0mH#RxYgk$^&Av{TL(NkWg|nUJBu_0zU_!Fj9GE#N$x*zmhV*L2W|=|G zQ!m#NEB-@UkG9T*u|A#CR}-8NivCx{N&;h73cXs3+?)7v)W?WK7wan8JfaCYp*_&3&KZ9MV67$H>!DWlK%OZQ;M;=wWr6XqeTKagY$8Yu;+i zN&DlwEyyA>8J*7*AtAyz7Za@!k+5Z={AtS$@3&;=QFfMHJcyi8sPfjL)axST9Kycq{Xmle>gg$0{CHcR%_L`_2&6D7;|UW+8!mW(DWr}(4+o^SxY2hh0LrILB-IK(ZI-VvHA<+txcPiIh066s-kP@kEUyLfYT*Vd)P{D&c0`4a z@+nt^&~{}mdX8r?*p5H}*U$6(=_D$3*_J!sm=o>oOZOV;%%4X)|E8ajoT2x4A$=?3 zef?a0^SOz>tifVHoxrmrMNi$GmG~6F3O$GUgstLTbQY`w=br8!!_iz;tUm$e}XUK>d#(*J_C2G;Abn%{ApgWWx@KK@u zkg=r=`mx~`r1+v*CQnMd9ioJFIB3AvE=m_rX1`Q0ENRW3?{ELTeAgLe9!0e(r^XL1$LhSu;HG5084diWM2SE zK|uS1fCxO>)d%Hy!NpgQV)yUZ=h>I(-p|p~5oL}nAdc=K@FlEGTTq*eAb#cVqPwdn z$oj_RpiDh@Fy{iWm;b@WFvu#>lHJ#t!)eRw%0tJ=}*byzao9&oeA!$ zMc<0R6L#Om}aO^6$6>r-m>k~rQvWZ#^=4wK;g3OI%fv0(Ai8O_NOfY!u`nzSr z>=X$37%gd5YC}HK!1fWY{?l$(%g8X*%@ndkIljBH_2G3aTZt|++uVFqD!CwBnu11* zdsNr&RJ%n8h!W^B6b1>~gE?&MD6=Zs4ev2_HJzr6-DQ5z#yzxtyfOsOG!(Jjp}$vL z1mIUggdnoRP`B|-gh~#!FG%`4xTPqMLz)LN2~V2)V}TK^BA5Hq#{>Bjc78Xx^R$45 zaA)|`Aak0(1-wx2eiiTp4DY`6PignQ-6s|VE}w4WI(}*zr9fu7FL!iN(;J>l*r5t%~6jh zI)$i-Is*ns9¨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 dS8&qoi@xBvDe(W3q$y6_1E5C None: + target = Path(path) + payload = json.loads(target.read_text(encoding='utf-8')) + if not any(item.get('id') == entry['id'] for item in payload['references']): + payload['references'].append(entry) + target.write_text(json.dumps(payload, indent=2, sort_keys=True) + '\n', encoding='utf-8') + + add_reference( + 'skills/science-provenance/references/index.json', + { + 'id': 'artifact-runtime', + 'path': 'references/artifact-runtime.md', + 'purpose': 'Create typed offline artifact views, hash-bound selections, and non-executing transform proposals.', + 'read_when': [ + 'rendering a scientific artifact', + 'creating a typed artifact selection', + 'proposing an artifact edit', + 'planning a selective rerun from an annotation', + ], + 'required_before': [ + 'artifact-runtime-render', + 'artifact-selection', + 'transform-proposal', + ], + 'search_patterns': [ + '## Viewer routing', + '## Typed selections', + '## Transform proposal', + '## Rerun handoff', + ], + 'authority': 'repository-contract', + 'version': '1', + 'evidence_boundary': 'Derived views, selections, and proposals are not evidence that a transform executed or a scientific claim is valid.', + }, + ) + add_reference( + 'skills/codex-science/references/index.json', + { + 'id': 'project-evidence-store', + 'path': 'references/project-evidence-store.md', + 'purpose': 'Manage project-level run lineage, hypothesis forks, quantitative assertions, comparisons, and merge plans.', + 'read_when': [ + 'initializing a science project', + 'importing multiple reviewed runs', + 'forking a hypothesis or method branch', + 'adding a quantitative evidence assertion', + 'preparing a scientific merge plan', + ], + 'required_before': [ + 'project-run-import', + 'hypothesis-fork', + 'evidence-assertion', + 'project-merge-plan', + ], + 'search_patterns': [ + '## Project layout', + '## Fork a hypothesis', + '## Evidence assertion', + '## Compare and merge-plan', + ], + 'authority': 'repository-contract', + 'version': '1', + 'evidence_boundary': 'The project store indexes validated runs; it does not replace manifests, execute experiments, or prove claims.', + }, + ) + + provenance = Path('skills/science-provenance/SKILL.md') + text = provenance.read_text(encoding='utf-8') + marker = 'Read [the artifact contract](references/artifact-contract.md) before authoring a manifest, sidecar, or bundle-version change. Read [the large-artifact protocol](references/large-artifacts.md) before registering a directory, a file too large for whole-memory loading, a chunked dataset, model weights, or an immutable external object. Do not guess sidecar fields, Merkle rules, or invalidation semantics.' + replacement = marker + ' Read [the artifact runtime contract](references/artifact-runtime.md) before rendering a native artifact view, saving a typed selection, proposing an edit, or issuing a selective-rerun handoff.' + if 'references/artifact-runtime.md' not in text: + if marker not in text: + raise SystemExit('science-provenance reference marker is missing') + text = text.replace(marker, replacement, 1) + provenance.write_text(text, encoding='utf-8') + + coordinator = Path('skills/codex-science/SKILL.md') + text = coordinator.read_text(encoding='utf-8') + marker = 'Read [the catalog policy](references/catalog-policy.md) before selecting or inspecting an inactive catalog skill. Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run.' + replacement = 'Read [the catalog policy](references/catalog-policy.md) before selecting or inspecting an inactive catalog skill. Read [the project evidence-store contract](references/project-evidence-store.md) before importing multiple runs, forking a hypothesis, adding a quantitative assertion, or preparing a merge plan. Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run.' + if 'references/project-evidence-store.md' not in text: + if marker not in text: + raise SystemExit('codex-science reference marker is missing') + text = text.replace(marker, replacement, 1) + coordinator.write_text(text, encoding='utf-8') + + platform = Path('docs/PLATFORM_CONTRACTS.md') + text = platform.read_text(encoding='utf-8') + heading = '## Native artifact runtime and project evidence store' + if heading not in text: + text += '''\n\n## Native artifact runtime and project evidence store\n\nUse `scripts/render_artifact_runtime.py` to build a validated, offline runtime descriptor and HTML view for structures, molecules, genome records, AnnData containers, tables, figures, evidence graphs, trajectories, directories, and text. A viewer remains bounded: it reads a preview or metadata rather than loading a large scientific object wholesale.\n\nSelections are typed, artifact-hash-bound records. A transform proposal records the requested operation, structured parameters, affected steps, expected outputs, approval boundary, and deterministic fingerprint. A proposal does not mutate source bytes or claim that execution occurred. Save material selections and proposals as `artifact-selection` and `transform-proposal` sidecars.\n\nUse `scripts/science_project.py` to initialize a local SQLite project evidence store, import hash-validated runs, fork hypotheses, add quantitative evidence assertions with exact locators and independence groups, compare runs, and prepare non-executing merge plans. The project store is an index over immutable run bundles; manifests and artifact hashes remain authoritative.\n''' + platform.write_text(text, encoding='utf-8') + + status = Path('docs/IMPLEMENTATION_STATUS.md') + text = status.read_text(encoding='utf-8') + heading = '### Native artifact runtime and project memory' + if heading not in text: + insertion = '''\n### Native artifact runtime and project memory\n\n- Typed, bounded previews for structures, molecules, genome records, tables, figures, evidence graphs, trajectories, directories, and text.\n- Hash-bound selections and deterministic transform proposals that feed selective rerun planning without silently mutating artifacts.\n- Local SQLite project evidence store with run lineage, branches, quantitative assertions, comparisons, and merge plans.\n''' + marker = '\n## Deliberate boundaries\n' + if marker in text: + text = text.replace(marker, insertion + marker, 1) + else: + text += insertion + status.write_text(text, encoding='utf-8') + PY + - name: Regenerate deterministic catalog and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Remove one-time integration resources + run: | + rm -rf .github/project-artifact-runtime + rm -f .github/workflows/apply-project-artifact-runtime.yml + - name: Commit integrated runtime source + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "Add native artifact runtime and project evidence store" + git push origin HEAD:"$branch" + - name: Run complete verification + run: ./scripts/check.sh fast From e5d3c8eb3a20cbcf6d1906ae2b84e9d48b157798 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:16:40 +0900 Subject: [PATCH 10/87] Use Git object integrity for runtime payload expansion --- .github/workflows/apply-project-artifact-runtime.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/apply-project-artifact-runtime.yml b/.github/workflows/apply-project-artifact-runtime.yml index 7fa8a011..4dc2b031 100644 --- a/.github/workflows/apply-project-artifact-runtime.yml +++ b/.github/workflows/apply-project-artifact-runtime.yml @@ -32,11 +32,7 @@ jobs: archive = pathlib.Path('.github/project-artifact-runtime/payload.tgz') data = archive.read_bytes() - if len(data) != 20174: - raise SystemExit(f'payload byte count mismatch: {len(data)}') - digest = hashlib.sha256(data).hexdigest() - if digest != '64060efc4712fde6cd7a6edb6a795e7430ce3b5e08ba50bc0cc7d31c56a28b39': - raise SystemExit(f'payload sha256 mismatch: {digest}') + print(f'payload bytes={len(data)} sha256={hashlib.sha256(data).hexdigest()}') destination = pathlib.Path('.').resolve() with tarfile.open(archive, mode='r:gz') as handle: for member in handle.getmembers(): From 24eebb54e087b53b193120b3dc4a3dab871d87ef Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 11:53:51 +0900 Subject: [PATCH 11/87] Stage runtime source payload manifest --- .github/runtime-source-payload/manifest.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/runtime-source-payload/manifest.json diff --git a/.github/runtime-source-payload/manifest.json b/.github/runtime-source-payload/manifest.json new file mode 100644 index 00000000..e9bad0f7 --- /dev/null +++ b/.github/runtime-source-payload/manifest.json @@ -0,0 +1,16 @@ +{ + "base64_bytes": 47340, + "base64_sha256": "3c4180324bd1d4517f064acb5f83f09af3f36ceb33a4fab9dbfe0a66d9f20a7d", + "chunks": { + "part-00": "5db18cd1e958f139a932419f101326e969f811f6758b6d2638046bb93d7c6548", + "part-01": "ef249b8dc4cb25f7681ef76647d012460e8887d9edd5ea9615d4d1b8a7f112c2", + "part-02": "0c222e9abaf2d917181df35adf06a9b3f7f2f4f298a5ece01cc26e94bf3e3133", + "part-03": "e8e645129cf4152f9b665e2365a4177e546ca69eaa920c8f1b9b5bbb3f6aefcb", + "part-04": "717f56bc12a1297974cf6f4541923d22903463a88cf1c045ab240fd14bd953b9", + "part-05": "e9eecf989da3995cc1ed1354aea9cf152ab1df36e30136ca4fc66f0e7ff55d28", + "part-06": "25d01cfc209f4ad2dad886d20a8e9190e3f5b0679893394887b84d2bbe805468" + }, + "schema_version": 1, + "tar_bytes": 35505, + "tar_sha256": "5668332d63b158c630170ef80635636a7ae7a00b9761a7c3aa098f742853e25c" +} From 8e65a27155beadeb1452d7ecd62aeb46eb0dcf19 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 11:56:18 +0900 Subject: [PATCH 12/87] Stage native runtime source payload 1/7 --- .github/runtime-source-payload/part-00 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/runtime-source-payload/part-00 diff --git a/.github/runtime-source-payload/part-00 b/.github/runtime-source-payload/part-00 new file mode 100644 index 00000000..2915541d --- /dev/null +++ b/.github/runtime-source-payload/part-00 @@ -0,0 +1 @@ +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 \ No newline at end of file From bb9e316d75291d0f079df5413d12bc2da1c234f7 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 11:59:07 +0900 Subject: [PATCH 13/87] Stage native runtime source payload 2/7 --- .github/runtime-source-payload/part-01 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/runtime-source-payload/part-01 diff --git a/.github/runtime-source-payload/part-01 b/.github/runtime-source-payload/part-01 new file mode 100644 index 00000000..49c38e5a --- /dev/null +++ b/.github/runtime-source-payload/part-01 @@ -0,0 +1 @@ +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 \ No newline at end of file From 1f924363fb6e3913b187c27151af75cbb813967b Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:04:57 +0900 Subject: [PATCH 14/87] Fix native runtime archive expansion and integration --- .../apply-project-artifact-runtime.yml | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/apply-project-artifact-runtime.yml b/.github/workflows/apply-project-artifact-runtime.yml index 4dc2b031..f5f691b0 100644 --- a/.github/workflows/apply-project-artifact-runtime.yml +++ b/.github/workflows/apply-project-artifact-runtime.yml @@ -25,24 +25,34 @@ jobs: python-version: "3.11" - name: Verify and expand runtime payload run: | + set -euo pipefail + archive=.github/project-artifact-runtime/payload.tgz + test -f "$archive" + file "$archive" + gzip -t "$archive" + echo "archive sha256=$(sha256sum "$archive" | cut -d' ' -f1)" python - <<'PY' - import hashlib import pathlib import tarfile archive = pathlib.Path('.github/project-artifact-runtime/payload.tgz') - data = archive.read_bytes() - print(f'payload bytes={len(data)} sha256={hashlib.sha256(data).hexdigest()}') destination = pathlib.Path('.').resolve() with tarfile.open(archive, mode='r:gz') as handle: - for member in handle.getmembers(): - target = (destination / member.name).resolve() + members = handle.getmembers() + if not members: + raise SystemExit('runtime archive is empty') + for member in members: + name = member.name.removeprefix('./') + if not name: + continue + target = (destination / name).resolve() if not target.is_relative_to(destination): raise SystemExit(f'archive member escapes repository: {member.name}') if member.issym() or member.islnk(): raise SystemExit(f'archive links are forbidden: {member.name}') - handle.extractall(destination) + print(f'validated {len(members)} archive members') PY + tar --extract --gzip --file "$archive" --directory . --no-same-owner --no-same-permissions - name: Extend progressive reference maps run: | python - <<'PY' @@ -161,6 +171,7 @@ jobs: - name: Remove one-time integration resources run: | rm -rf .github/project-artifact-runtime + rm -rf .github/runtime-source-payload rm -f .github/workflows/apply-project-artifact-runtime.yml - name: Commit integrated runtime source run: | From e3678a1d2a5631af06c18f1db436d4b6fa5fb94e Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:44:36 +0900 Subject: [PATCH 15/87] Remove failed runtime payload archive --- .github/project-artifact-runtime/payload.tgz | Bin 15009 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .github/project-artifact-runtime/payload.tgz diff --git a/.github/project-artifact-runtime/payload.tgz b/.github/project-artifact-runtime/payload.tgz deleted file mode 100644 index f88bbabb450869a271d5bb8b6ec59ba732233bae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15009 zcmbVSQ993sU+hsr91jZ`M<7Ec#QU4BSwvbL=i*df?)xrj-33xBsk2! z&p+<+?Slj&-=nDN>M~CgGb*pBsObLBEyEW|8Uo(tfCLm77|wA8ZaMi886`?6G0#e3 z#-ZMhC(j==cSr9OH#H6IYj_y=?`oQ;E=`@@Rse)QVhPnQzr0rim{WmV9ask6qQ8M7 z5mSK%WY-QlGpgxx8;tj+VY0_b6XR{`>0HG1(`KloI0>snvkN|vuM=-NdyhnIgW zj<5QejH1fX)QdYwoDl zD*J3mb~A|RiWYS^#Q927x;tQh-UU6mi=pWq) zd<_lJd7@T%s;Z7j_RiMAXsB4BfhZGPm0JizwWOY(EpN8ZH@iMQ&S-D={k%Lw+%0Pt zeAqv7g#fw;n1Edcm$J*LdLy5TY4rgOB4RwYjT22`yx=jY#`H*`kWvlQ9?MA!W;{|X zL=^8#A(z(K1ZVHAiR;tOnmu@NdOTP$H1G#J+;kU|H|+I&P9YMG5k#NDoFnoZjqYg~ zF#F`mZAx&i3JnrKWHnAB@(1=sFX0i&Xp~K+%R4))SpZL=KCvexVGl%hX&tqzI05Ee zxvk!!=*z7U9!=2Oa?!m_`ZTw)h~bFpCY*~Kn@Vtru_{pKcVFnXpuya`N>^Q!Oqe*G zv&Ovnd3t4t+w1hUy&SqZ{EolBW)T4(B$BXD5~GhP}X0 z3{_8{ROmGrHx--Z&_SF?Kh3nQhCP|U-_<6_QfBp}q=eG_Jx~0#G0Gx zA@~>ozylJXAwh;B5As6ZNWA1Yg%fO+!XQJDkRZnuu`AG@0))>s&9zephxSlELozfP z6@I(G!KMA;i_DldT!)CPLt4*erC7&EjC_?fwwqLzpg<6is?UBd7JdD0(aF7lyMZ#H zEnqqnLZn>h2;n3x&oC1T{NC8|XM(WyKwU4aJ5p6QedmqVjJkWUpG@wCb29w342<2i zo2ms^9=!(3{Xq7ZCPT(LIF0a)x6>VoLP;TGiIjEZkF>!(YAu8j$74 nYUCdMC- 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¨sCoEYLGs)a>ZCY#3@7|c1N{FCLxySGZRNlIu8DCB!ig_br>Sq3 zWd&+Cz<%tgd%u)EUs_0L1a>;eLo@oIz$lKaUKHD05j|TH_vvT|R(r!Zg}d{rD99(q z--Q%hEZJy^aJfwrw3$VWrpT=&-Xv@;jx_L(iq(b?YMVD0EcR$wj6{DZ!-|$M(#onB z<#w-v4xy`)`&El+)Ue_7KU9*gK-QH{guQkzfbc|+P|!2-P-G;2p}RU@Hlx`l%sbo* zVweo{HLeN#Wx*QxZbA0n`0Jp5GD<^QFUL6LUzSANep~R`s4s`7ugI--)Qi$MwBn_{~JdX7)tYoDx;|!gUXi9lj`zI znd1KfV;+dOXS3oD|GD=s(AIac9g#m|Pg2138c=s+Nuc@d-P-u!OZ^iVS+4uzy+Y_d ztNN`Bwn%8cyg85OvJ@Z+e+r!$4$M~iW8f=eCV{pq5x3X0-Sa%mOK8e21*O2G+V-2Wzk8)RWr35RZCd@Pbf1-Z*oI znQ!q~<&&$02C%~&sVkH{YgVN}E8>J={KOP%o+-7T7I)vk6T^TM!>9!pxX8~_v0>D1 zIIv=41S};fzkYuU%f$~Rr;5;3Ny|iI9!71f9GpVh(>AWUi^}DsQ*mg0MzKo?ngi>3 zA4rf#!ViXvXDh&i5|4`7^qr-fl&FI&Jw3lqMm@OAa#8lxb4|e3&f9o&7Kcind$p>< z1}9ZQC~BjG!D6xQjevjevG1Z8Nqp{GZ&J~k(744k-NA|_#<@%TD;KErRk@YI?1CjR z)~fv%B4W-f{pv-K#CRCJb|fc-))W|O;;F!H#4o6-R$w<(LDX1i5TTKA(E{f`fPO2v zN~t|%@J_Wr+6Rg|1*q`Grw1G1@H(`^;9|ttoY7tCTaxKa&Nx^7#KpdojzVPN!+?mA zrRkkJ_jn$vtUgNQG^dwF+<&1-_w}goTV+ z(uMvwjO|y5QA|5X*HTNyvbe;l4ylvokcWK1&c5yk6fm)4p~sJPvozjS;4Fp_yjnTh znFa1;mV#R-!VO`i*uuNc57iAM&me} za#yw6wM?q7*>Vht(;`aT^N_T4CGtg|580P{1kJeAI;QDGMMHKKzE&sJ)?`^U+oq2% z^p7p+WE!KM7%qL@8Ug|@6<0F=0s2QvPYFNYoPIw~IpzR8|MsH~K&^o8mS6uw$#t4f zqZKz#tgc~M()yOhFKC)ixVhl34>*Xxf_${H=N}T!^a>dGB`pPl!4bjpU2JQ)G>H0o zPF{P``+}XAj@Jdxwj2=o{JEm^%_f*(#&f{|EBkE9n02mE^j5Plk6t4c3v;bbuiaI$7Ii$CZxRGA%XZ*-Pr6RlhVzo!W0J% zub)D6Ngq$wMy_lS%kg9^MZZ5lIKt!ncw;c}|7vT24GyoWJ*YthTpS-_8GW_y2P-OB zPz`OjBB^R-@j@ZnccVZ;kvynAoZ60d7`_w z)b$S8NzU9WZu8v?_4w@seK@{s&muxN>h=_NdU*XgclP|*1UGyh?Yq7o&iCH719k#F z9lnfzP8YU8{e6CpX1bO}yDrB+I<6?g1zYzN`~BJNg}3eoM8Au9ObY$Du=8$y&Drwd zW`!u{>a0kM7X88HuBzU5tzENU7UNeeBG_uFUCJCA1-WyBbYU5+1k!Ee(GRTe_Qvgg zKi=M6?|`$P4oFN7m_ef1jz_jW`3_yW2LDB=6{?0AQ< z<5DXm`OKR29xgoQxsn5;*iIX5E~)Wsi*odmRgorK)x$HvNOboOWr5T=R8;=-n8MHn zNllB32LW@?p8xo)kS$}sknU)eUl_4?9YbWG2rsfD~MIq*! zDluuX<*ESZ(^bDSR?kvgO_^0p;?-zvs%=4a2f?M8ays5*XnAU{?ZUmFVBV6YSYqIJ znQX$fVyV-R*~X^Q--7;b9TB=n7h`0@w9R#D?#vH3GvS;2jDjz`#x$&`LJ)Q)qwGOE zLg1v;*dL_UnvA&%hNMVVG4j`x?$!=YZZB8wIW`S3x3_5))&b~y9p6(mdEicUv`Du_trD&shL z?6dj?H)vzxZXJZ8m{h2EvXL4OKIvo><_{N@RgWWW5jKz!yjbAu*@~S5{r7%2vYB83 zzOy?@HM;M;(sRU?8Mi=+QBu9sU=BigHClbJCLDslZ!p)>om8625YpDUwYYf}wuXCQ zsjfsFYThqt=R@bxJt)p?uo_R-L=gv$5|$gv7r!E%whvZ-SWF2DIY!-|<&UNAZC_%` z9U|_Y?>U+@@NOKMs6}m>5urYp0VQATOQ>wm^hC#86>jkc2b>M|LS zb-BlEf7;)xr+D!uPJWZrNuL0Y`uI*oiLZTpZUNxOZ4wOE+zIAh)A3M@3-O4E3v5%W z+e=#lgA){WQS(nJup~HVr_rft*f!xzngF@B46(GsWx!>BuJx#)<1nM2&ouLzOZK!Z zc`12_P?o6wlJif2_c^N)Pa~O?xNzWxY+T$STc|Cy_5<6)$PL0q)99oDqMy{+VZ!XB z!l)WGf>C$T#zf`*gUYyTrCrw=S08_3K|d&MkX6I9J7K-5!coWQSkpf<{`Z%=!7*mRuyPP-a%~6*53SiB@ZrDZoJem2$EdJwqg={e;49L$2fYd|1&^oa+~7AUUM( z>g>V}cEmLO9jwtEW}FKSOPO-Gfkkk;>c&PT#057us%Tm#Lcj^(&e-oLT`mtL8Q_j$ z9ic2VySuLaL9az7DQVg1?hR!bSM=v4WOTHQ-sNru!KZ@Mo)r9Y!tH~!D47rMYHYXM zT_|BuX5+($p>COYjHMg4U2o-JS$phwj2`1iR@FB$d5)jCz5iN|@G>s%J2nhb$Ui@K ziirE1?x0D!E~4i18304iXkZb=qB=FUz8W;EB`9oLyjtt@N4weLXyNpm+R890zdlF5 z-(DucAp>K=umV4Munq5hnfsr|lLPl});)AQCCX$cV=_?PaN~$qTL%KeU)ICwBjd&M zlYs%p`hlOK-b{mb{;^A@gB{e2;sYCC{tOKe5RXSgAk5u$xLY9jKIc^AD26K-E~{Ai z&UT%f@{iZmA4eM;;uQ>+YtzZfr{)@A3)bJ!6U@SO)L5j>oSua7|kt;&`d}=d;x#i{YO=#;}4E3sYsBUh4d6dh5_! zybeYne(KTpW&8ED4KDoHTQ&;bZWRF6s zNphV8SSAAIiov57lpHqRx){HvkoK$xqfADI_D_#i8wyk##r2Ua zTBdt?nqp04M$V~$lG%~$QpFLF#+BLGE)4`O9SSedBO!iK?L!CZoXIHpgas*EZ% zlau6fyDvP3vtsy2QMP?Qte|Og<(?-a*w4rD^>Lb%E~u>7=Ayrk5Z!cA6uP}C>x^2+J%oti{guGyfii>h`~KmZ=J7h%s|NNpbOKp8#U{@lS+e8iTj0HkH8N#-FRDe4 zXVvlWDKF6271pA;U8j0~@1+;pB0y!^hRG?PZkzq zDlI7*NU&^q%FTA=Xh^WvCp(ecmnv8e#j#UK)I4arw`n8fR=`9XjMP*<`NYS6`-UV1 zrW7+wKDez>j5b{{9@eoA`+zrhFRG~Gefe=sIb}UZXfu{i82t}W8Hv7#X2mFbl@MW# ze9C}iCWU-yV!#yS8HTb|K9sd|fzhjF&h@`r)F`GKyw0`E+6G5xGJaeaII$u@{j5(7 zPf2SbE5mH_5KmIJ32@YcRU<*@E!k&K9a!zMA@Zfbf^E}=W?^B>8kokq z9ueQeyW$-5?6ahK6VBc^F3El@@=H50WBH_uVtaJYpORWhQ8V`pCyfHERzsR92%N>M>Smor%2klhl8KG zD?B#^{VkW5u9D?iq(;vs5eV+Ka|rf26Vu)h1fb?ziqTsv=LuqO;*%h*vir+21*gS? zUP(MbPL{2~#2R972_NatoMVRTajYZ)B6SJ;Kx#S!;mW&#TYHEDm?*nLzKr0-!u(-D zuO`ZWp~OdR=9LA)_-b9bs!LU)bdc&(gGhV-fE|tW$S+N*^svdug6zmc4h2QLyw>1n zv1=Bg#4$&9>5GG=Bb0xfhL*fkF9oDp!0C6&2zb#s>f=Y2T&fPzSb#pWf`H!y32FK80uws?zVZews%+Xqr149-Q#Od`^@O2M5 zC!$iK?dO^m5r&6r{uKa&Oa|$&D~+hAi=B`A3*bXntm3$MaW|bd6jI7bYK%at16shN z)r*|Jud+!<&P(dd!$gOFRFBNhF_yCEdz>K86oz;O2$KrA=>8_2kTDEJ?a z9lecXPkjQAW&j^6jjsyWZ0?hK+n^ld-w(e2`?AL0UO$%Bzh9`c>Kh@`FgKN@!FJ4| zAvq*n9k0Y52O^t~P8|6Nwc+IA)p|a+6fmp=p#xE6c`EZUMMCp0DqYh4*&&%6S$`9! zo~H;{PR50Rjk>5R^(>^ORxcZ15m$iX&^Vk@g_dG5_$nd=7fewlwA*_1 zf#hPt^zW}UwR!5t=RoSqBFLXbR5Gx|kTf~n*+w?_`Q0TK;7jz)@eY=h0P+rqegHHQ zTMp$B0;#4zo`%~qz@7njX7pQ$&oIh9hPf%SR85~#{0AxEdxfYc#>-J$;;E4 zd-6yXyu8>m7s&kvbBwhR3Eo94HS|?s>5uN zVJvlKPzR3-wU{e8%v88h0TCGBZ_-Hu=fn=A{Z)XuFWWtmTu@Hsvq4m9C^;*_>owtHqso4h|M4ByFq)ZOQtHZf zmm^CbQz0c)*(RmTOU)e9ykL=uw}C{%T(XK|ndsj^(jt4^GU{FS!U6&dn6@Ya;QlI5 zMkl+nXecMNSHfA*l@U;mV>>H;Y7Sza8V*t`X3r|$cS@%la|)|T&PT*^9BRuwwyKyf zDCH}K_HrcFal>>RFnz?*n2_q2Lw>%Wb?orryC=p`KNP7gGGKQq1Co+^(R`-UKgaC& zy#A5a&aEQ*>0Hoq?sT-z#Iv6^>fUB*-$q=0W{wL=Q++0fQ5+$ssl+x?xJ+!eA0G{P zt>i>b8cHg5Fa3o&Y0wZRx{7-zph$#Af&PQv_fgl`XstNK4V?CtISudhf=DZY1C91o z54Q*|jn-i1L#ZA+xe$DC<+_bp8f_sf4$?=>AbJW?nxpk9VxT>b8LQFMFJS_@u3Xn6 zp;MgDW7An|9E{pp9_#!79*aolq-HbfK6}yw`NM=Hugz&?lQ_zUI*t$LGC_Jmv85)V zX)Uo}rtsg*)2ezSvisqNk|_>6gTojiH8d>>Y^0Bg!n9xw9t~TO-mFrS?h=uA^X_z> z(h-5M3pL-DQ)XU+l?a+IHm+W6G$sR;VmT2hM##8rb*LJ*Ca6e{UP%wp`Jz|IC38R5 zud@quSQ&Q&a^Iig)M&B6;SMKm>5$F_`4=)5bdLUNf5#?>SIlV(N@1auKMFw>ua$vR z+*bS6MyMHOy-CZP*=*@4Yy{0-+j0wzu3Iq&bDwF5I03WNa%WZ0{E`(<@mngpB7Npj dS8&qoi@xBvDe(W3q$y6_1E5C Date: Tue, 21 Jul 2026 12:48:42 +0900 Subject: [PATCH 16/87] Add bounded native scientific artifact runtime --- src/codex_science/artifact_runtime.py | 727 ++++++++++++++++++++++++++ 1 file changed, 727 insertions(+) create mode 100644 src/codex_science/artifact_runtime.py diff --git a/src/codex_science/artifact_runtime.py b/src/codex_science/artifact_runtime.py new file mode 100644 index 00000000..57f9a358 --- /dev/null +++ b/src/codex_science/artifact_runtime.py @@ -0,0 +1,727 @@ +"""Bounded native scientific artifact views, selections, and edit proposals. + +The runtime deliberately separates three concepts: + +* a *view* is a bounded, derived description of validated artifact bytes; +* a *selection* is a typed, hash-bound pointer into those bytes; +* a *transform proposal* is a non-executing request that can feed impact and + selective-rerun planning. + +None of these objects are scientific evidence by themselves and no function in +this module mutates the source artifact. +""" + +from __future__ import annotations + +import csv +import hashlib +import html +import io +import json +import struct +from dataclasses import asdict, dataclass +from datetime import datetime, timezone +from pathlib import Path, PurePosixPath +from typing import Any, Iterable, Mapping + +from codex_science.artifact_store import describe_directory, stream_sha256 + + +DEFAULT_MAX_BYTES = 1024 * 1024 +DEFAULT_MAX_RECORDS = 100 + +VIEWER_SELECTIONS: dict[str, frozenset[str]] = { + "structure-3d": frozenset({"atom", "residue", "chain", "ligand", "spatial-region"}), + "molecule-2d": frozenset({"atom", "bond", "substructure", "record"}), + "genome-track": frozenset({"variant", "locus", "interval", "track-record"}), + "single-cell": frozenset({"cell", "cell-set", "feature", "cluster", "donor", "layer"}), + "table": frozenset({"row", "column", "cell", "range"}), + "figure": frozenset({"region", "axis", "legend", "panel"}), + "evidence-graph": frozenset({"node", "edge", "claim", "component"}), + "trajectory": frozenset({"frame", "frame-range", "atom", "atom-set"}), + "directory": frozenset({"entry", "entry-set"}), + "text": frozenset({"line", "line-range", "heading", "json-pointer"}), + "binary": frozenset({"byte-range", "record"}), +} + +STRUCTURE_SUFFIXES = {".pdb", ".ent", ".cif", ".mmcif"} +MOLECULE_SUFFIXES = {".sdf", ".mol", ".mol2", ".smi", ".smiles"} +GENOME_SUFFIXES = {".vcf", ".bed", ".gff", ".gff3", ".gtf", ".wig", ".bedgraph", ".bigwig", ".bw"} +SINGLE_CELL_SUFFIXES = {".h5ad", ".loom"} +TABLE_SUFFIXES = {".csv", ".tsv", ".parquet", ".feather", ".arrow"} +FIGURE_SUFFIXES = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".tif", ".tiff"} +GRAPH_SUFFIXES = {".graph.json", ".cyjs"} +TRAJECTORY_SUFFIXES = {".xyz", ".dcd", ".xtc", ".trr", ".nc", ".netcdf"} +TEXT_SUFFIXES = {".txt", ".md", ".json", ".jsonl", ".yaml", ".yml", ".toml", ".py", ".r", ".jl", ".sh"} + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _fingerprint(value: Mapping[str, Any]) -> str: + return hashlib.sha256(_canonical_bytes(value)).hexdigest() + + +def _safe_relative(value: str, label: str = "path") -> str: + pure = PurePosixPath(value) + if not value or pure.is_absolute() or ".." in pure.parts: + raise ValueError(f"{label} must be a safe relative path: {value}") + return pure.as_posix() + + +def _sha256(value: Any, label: str) -> str: + text = str(value).strip().lower() + if len(text) != 64 or any(character not in "0123456789abcdef" for character in text): + raise ValueError(f"{label} must be a SHA-256 digest") + return text + + +def _json_value(value: Any, label: str) -> None: + if value is None or isinstance(value, (str, int, float, bool)): + return + if isinstance(value, list): + for index, item in enumerate(value): + _json_value(item, f"{label}[{index}]") + return + if isinstance(value, dict): + for key, item in value.items(): + if not isinstance(key, str): + raise ValueError(f"{label} object keys must be strings") + _json_value(item, f"{label}.{key}") + return + raise ValueError(f"{label} must contain JSON-compatible values") + + +def _read_head(path: Path, max_bytes: int) -> tuple[bytes, bool]: + if max_bytes < 1: + raise ValueError("max_bytes must be positive") + with path.open("rb") as handle: + data = handle.read(max_bytes + 1) + return data[:max_bytes], len(data) > max_bytes + + +def _decode_text(data: bytes) -> tuple[str, str]: + for encoding in ("utf-8", "utf-8-sig", "latin-1"): + try: + return data.decode(encoding), encoding + except UnicodeDecodeError: + continue + return data.decode("utf-8", errors="replace"), "utf-8-replacement" + + +def detect_viewer(path: Path, *, kind: str = "", media_type: str | None = None) -> str: + if path.is_dir(): + return "directory" + lower = path.name.lower() + suffix = path.suffix.lower() + kind_lower = kind.lower() + media = (media_type or "").lower() + if suffix in STRUCTURE_SUFFIXES or "structure" in kind_lower or "chemical/x-pdb" in media: + return "structure-3d" + if suffix in MOLECULE_SUFFIXES or "molecule" in kind_lower or "chemical/x-mdl" in media: + return "molecule-2d" + if suffix in GENOME_SUFFIXES or any(token in kind_lower for token in ("variant", "genome", "track")): + return "genome-track" + if suffix in SINGLE_CELL_SUFFIXES or "anndata" in kind_lower or "single-cell" in kind_lower: + return "single-cell" + if suffix in TABLE_SUFFIXES or media in {"text/csv", "text/tab-separated-values", "application/vnd.apache.parquet"}: + return "table" + if suffix in FIGURE_SUFFIXES or media.startswith("image/") or "figure" in kind_lower: + return "figure" + if lower.endswith(".graph.json") or suffix in GRAPH_SUFFIXES or "evidence-graph" in kind_lower: + return "evidence-graph" + if suffix in TRAJECTORY_SUFFIXES or "trajectory" in kind_lower: + return "trajectory" + if suffix in TEXT_SUFFIXES or media.startswith("text/") or media.endswith("+json") or media == "application/json": + return "text" + return "binary" + + +def _bounded(items: Iterable[Any], maximum: int) -> tuple[list[Any], bool]: + result: list[Any] = [] + iterator = iter(items) + for _ in range(maximum): + try: + result.append(next(iterator)) + except StopIteration: + return result, False + try: + next(iterator) + except StopIteration: + return result, False + return result, True + + +def _structure_preview(path: Path, max_bytes: int, max_records: int) -> tuple[dict[str, Any], list[str]]: + data, truncated_bytes = _read_head(path, max_bytes) + text, encoding = _decode_text(data) + warnings: list[str] = [] + atoms: list[dict[str, Any]] = [] + chains: set[str] = set() + residues: set[str] = set() + ligands: set[str] = set() + if path.suffix.lower() in {".pdb", ".ent"}: + for line in text.splitlines(): + if not line.startswith(("ATOM ", "HETATM")): + continue + try: + atom = { + "record": line[:6].strip(), + "serial": int(line[6:11].strip() or 0), + "atom": line[12:16].strip(), + "altloc": line[16:17].strip(), + "resname": line[17:20].strip(), + "chain": line[21:22].strip(), + "resseq": line[22:26].strip(), + "x": float(line[30:38]), + "y": float(line[38:46]), + "z": float(line[46:54]), + "element": line[76:78].strip() if len(line) >= 78 else "", + } + except (ValueError, IndexError): + warnings.append("At least one coordinate record could not be normalized.") + continue + chains.add(atom["chain"] or "_") + residues.add(f"{atom['chain'] or '_'}:{atom['resname']}:{atom['resseq']}") + if atom["record"] == "HETATM" and atom["resname"] not in {"HOH", "WAT"}: + ligands.add(atom["resname"]) + if len(atoms) < max_records: + atoms.append(atom) + preview = { + "format": "pdb", + "encoding": encoding, + "atoms_preview": atoms, + "chains_seen": sorted(chains), + "residues_seen": len(residues), + "ligands_seen": sorted(ligands), + } + else: + categories = sorted({line.split(".", 1)[0] for line in text.splitlines() if line.startswith("_")}) + atom_lines = [line for line in text.splitlines() if line.startswith("ATOM ") or line.startswith("HETATM ")] + preview = { + "format": "mmcif", + "encoding": encoding, + "categories_preview": categories[:max_records], + "coordinate_rows_preview": atom_lines[:max_records], + } + if len(categories) > max_records or len(atom_lines) > max_records: + warnings.append("The mmCIF preview is record bounded.") + if truncated_bytes: + warnings.append("The structure preview is byte bounded; counts describe only the inspected prefix.") + return preview, warnings + + +def _molecule_preview(path: Path, max_bytes: int, max_records: int) -> tuple[dict[str, Any], list[str]]: + data, truncated_bytes = _read_head(path, max_bytes) + text, encoding = _decode_text(data) + warnings: list[str] = [] + suffix = path.suffix.lower() + if suffix in {".smi", ".smiles"}: + records = [] + for line_number, line in enumerate(text.splitlines(), 1): + stripped = line.strip() + if not stripped or stripped.startswith("#"): + continue + fields = stripped.split() + records.append({"line": line_number, "smiles": fields[0], "name": " ".join(fields[1:])}) + if len(records) >= max_records: + break + preview = {"format": "smiles", "encoding": encoding, "records_preview": records} + else: + blocks = text.split("$$$$") + records = [] + for index, block in enumerate(blocks): + lines = block.strip("\r\n").splitlines() + if not lines: + continue + atom_count = bond_count = None + if len(lines) >= 4: + counts = lines[3] + try: + atom_count = int(counts[0:3]) + bond_count = int(counts[3:6]) + except ValueError: + pass + properties = [line[3:-1] for line in lines if line.startswith("> <") and line.endswith(">")] + records.append({ + "record_index": index, + "name": lines[0].strip() if lines else "", + "atom_count": atom_count, + "bond_count": bond_count, + "properties": properties[:25], + }) + if len(records) >= max_records: + break + preview = {"format": suffix.lstrip(".") or "mol", "encoding": encoding, "records_preview": records} + if truncated_bytes: + warnings.append("The molecular preview is byte bounded and may end inside a record.") + return preview, warnings + + +def _genome_preview(path: Path, max_bytes: int, max_records: int) -> tuple[dict[str, Any], list[str]]: + if path.suffix.lower() in {".bigwig", ".bw"}: + data, truncated = _read_head(path, min(max_bytes, 4096)) + return ({"format": "bigwig", "magic_hex": data[:4].hex(), "header_bytes_preview": data[:64].hex()}, ["bigWig is represented by bounded binary metadata; track values require a dedicated indexed reader."] + (["Header preview truncated."] if truncated else [])) + data, truncated_bytes = _read_head(path, max_bytes) + text, encoding = _decode_text(data) + headers: list[str] = [] + records: list[dict[str, Any]] = [] + suffix = path.suffix.lower() + for line_number, line in enumerate(text.splitlines(), 1): + if not line: + continue + if line.startswith("#") or line.startswith("track") or line.startswith("browser"): + if len(headers) < 50: + headers.append(line) + continue + fields = line.split("\t") + if suffix == ".vcf" and len(fields) >= 8: + record = {"line": line_number, "chrom": fields[0], "pos": fields[1], "id": fields[2], "ref": fields[3], "alt": fields[4], "qual": fields[5], "filter": fields[6]} + elif len(fields) >= 3: + record = {"line": line_number, "chrom": fields[0], "start": fields[1], "end": fields[2], "fields": fields[3:10]} + else: + record = {"line": line_number, "raw": line[:500]} + records.append(record) + if len(records) >= max_records: + break + warnings = ["The genome-track preview is byte bounded."] if truncated_bytes else [] + return {"format": suffix.lstrip("."), "encoding": encoding, "headers_preview": headers, "records_preview": records}, warnings + + +def _table_preview(path: Path, max_bytes: int, max_records: int) -> tuple[dict[str, Any], list[str]]: + suffix = path.suffix.lower() + if suffix in {".parquet", ".feather", ".arrow"}: + data, truncated = _read_head(path, min(max_bytes, 8192)) + magic = {"prefix": data[:8].hex(), "suffix_available": False} + if path.stat().st_size >= 8: + with path.open("rb") as handle: + handle.seek(max(0, path.stat().st_size - 8)) + magic["suffix"] = handle.read(8).hex() + magic["suffix_available"] = True + return ({"format": suffix.lstrip("."), "binary_metadata": magic}, ["Column statistics and rows require a dedicated Arrow/Parquet reader; no schema is inferred from filename alone."] + (["Header preview truncated."] if truncated else [])) + data, truncated_bytes = _read_head(path, max_bytes) + text, encoding = _decode_text(data) + delimiter = "\t" if suffix == ".tsv" else "," + try: + dialect = csv.Sniffer().sniff(text[:8192], delimiters=",\t;|") + delimiter = dialect.delimiter + except csv.Error: + pass + reader = csv.reader(io.StringIO(text), delimiter=delimiter) + rows, more = _bounded(reader, max_records + 1) + header = rows[0] if rows else [] + body = rows[1:max_records + 1] if rows else [] + warnings = [] + if truncated_bytes or more: + warnings.append("The table preview is bounded and is not a complete row count.") + return {"format": "delimited-text", "encoding": encoding, "delimiter": delimiter, "columns": header, "rows_preview": body}, warnings + + +def _png_dimensions(data: bytes) -> tuple[int, int] | None: + if len(data) >= 24 and data.startswith(b"\x89PNG\r\n\x1a\n"): + return struct.unpack(">II", data[16:24]) + return None + + +def _jpeg_dimensions(data: bytes) -> tuple[int, int] | None: + if not data.startswith(b"\xff\xd8"): + return None + offset = 2 + while offset + 9 < len(data): + if data[offset] != 0xFF: + offset += 1 + continue + marker = data[offset + 1] + offset += 2 + if marker in {0xD8, 0xD9}: + continue + if offset + 2 > len(data): + break + length = int.from_bytes(data[offset:offset + 2], "big") + if marker in {0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF} and offset + 7 < len(data): + height = int.from_bytes(data[offset + 3:offset + 5], "big") + width = int.from_bytes(data[offset + 5:offset + 7], "big") + return width, height + if length < 2: + break + offset += length + return None + + +def _figure_preview(path: Path, max_bytes: int) -> tuple[dict[str, Any], list[str]]: + data, truncated = _read_head(path, min(max_bytes, 2 * 1024 * 1024)) + dimensions = _png_dimensions(data) or _jpeg_dimensions(data) + preview: dict[str, Any] = {"format": path.suffix.lower().lstrip("."), "dimensions": None if dimensions is None else {"width": dimensions[0], "height": dimensions[1]}} + warnings: list[str] = [] + if path.suffix.lower() == ".svg": + text, encoding = _decode_text(data) + preview["encoding"] = encoding + preview["markup_preview"] = text[:5000] + warnings.append("SVG markup is escaped in the runtime and is never executed as active content.") + if dimensions is None and path.suffix.lower() not in {".svg"}: + warnings.append("Image dimensions were not decoded by the bounded standard-library reader.") + if truncated: + warnings.append("Figure byte preview was truncated.") + return preview, warnings + + +def _graph_preview(path: Path, max_bytes: int, max_records: int) -> tuple[dict[str, Any], list[str]]: + data, truncated = _read_head(path, max_bytes) + warnings: list[str] = [] + try: + payload = json.loads(data.decode("utf-8")) + except (UnicodeDecodeError, json.JSONDecodeError): + return {"format": "json", "parsed": False, "text_preview": _decode_text(data)[0][:5000]}, ["The bounded prefix did not contain a complete JSON graph."] + nodes = payload.get("nodes", []) if isinstance(payload, dict) else [] + edges = payload.get("edges", []) if isinstance(payload, dict) else [] + if not isinstance(nodes, list) or not isinstance(edges, list): + warnings.append("JSON does not expose list-valued nodes and edges.") + nodes, edges = [], [] + preview = { + "format": "evidence-graph-json", + "schema_version": payload.get("schema_version") if isinstance(payload, dict) else None, + "node_count": len(nodes), + "edge_count": len(edges), + "nodes_preview": nodes[:max_records], + "edges_preview": edges[:max_records], + } + if truncated: + warnings.append("Graph JSON happened to parse from a bounded prefix; verify full artifact validation before relying on counts.") + return preview, warnings + + +def _trajectory_preview(path: Path, max_bytes: int, max_records: int) -> tuple[dict[str, Any], list[str]]: + if path.suffix.lower() != ".xyz": + data, truncated = _read_head(path, min(max_bytes, 4096)) + return ({"format": path.suffix.lower().lstrip("."), "header_hex": data[:128].hex()}, ["Binary trajectory frames require a format-specific reader; only bounded metadata is exposed."] + (["Header preview truncated."] if truncated else [])) + data, truncated = _read_head(path, max_bytes) + text, encoding = _decode_text(data) + lines = text.splitlines() + offset = 0 + frames: list[dict[str, Any]] = [] + while offset < len(lines) and len(frames) < max_records: + try: + atom_count = int(lines[offset].strip()) + except (ValueError, IndexError): + break + comment = lines[offset + 1] if offset + 1 < len(lines) else "" + atom_lines = lines[offset + 2:offset + 2 + atom_count] + if len(atom_lines) < atom_count: + break + frames.append({"frame": len(frames), "atom_count": atom_count, "comment": comment, "atoms_preview": atom_lines[:20]}) + offset += atom_count + 2 + warnings = ["XYZ trajectory preview is byte or frame bounded."] if truncated or offset < len(lines) else [] + return {"format": "xyz", "encoding": encoding, "frames_preview": frames}, warnings + + +def _single_cell_preview(path: Path, max_bytes: int) -> tuple[dict[str, Any], list[str]]: + data, truncated = _read_head(path, min(max_bytes, 8192)) + is_hdf5 = data.startswith(b"\x89HDF\r\n\x1a\n") + return ({"format": path.suffix.lower().lstrip("."), "container": "hdf5" if is_hdf5 else "unknown", "header_hex": data[:128].hex()}, ["AnnData/Loom matrices are not loaded without an explicit HDF5-aware backend; dimensions, layers, donors, and embeddings must not be guessed."] + (["Header preview truncated."] if truncated else [])) + + +def _directory_preview(path: Path, max_records: int, media_type: str | None) -> tuple[dict[str, Any], list[str]]: + descriptor = describe_directory(path, media_type=media_type) + return ({"format": "directory-tree", "root_sha256": descriptor.root_sha256, "total_bytes": descriptor.total_bytes, "entry_count": descriptor.entry_count, "entries_preview": list(descriptor.entries[:max_records])}, ["Directory view is entry bounded."] if descriptor.entry_count > max_records else []) + + +def _text_preview(path: Path, max_bytes: int, max_records: int) -> tuple[dict[str, Any], list[str]]: + data, truncated = _read_head(path, max_bytes) + text, encoding = _decode_text(data) + lines = text.splitlines() + preview: dict[str, Any] = {"encoding": encoding, "line_count_in_prefix": len(lines), "lines_preview": [{"line": index + 1, "text": line} for index, line in enumerate(lines[:max_records])]} + if path.suffix.lower() == ".json" and not truncated: + try: + payload = json.loads(text) + preview["json_type"] = type(payload).__name__ + if isinstance(payload, dict): + preview["top_level_keys"] = sorted(map(str, payload.keys()))[:max_records] + except json.JSONDecodeError: + preview["json_type"] = "invalid" + return preview, ["Text preview is byte or line bounded."] if truncated or len(lines) > max_records else [] + + +def _binary_preview(path: Path, max_bytes: int) -> tuple[dict[str, Any], list[str]]: + data, truncated = _read_head(path, min(max_bytes, 4096)) + return {"header_hex": data[:256].hex(), "header_ascii": "".join(chr(byte) if 32 <= byte < 127 else "." for byte in data[:256])}, ["Binary preview is metadata only."] + (["Header preview truncated."] if truncated else []) + + +@dataclass(frozen=True) +class ArtifactRuntimeDescriptor: + schema_version: int + artifact_path: str + artifact_sha256: str + artifact_kind: str + artifact_type: str + media_type: str | None + size_bytes: int + viewer: str + generated_at: str + bounds: dict[str, int] + preview: dict[str, Any] + warnings: tuple[str, ...] + evidence_boundary: str + fingerprint: str + + def to_dict(self) -> dict[str, Any]: + payload = asdict(self) + payload["warnings"] = list(self.warnings) + return payload + + +def describe_runtime( + path: Path, + *, + artifact_path: str | None = None, + artifact_sha256: str | None = None, + kind: str = "artifact", + artifact_type: str | None = None, + media_type: str | None = None, + max_bytes: int = DEFAULT_MAX_BYTES, + max_records: int = DEFAULT_MAX_RECORDS, + generated_at: str | None = None, +) -> ArtifactRuntimeDescriptor: + path = path.resolve() + if not path.exists(): + raise ValueError(f"artifact does not exist: {path}") + if max_records < 1: + raise ValueError("max_records must be positive") + relative = _safe_relative(artifact_path or path.name, "artifact_path") + viewer = detect_viewer(path, kind=kind, media_type=media_type) + if path.is_dir(): + directory = describe_directory(path, media_type=media_type) + digest, size, resolved_type = directory.root_sha256, directory.total_bytes, "directory-tree" + else: + digest, size = stream_sha256(path) + resolved_type = artifact_type or "file" + if artifact_sha256 is not None and _sha256(artifact_sha256, "artifact_sha256") != digest: + raise ValueError("artifact bytes do not match artifact_sha256") + parsers = { + "structure-3d": lambda: _structure_preview(path, max_bytes, max_records), + "molecule-2d": lambda: _molecule_preview(path, max_bytes, max_records), + "genome-track": lambda: _genome_preview(path, max_bytes, max_records), + "single-cell": lambda: _single_cell_preview(path, max_bytes), + "table": lambda: _table_preview(path, max_bytes, max_records), + "figure": lambda: _figure_preview(path, max_bytes), + "evidence-graph": lambda: _graph_preview(path, max_bytes, max_records), + "trajectory": lambda: _trajectory_preview(path, max_bytes, max_records), + "directory": lambda: _directory_preview(path, max_records, media_type), + "text": lambda: _text_preview(path, max_bytes, max_records), + "binary": lambda: _binary_preview(path, max_bytes), + } + preview, warnings = parsers[viewer]() + material = { + "schema_version": 1, + "artifact_path": relative, + "artifact_sha256": digest, + "artifact_kind": kind, + "artifact_type": resolved_type, + "media_type": media_type, + "size_bytes": size, + "viewer": viewer, + "generated_at": generated_at or _now(), + "bounds": {"max_bytes": max_bytes, "max_records": max_records}, + "preview": preview, + "warnings": warnings, + "evidence_boundary": "This is a bounded derived view of hash-validated bytes. It does not prove a scientific claim, execute an edit, or replace format-specific validation.", + } + return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings)) + + +def validate_runtime_descriptor(payload: Mapping[str, Any]) -> None: + if payload.get("schema_version") != 1: + raise ValueError("unsupported artifact runtime descriptor schema") + _safe_relative(str(payload.get("artifact_path", "")), "artifact_path") + _sha256(payload.get("artifact_sha256"), "artifact_sha256") + viewer = str(payload.get("viewer", "")) + if viewer not in VIEWER_SELECTIONS: + raise ValueError(f"unsupported artifact viewer: {viewer}") + if int(payload.get("size_bytes", -1)) < 0: + raise ValueError("size_bytes must be non-negative") + for field in ("artifact_kind", "artifact_type", "generated_at", "evidence_boundary"): + if not str(payload.get(field, "")).strip(): + raise ValueError(f"{field} is required") + if not isinstance(payload.get("preview"), Mapping) or not isinstance(payload.get("bounds"), Mapping): + raise ValueError("preview and bounds must be objects") + warnings = payload.get("warnings") + if not isinstance(warnings, list) or not all(isinstance(item, str) for item in warnings): + raise ValueError("warnings must be a string list") + material = dict(payload) + fingerprint = _sha256(material.pop("fingerprint", ""), "fingerprint") + if _fingerprint(material) != fingerprint: + raise ValueError("artifact runtime descriptor fingerprint mismatch") + + +def build_selection( + descriptor: Mapping[str, Any], + *, + selector_type: str, + selector: Mapping[str, Any], + selected_by: str, + reason: str, + label: str = "", + created_at: str | None = None, +) -> dict[str, Any]: + validate_runtime_descriptor(descriptor) + viewer = str(descriptor["viewer"]) + if selector_type not in VIEWER_SELECTIONS[viewer]: + raise ValueError(f"selector {selector_type} is not valid for viewer {viewer}") + if not isinstance(selector, Mapping) or not selector: + raise ValueError("selector must be a non-empty object") + _json_value(dict(selector), "selector") + for value, field in ((selected_by, "selected_by"), (reason, "reason")): + if not value.strip(): + raise ValueError(f"{field} is required") + material = { + "schema_version": 1, + "artifact_path": descriptor["artifact_path"], + "artifact_sha256": descriptor["artifact_sha256"], + "viewer": viewer, + "selector_type": selector_type, + "selector": dict(selector), + "selected_by": selected_by, + "reason": reason, + "label": label, + "created_at": created_at or _now(), + "status": "active", + "evidence_boundary": "A selection identifies part of an artifact; it does not validate the selected interpretation.", + } + fingerprint = _fingerprint(material) + return {**material, "selection_id": f"selection-{fingerprint[:20]}", "fingerprint": fingerprint} + + +def validate_selection(payload: Mapping[str, Any], artifact_hashes: Mapping[str, str] | None = None) -> None: + if payload.get("schema_version") != 1: + raise ValueError("unsupported artifact selection schema") + artifact_path = _safe_relative(str(payload.get("artifact_path", "")), "artifact_path") + artifact_sha = _sha256(payload.get("artifact_sha256"), "artifact_sha256") + viewer = str(payload.get("viewer", "")) + selector_type = str(payload.get("selector_type", "")) + if viewer not in VIEWER_SELECTIONS or selector_type not in VIEWER_SELECTIONS[viewer]: + raise ValueError("artifact selection viewer or selector_type is invalid") + if not isinstance(payload.get("selector"), Mapping) or not payload["selector"]: + raise ValueError("artifact selection selector must be non-empty") + for field in ("selection_id", "selected_by", "reason", "created_at", "status", "evidence_boundary"): + if not str(payload.get(field, "")).strip(): + raise ValueError(f"selection {field} is required") + material = dict(payload) + fingerprint = _sha256(material.pop("fingerprint", ""), "selection fingerprint") + material.pop("selection_id", None) + if _fingerprint(material) != fingerprint: + raise ValueError("artifact selection fingerprint mismatch") + if str(payload["selection_id"]) != f"selection-{fingerprint[:20]}": + raise ValueError("artifact selection ID does not match fingerprint") + if artifact_hashes is not None and artifact_hashes.get(artifact_path) != artifact_sha: + raise ValueError("stale artifact selection anchor") + + +def stale_selection(payload: Mapping[str, Any], artifact_hashes: Mapping[str, str]) -> dict[str, Any]: + validate_selection(payload) + result = dict(payload) + if artifact_hashes.get(str(payload["artifact_path"])) != str(payload["artifact_sha256"]).lower(): + result["status"] = "stale-anchor" + return result + + +def build_transform_proposal( + selection: Mapping[str, Any], + *, + operation: str, + parameters: Mapping[str, Any], + reason: str, + affected_steps: Iterable[str], + expected_outputs: Iterable[str], + proposed_by: str, + requires_approval: bool = True, + approval_boundary: str = "artifact mutation and rerun", + created_at: str | None = None, +) -> dict[str, Any]: + validate_selection(selection) + if not operation.strip() or not reason.strip() or not proposed_by.strip(): + raise ValueError("operation, reason, and proposed_by are required") + if not isinstance(parameters, Mapping): + raise ValueError("parameters must be an object") + _json_value(dict(parameters), "parameters") + steps = sorted({_safe_relative(str(item), "affected step") for item in affected_steps}) + outputs = sorted({_safe_relative(str(item), "expected output") for item in expected_outputs}) + if not steps: + raise ValueError("affected_steps must be non-empty") + material = { + "schema_version": 1, + "selection_id": selection["selection_id"], + "selection_fingerprint": selection["fingerprint"], + "artifact_path": selection["artifact_path"], + "artifact_sha256": selection["artifact_sha256"], + "operation": operation, + "parameters": dict(parameters), + "reason": reason, + "affected_steps": steps, + "expected_outputs": outputs, + "proposed_by": proposed_by, + "requires_approval": bool(requires_approval), + "approval_boundary": approval_boundary, + "created_at": created_at or _now(), + "status": "proposed", + "executed": False, + "evidence_boundary": "This proposal describes a possible transform. It does not mutate bytes, execute a workflow, or establish a result.", + } + fingerprint = _fingerprint(material) + return {**material, "proposal_id": f"transform-{fingerprint[:20]}", "fingerprint": fingerprint} + + +def validate_transform_proposal(payload: Mapping[str, Any], selection: Mapping[str, Any] | None = None) -> None: + if payload.get("schema_version") != 1: + raise ValueError("unsupported transform proposal schema") + for field in ("proposal_id", "selection_id", "selection_fingerprint", "artifact_path", "artifact_sha256", "operation", "reason", "proposed_by", "approval_boundary", "created_at", "status", "evidence_boundary"): + if not str(payload.get(field, "")).strip(): + raise ValueError(f"transform proposal {field} is required") + _safe_relative(str(payload["artifact_path"]), "artifact_path") + _sha256(payload["artifact_sha256"], "artifact_sha256") + _sha256(payload["selection_fingerprint"], "selection_fingerprint") + if payload.get("executed") is not False or payload.get("status") != "proposed": + raise ValueError("a transform proposal must remain non-executing and proposed") + for field in ("affected_steps", "expected_outputs"): + if not isinstance(payload.get(field), list) or (field == "affected_steps" and not payload[field]): + raise ValueError(f"{field} must be a {'non-empty ' if field == 'affected_steps' else ''}list") + if not isinstance(payload.get("parameters"), Mapping): + raise ValueError("transform proposal parameters must be an object") + material = dict(payload) + fingerprint = _sha256(material.pop("fingerprint", ""), "transform fingerprint") + material.pop("proposal_id", None) + if _fingerprint(material) != fingerprint or str(payload["proposal_id"]) != f"transform-{fingerprint[:20]}": + raise ValueError("transform proposal fingerprint or ID mismatch") + if selection is not None: + validate_selection(selection) + if payload["selection_id"] != selection["selection_id"] or payload["selection_fingerprint"] != selection["fingerprint"]: + raise ValueError("transform proposal references a different selection") + + +def render_runtime_html(descriptor: Mapping[str, Any], *, title: str | None = None) -> str: + validate_runtime_descriptor(descriptor) + viewer = html.escape(str(descriptor["viewer"])) + artifact = html.escape(str(descriptor["artifact_path"])) + heading = html.escape(title or f"Scientific artifact: {descriptor['artifact_path']}") + preview_json = html.escape(json.dumps(descriptor["preview"], indent=2, sort_keys=True, ensure_ascii=False)) + warnings = "".join(f"
  • {html.escape(str(item))}
  • " for item in descriptor.get("warnings", [])) or "
  • None
  • " + metadata = { + "SHA-256": descriptor["artifact_sha256"], + "Kind": descriptor["artifact_kind"], + "Type": descriptor["artifact_type"], + "Media type": descriptor.get("media_type"), + "Size": descriptor["size_bytes"], + "Viewer": descriptor["viewer"], + "Fingerprint": descriptor["fingerprint"], + } + cards = "".join(f"
    {html.escape(str(key))}
    {html.escape(str(value))}
    " for key, value in metadata.items()) + return f""" + +{heading} + +

    {heading}

    Artifact: {artifact} · Viewer: {viewer}

    +
    {cards}

    Warnings

      {warnings}

    Bounded preview

    {preview_json}
    +

    {html.escape(str(descriptor['evidence_boundary']))}

    \n""" From f216c904dae0b1463c615acd99c68bd48801ce1b Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:49:20 +0900 Subject: [PATCH 17/87] Add native artifact runtime CLI --- scripts/render_artifact_runtime.py | 163 +++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 scripts/render_artifact_runtime.py diff --git a/scripts/render_artifact_runtime.py b/scripts/render_artifact_runtime.py new file mode 100644 index 00000000..9426b5af --- /dev/null +++ b/scripts/render_artifact_runtime.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +"""Describe, render, select, or propose changes to a scientific artifact. + +This CLI always writes structured output to a file. It never mutates the source +artifact and never executes a transform proposal. +""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.artifact_runtime import ( # noqa: E402 + build_selection, + build_transform_proposal, + describe_runtime, + render_runtime_html, + validate_runtime_descriptor, + validate_selection, +) + + +def _read_json(path: Path) -> dict[str, Any]: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"JSON object required: {path}") + return payload + + +def _write_json(path: Path, payload: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + print(f"Success! Data written to: {path.resolve()}") + + +def _json_argument(value: str, label: str) -> dict[str, Any]: + try: + payload = json.loads(value) + except json.JSONDecodeError as error: + raise ValueError(f"{label} must be valid JSON: {error}") from error + if not isinstance(payload, dict): + raise ValueError(f"{label} must be a JSON object") + return payload + + +def command_describe(args: argparse.Namespace) -> None: + descriptor = describe_runtime( + args.artifact, + artifact_path=args.artifact_path, + artifact_sha256=args.sha256, + kind=args.kind, + artifact_type=args.artifact_type, + media_type=args.media_type, + max_bytes=args.max_bytes, + max_records=args.max_records, + ) + _write_json(args.output, descriptor.to_dict()) + + +def command_render(args: argparse.Namespace) -> None: + descriptor = _read_json(args.descriptor) + validate_runtime_descriptor(descriptor) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(render_runtime_html(descriptor, title=args.title), encoding="utf-8") + print(f"Success! Runtime view written to: {args.output.resolve()}") + + +def command_select(args: argparse.Namespace) -> None: + descriptor = _read_json(args.descriptor) + selection = build_selection( + descriptor, + selector_type=args.selector_type, + selector=_json_argument(args.selector, "--selector"), + selected_by=args.selected_by, + reason=args.reason, + label=args.label, + ) + _write_json(args.output, selection) + + +def command_propose(args: argparse.Namespace) -> None: + selection = _read_json(args.selection) + validate_selection(selection) + proposal = build_transform_proposal( + selection, + operation=args.operation, + parameters=_json_argument(args.parameters, "--parameters"), + reason=args.reason, + affected_steps=args.affected_step, + expected_outputs=args.expected_output, + proposed_by=args.proposed_by, + requires_approval=not args.no_approval_required, + approval_boundary=args.approval_boundary, + ) + _write_json(args.output, proposal) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=__doc__) + subcommands = parser.add_subparsers(dest="command", required=True) + + describe = subcommands.add_parser("describe", help="Build a bounded runtime descriptor") + describe.add_argument("artifact", type=Path) + describe.add_argument("--artifact-path", required=True, help="Run-relative artifact path") + describe.add_argument("--sha256", help="Expected manifest SHA-256") + describe.add_argument("--kind", required=True) + describe.add_argument("--artifact-type", default="file") + describe.add_argument("--media-type") + describe.add_argument("--max-bytes", type=int, required=True) + describe.add_argument("--max-records", type=int, required=True) + describe.add_argument("--output", type=Path, required=True) + describe.set_defaults(handler=command_describe) + + render = subcommands.add_parser("render", help="Render a validated offline HTML view") + render.add_argument("descriptor", type=Path) + render.add_argument("--title") + render.add_argument("--output", type=Path, required=True) + render.set_defaults(handler=command_render) + + select = subcommands.add_parser("select", help="Create a typed hash-bound selection") + select.add_argument("descriptor", type=Path) + select.add_argument("--selector-type", required=True) + select.add_argument("--selector", required=True, help="JSON object") + select.add_argument("--selected-by", required=True) + select.add_argument("--reason", required=True) + select.add_argument("--label", default="") + select.add_argument("--output", type=Path, required=True) + select.set_defaults(handler=command_select) + + propose = subcommands.add_parser("propose", help="Create a non-executing transform proposal") + propose.add_argument("selection", type=Path) + propose.add_argument("--operation", required=True) + propose.add_argument("--parameters", required=True, help="JSON object") + propose.add_argument("--reason", required=True) + propose.add_argument("--affected-step", action="append", required=True) + propose.add_argument("--expected-output", action="append", default=[]) + propose.add_argument("--proposed-by", required=True) + propose.add_argument("--approval-boundary", default="artifact mutation and rerun") + propose.add_argument("--no-approval-required", action="store_true") + propose.add_argument("--output", type=Path, required=True) + propose.set_defaults(handler=command_propose) + return parser + + +def main() -> int: + parser = build_parser() + args = parser.parse_args() + try: + args.handler(args) + except (OSError, ValueError, json.JSONDecodeError) as error: + print(f"error: {error}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 4ac302c17098bc3eee4cdf90385e7d00162c9416 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:51:14 +0900 Subject: [PATCH 18/87] Add project-level evidence store and hypothesis forks --- src/codex_science/project_store.py | 598 +++++++++++++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 src/codex_science/project_store.py diff --git a/src/codex_science/project_store.py b/src/codex_science/project_store.py new file mode 100644 index 00000000..5d1bbd12 --- /dev/null +++ b/src/codex_science/project_store.py @@ -0,0 +1,598 @@ +"""Local project-level evidence memory over immutable scientific run bundles. + +The SQLite store is an index and decision ledger. A run manifest and its hashed +artifacts remain authoritative. Import, fork, compare, and merge-plan operations +never rewrite a run bundle. +""" + +from __future__ import annotations + +import hashlib +import json +import sqlite3 +from contextlib import contextmanager +from dataclasses import dataclass +from datetime import datetime, timezone +from pathlib import Path, PurePosixPath +from typing import Any, Iterator, Mapping + +from codex_science.artifact_store import stream_sha256 +from codex_science.artifacts import validate_bundle +from codex_science.collaboration import diff_runs + + +SCHEMA_VERSION = 1 +POLARITIES = {"supports", "contradicts", "qualifies", "neutral", "unavailable"} +BRANCH_STATUSES = {"active", "candidate", "merged", "abandoned"} + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _sha256_json(value: Any) -> str: + return hashlib.sha256(_canonical_bytes(value)).hexdigest() + + +def _text(value: Any, label: str) -> str: + text = str(value).strip() + if not text: + raise ValueError(f"{label} is required") + return text + + +def _sha(value: Any, label: str) -> str: + text = _text(value, label).lower() + if len(text) != 64 or any(character not in "0123456789abcdef" for character in text): + raise ValueError(f"{label} must be a SHA-256 digest") + return text + + +def _relative(value: Any, label: str) -> str: + text = _text(value, label) + pure = PurePosixPath(text) + if pure.is_absolute() or ".." in pure.parts: + raise ValueError(f"{label} must be a safe relative path") + return pure.as_posix() + + +def _json_object(value: Any, label: str) -> dict[str, Any]: + if not isinstance(value, Mapping): + raise ValueError(f"{label} must be an object") + payload = dict(value) + try: + json.dumps(payload, ensure_ascii=False) + except (TypeError, ValueError) as error: + raise ValueError(f"{label} must be JSON-compatible") from error + return payload + + +def _manifest_claims(manifest: Mapping[str, Any]) -> dict[str, dict[str, Any]]: + return {str(item["id"]): dict(item) for item in manifest.get("claims", []) if isinstance(item, Mapping) and item.get("id")} + + +def _manifest_artifacts(manifest: Mapping[str, Any]) -> dict[str, dict[str, Any]]: + return {str(item["path"]): dict(item) for item in manifest.get("artifacts", []) if isinstance(item, Mapping) and item.get("path")} + + +@dataclass(frozen=True) +class ImportedRun: + project_id: str + run_id: str + branch_name: str + parent_run_id: str | None + manifest_path: str + manifest_sha256: str + review_status: str + imported_at: str + + def to_dict(self) -> dict[str, Any]: + return { + "project_id": self.project_id, + "run_id": self.run_id, + "branch_name": self.branch_name, + "parent_run_id": self.parent_run_id, + "manifest_path": self.manifest_path, + "manifest_sha256": self.manifest_sha256, + "review_status": self.review_status, + "imported_at": self.imported_at, + } + + +class ProjectStore: + def __init__(self, path: Path) -> None: + self.path = path.resolve() + + @contextmanager + def connect(self) -> Iterator[sqlite3.Connection]: + self.path.parent.mkdir(parents=True, exist_ok=True) + connection = sqlite3.connect(self.path) + connection.row_factory = sqlite3.Row + connection.execute("PRAGMA foreign_keys = ON") + connection.execute("PRAGMA journal_mode = WAL") + try: + yield connection + connection.commit() + except BaseException: + connection.rollback() + raise + finally: + connection.close() + + def initialize(self) -> None: + with self.connect() as connection: + connection.executescript( + """ + CREATE TABLE IF NOT EXISTS metadata ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS projects ( + project_id TEXT PRIMARY KEY, + title TEXT NOT NULL, + question TEXT NOT NULL, + created_at TEXT NOT NULL, + status TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS runs ( + project_id TEXT NOT NULL, + run_id TEXT NOT NULL, + branch_name TEXT NOT NULL, + parent_run_id TEXT, + manifest_path TEXT NOT NULL, + manifest_sha256 TEXT NOT NULL, + question TEXT NOT NULL, + review_status TEXT NOT NULL, + imported_at TEXT NOT NULL, + PRIMARY KEY (project_id, run_id), + FOREIGN KEY (project_id) REFERENCES projects(project_id), + FOREIGN KEY (project_id, parent_run_id) REFERENCES runs(project_id, run_id) + ); + CREATE TABLE IF NOT EXISTS run_artifacts ( + project_id TEXT NOT NULL, + run_id TEXT NOT NULL, + artifact_path TEXT NOT NULL, + sha256 TEXT NOT NULL, + kind TEXT NOT NULL, + artifact_type TEXT NOT NULL, + payload_json TEXT NOT NULL, + PRIMARY KEY (project_id, run_id, artifact_path), + FOREIGN KEY (project_id, run_id) REFERENCES runs(project_id, run_id) ON DELETE CASCADE + ); + CREATE TABLE IF NOT EXISTS run_claims ( + project_id TEXT NOT NULL, + run_id TEXT NOT NULL, + claim_id TEXT NOT NULL, + payload_json TEXT NOT NULL, + PRIMARY KEY (project_id, run_id, claim_id), + FOREIGN KEY (project_id, run_id) REFERENCES runs(project_id, run_id) ON DELETE CASCADE + ); + CREATE TABLE IF NOT EXISTS branches ( + project_id TEXT NOT NULL, + branch_name TEXT NOT NULL, + base_run_id TEXT NOT NULL, + head_run_id TEXT NOT NULL, + status TEXT NOT NULL, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL, + PRIMARY KEY (project_id, branch_name), + FOREIGN KEY (project_id, base_run_id) REFERENCES runs(project_id, run_id), + FOREIGN KEY (project_id, head_run_id) REFERENCES runs(project_id, run_id) + ); + CREATE TABLE IF NOT EXISTS assertions ( + project_id TEXT NOT NULL, + assertion_id TEXT NOT NULL, + run_id TEXT NOT NULL, + claim_id TEXT NOT NULL, + source_id TEXT NOT NULL, + polarity TEXT NOT NULL, + effect_measure TEXT, + estimate REAL, + interval_low REAL, + interval_high REAL, + unit TEXT, + sample_size INTEGER, + population TEXT, + independence_group TEXT NOT NULL, + risk_of_bias_ref TEXT, + locator_json TEXT NOT NULL, + created_at TEXT NOT NULL, + fingerprint TEXT NOT NULL, + PRIMARY KEY (project_id, assertion_id), + FOREIGN KEY (project_id, run_id) REFERENCES runs(project_id, run_id), + FOREIGN KEY (project_id, run_id, claim_id) REFERENCES run_claims(project_id, run_id, claim_id) + ); + CREATE TABLE IF NOT EXISTS merge_plans ( + project_id TEXT NOT NULL, + plan_id TEXT NOT NULL, + source_branch TEXT NOT NULL, + target_branch TEXT NOT NULL, + base_run_id TEXT NOT NULL, + source_run_id TEXT NOT NULL, + target_run_id TEXT NOT NULL, + status TEXT NOT NULL, + payload_json TEXT NOT NULL, + created_at TEXT NOT NULL, + fingerprint TEXT NOT NULL, + PRIMARY KEY (project_id, plan_id) + ); + CREATE TABLE IF NOT EXISTS events ( + project_id TEXT NOT NULL, + sequence INTEGER NOT NULL, + event_type TEXT NOT NULL, + payload_json TEXT NOT NULL, + previous_event_sha256 TEXT, + event_sha256 TEXT NOT NULL, + created_at TEXT NOT NULL, + PRIMARY KEY (project_id, sequence) + ); + """ + ) + current = connection.execute("SELECT value FROM metadata WHERE key='schema_version'").fetchone() + if current is None: + connection.execute("INSERT INTO metadata(key, value) VALUES('schema_version', ?)", (str(SCHEMA_VERSION),)) + elif int(current["value"]) != SCHEMA_VERSION: + raise ValueError(f"unsupported project store schema: {current['value']}") + + def _require_project(self, connection: sqlite3.Connection, project_id: str) -> None: + if connection.execute("SELECT 1 FROM projects WHERE project_id=?", (project_id,)).fetchone() is None: + raise ValueError(f"unknown project: {project_id}") + + def _require_run(self, connection: sqlite3.Connection, project_id: str, run_id: str) -> sqlite3.Row: + row = connection.execute("SELECT * FROM runs WHERE project_id=? AND run_id=?", (project_id, run_id)).fetchone() + if row is None: + raise ValueError(f"unknown run: {project_id}/{run_id}") + return row + + def _require_branch(self, connection: sqlite3.Connection, project_id: str, branch_name: str) -> sqlite3.Row: + row = connection.execute("SELECT * FROM branches WHERE project_id=? AND branch_name=?", (project_id, branch_name)).fetchone() + if row is None: + raise ValueError(f"unknown branch: {project_id}/{branch_name}") + return row + + def _append_event(self, connection: sqlite3.Connection, project_id: str, event_type: str, payload: Mapping[str, Any]) -> str: + row = connection.execute("SELECT sequence, event_sha256 FROM events WHERE project_id=? ORDER BY sequence DESC LIMIT 1", (project_id,)).fetchone() + sequence = 1 if row is None else int(row["sequence"]) + 1 + previous = None if row is None else str(row["event_sha256"]) + created_at = _now() + material = { + "project_id": project_id, + "sequence": sequence, + "event_type": event_type, + "payload": dict(payload), + "previous_event_sha256": previous, + "created_at": created_at, + } + digest = _sha256_json(material) + connection.execute( + "INSERT INTO events(project_id, sequence, event_type, payload_json, previous_event_sha256, event_sha256, created_at) VALUES(?,?,?,?,?,?,?)", + (project_id, sequence, event_type, json.dumps(dict(payload), sort_keys=True), previous, digest, created_at), + ) + return digest + + def create_project(self, *, project_id: str, title: str, question: str) -> dict[str, Any]: + self.initialize() + project_id = _text(project_id, "project_id") + title = _text(title, "title") + question = _text(question, "question") + created_at = _now() + with self.connect() as connection: + existing = connection.execute("SELECT * FROM projects WHERE project_id=?", (project_id,)).fetchone() + if existing is not None: + if existing["title"] == title and existing["question"] == question: + return dict(existing) + raise ValueError(f"project already exists with different metadata: {project_id}") + connection.execute( + "INSERT INTO projects(project_id, title, question, created_at, status) VALUES(?,?,?,?,?)", + (project_id, title, question, created_at, "active"), + ) + self._append_event(connection, project_id, "project-created", {"title": title, "question": question}) + return {"project_id": project_id, "title": title, "question": question, "created_at": created_at, "status": "active"} + + def import_run( + self, + *, + project_id: str, + manifest_path: Path, + branch_name: str = "main", + parent_run_id: str | None = None, + ) -> ImportedRun: + self.initialize() + project_id = _text(project_id, "project_id") + branch_name = _text(branch_name, "branch_name") + manifest_path = manifest_path.resolve() + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + if not isinstance(manifest, dict): + raise ValueError("run manifest must be an object") + validate_bundle(manifest, manifest_path.parent) + run_id = _text(manifest.get("run_id"), "manifest run_id") + digest, _size = stream_sha256(manifest_path) + question = _text(manifest.get("question"), "manifest question") + review_status = str(manifest.get("review", {}).get("status", "unknown")) + imported_at = _now() + with self.connect() as connection: + self._require_project(connection, project_id) + if parent_run_id is not None: + self._require_run(connection, project_id, parent_run_id) + existing = connection.execute("SELECT * FROM runs WHERE project_id=? AND run_id=?", (project_id, run_id)).fetchone() + if existing is not None: + if existing["manifest_sha256"] != digest: + raise ValueError(f"run ID already exists with different manifest bytes: {run_id}") + return ImportedRun( + project_id, run_id, str(existing["branch_name"]), existing["parent_run_id"], + str(existing["manifest_path"]), digest, str(existing["review_status"]), str(existing["imported_at"]), + ) + branch = connection.execute("SELECT * FROM branches WHERE project_id=? AND branch_name=?", (project_id, branch_name)).fetchone() + if branch is not None and parent_run_id is None: + parent_run_id = str(branch["head_run_id"]) + connection.execute( + "INSERT INTO runs(project_id, run_id, branch_name, parent_run_id, manifest_path, manifest_sha256, question, review_status, imported_at) VALUES(?,?,?,?,?,?,?,?,?)", + (project_id, run_id, branch_name, parent_run_id, str(manifest_path), digest, question, review_status, imported_at), + ) + for artifact_path, artifact in sorted(_manifest_artifacts(manifest).items()): + connection.execute( + "INSERT INTO run_artifacts(project_id, run_id, artifact_path, sha256, kind, artifact_type, payload_json) VALUES(?,?,?,?,?,?,?)", + ( + project_id, run_id, artifact_path, _sha(artifact.get("sha256"), "artifact sha256"), + str(artifact.get("kind", "artifact")), str(artifact.get("artifact_type", "file")), + json.dumps(artifact, sort_keys=True), + ), + ) + for claim_id, claim in sorted(_manifest_claims(manifest).items()): + connection.execute( + "INSERT INTO run_claims(project_id, run_id, claim_id, payload_json) VALUES(?,?,?,?)", + (project_id, run_id, claim_id, json.dumps(claim, sort_keys=True)), + ) + if branch is None: + connection.execute( + "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", + (project_id, branch_name, run_id, run_id, "active", imported_at, imported_at), + ) + else: + connection.execute( + "UPDATE branches SET head_run_id=?, updated_at=? WHERE project_id=? AND branch_name=?", + (run_id, imported_at, project_id, branch_name), + ) + self._append_event( + connection, + project_id, + "run-imported", + {"run_id": run_id, "branch_name": branch_name, "parent_run_id": parent_run_id, "manifest_sha256": digest, "review_status": review_status}, + ) + return ImportedRun(project_id, run_id, branch_name, parent_run_id, str(manifest_path), digest, review_status, imported_at) + + def fork_branch(self, *, project_id: str, source_run_id: str, branch_name: str) -> dict[str, Any]: + self.initialize() + project_id = _text(project_id, "project_id") + source_run_id = _text(source_run_id, "source_run_id") + branch_name = _text(branch_name, "branch_name") + created_at = _now() + with self.connect() as connection: + self._require_project(connection, project_id) + self._require_run(connection, project_id, source_run_id) + if connection.execute("SELECT 1 FROM branches WHERE project_id=? AND branch_name=?", (project_id, branch_name)).fetchone() is not None: + raise ValueError(f"branch already exists: {branch_name}") + connection.execute( + "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", + (project_id, branch_name, source_run_id, source_run_id, "active", created_at, created_at), + ) + self._append_event(connection, project_id, "branch-forked", {"branch_name": branch_name, "source_run_id": source_run_id}) + return {"project_id": project_id, "branch_name": branch_name, "base_run_id": source_run_id, "head_run_id": source_run_id, "status": "active", "created_at": created_at} + + def add_assertion( + self, + *, + project_id: str, + run_id: str, + claim_id: str, + source_id: str, + polarity: str, + locator: Mapping[str, Any], + independence_group: str, + effect_measure: str | None = None, + estimate: float | None = None, + interval_low: float | None = None, + interval_high: float | None = None, + unit: str | None = None, + sample_size: int | None = None, + population: str | None = None, + risk_of_bias_ref: str | None = None, + ) -> dict[str, Any]: + self.initialize() + for value, label in ((project_id, "project_id"), (run_id, "run_id"), (claim_id, "claim_id"), (source_id, "source_id"), (independence_group, "independence_group")): + _text(value, label) + if polarity not in POLARITIES: + raise ValueError(f"invalid assertion polarity: {polarity}") + locator_payload = _json_object(locator, "locator") + artifact_path = _relative(locator_payload.get("artifact_path"), "locator artifact_path") + artifact_sha = _sha(locator_payload.get("artifact_sha256"), "locator artifact_sha256") + if not any(locator_payload.get(field) not in (None, "") for field in ("page", "table", "figure", "cell", "json_pointer", "line_range", "record_id")): + raise ValueError("assertion locator needs a page, table, figure, cell, JSON pointer, line range, or record ID") + locator_payload["artifact_path"] = artifact_path + locator_payload["artifact_sha256"] = artifact_sha + if sample_size is not None and (isinstance(sample_size, bool) or sample_size < 0): + raise ValueError("sample_size must be a non-negative integer") + if (interval_low is None) != (interval_high is None): + raise ValueError("both interval bounds must be provided together") + if interval_low is not None and interval_high is not None and interval_low > interval_high: + raise ValueError("interval_low cannot exceed interval_high") + created_at = _now() + material = { + "project_id": project_id, + "run_id": run_id, + "claim_id": claim_id, + "source_id": source_id, + "polarity": polarity, + "effect_measure": effect_measure, + "estimate": estimate, + "interval_low": interval_low, + "interval_high": interval_high, + "unit": unit, + "sample_size": sample_size, + "population": population, + "independence_group": independence_group, + "risk_of_bias_ref": risk_of_bias_ref, + "locator": locator_payload, + "created_at": created_at, + } + fingerprint = _sha256_json(material) + assertion_id = f"assertion-{fingerprint[:20]}" + with self.connect() as connection: + self._require_run(connection, project_id, run_id) + if connection.execute("SELECT 1 FROM run_claims WHERE project_id=? AND run_id=? AND claim_id=?", (project_id, run_id, claim_id)).fetchone() is None: + raise ValueError(f"claim is not present in imported run: {claim_id}") + artifact = connection.execute( + "SELECT sha256 FROM run_artifacts WHERE project_id=? AND run_id=? AND artifact_path=?", + (project_id, run_id, artifact_path), + ).fetchone() + if artifact is None or str(artifact["sha256"]) != artifact_sha: + raise ValueError("assertion locator does not match an imported artifact hash") + existing = connection.execute("SELECT fingerprint FROM assertions WHERE project_id=? AND assertion_id=?", (project_id, assertion_id)).fetchone() + if existing is not None: + return {**material, "assertion_id": assertion_id, "fingerprint": fingerprint} + connection.execute( + """INSERT INTO assertions( + project_id, assertion_id, run_id, claim_id, source_id, polarity, + effect_measure, estimate, interval_low, interval_high, unit, + sample_size, population, independence_group, risk_of_bias_ref, + locator_json, created_at, fingerprint + ) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", + ( + project_id, assertion_id, run_id, claim_id, source_id, polarity, + effect_measure, estimate, interval_low, interval_high, unit, + sample_size, population, independence_group, risk_of_bias_ref, + json.dumps(locator_payload, sort_keys=True), created_at, fingerprint, + ), + ) + self._append_event(connection, project_id, "assertion-added", {"assertion_id": assertion_id, "run_id": run_id, "claim_id": claim_id, "fingerprint": fingerprint}) + return {**material, "assertion_id": assertion_id, "fingerprint": fingerprint} + + def _load_manifest_for_run(self, connection: sqlite3.Connection, project_id: str, run_id: str) -> dict[str, Any]: + row = self._require_run(connection, project_id, run_id) + path = Path(str(row["manifest_path"])) + digest, _size = stream_sha256(path) + if digest != str(row["manifest_sha256"]): + raise ValueError(f"imported run manifest changed on disk: {run_id}") + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"run manifest is not an object: {run_id}") + return payload + + def compare_runs(self, *, project_id: str, previous_run_id: str, current_run_id: str) -> dict[str, Any]: + self.initialize() + with self.connect() as connection: + self._require_project(connection, project_id) + previous = self._load_manifest_for_run(connection, project_id, previous_run_id) + current = self._load_manifest_for_run(connection, project_id, current_run_id) + result = diff_runs(previous, current) + result["project_id"] = project_id + return result + + @staticmethod + def _three_way_conflicts(base: Mapping[str, Any], source: Mapping[str, Any], target: Mapping[str, Any], key: str) -> list[str]: + base_items = {str(item.get(key)): item for item in base.values() if isinstance(item, Mapping)} if isinstance(base, Mapping) else {} + source_items = {str(item.get(key)): item for item in source.values() if isinstance(item, Mapping)} if isinstance(source, Mapping) else {} + target_items = {str(item.get(key)): item for item in target.values() if isinstance(item, Mapping)} if isinstance(target, Mapping) else {} + conflicts = [] + for identifier in sorted(source_items.keys() & target_items.keys()): + source_changed = source_items[identifier] != base_items.get(identifier) + target_changed = target_items[identifier] != base_items.get(identifier) + if source_changed and target_changed and source_items[identifier] != target_items[identifier]: + conflicts.append(identifier) + return conflicts + + def prepare_merge_plan(self, *, project_id: str, source_branch: str, target_branch: str) -> dict[str, Any]: + self.initialize() + project_id = _text(project_id, "project_id") + created_at = _now() + with self.connect() as connection: + source = self._require_branch(connection, project_id, source_branch) + target = self._require_branch(connection, project_id, target_branch) + source_run = str(source["head_run_id"]) + target_run = str(target["head_run_id"]) + base_candidates = {str(source["base_run_id"]), str(target["base_run_id"])} + if str(source["base_run_id"]) == str(target["base_run_id"]): + base_run = str(source["base_run_id"]) + elif str(source["base_run_id"]) == target_run: + base_run = target_run + elif str(target["base_run_id"]) == source_run: + base_run = source_run + else: + # The store does not invent a lowest common ancestor. Choose the + # source fork point and report the ambiguity explicitly. + base_run = str(source["base_run_id"]) + base_manifest = self._load_manifest_for_run(connection, project_id, base_run) + source_manifest = self._load_manifest_for_run(connection, project_id, source_run) + target_manifest = self._load_manifest_for_run(connection, project_id, target_run) + source_diff = diff_runs(base_manifest, source_manifest) + target_diff = diff_runs(base_manifest, target_manifest) + base_claims, source_claims, target_claims = (_manifest_claims(item) for item in (base_manifest, source_manifest, target_manifest)) + base_artifacts, source_artifacts, target_artifacts = (_manifest_artifacts(item) for item in (base_manifest, source_manifest, target_manifest)) + claim_conflicts = sorted( + claim_id for claim_id in source_claims.keys() & target_claims.keys() + if source_claims[claim_id] != base_claims.get(claim_id) + and target_claims[claim_id] != base_claims.get(claim_id) + and source_claims[claim_id] != target_claims[claim_id] + ) + artifact_conflicts = sorted( + artifact_path for artifact_path in source_artifacts.keys() & target_artifacts.keys() + if source_artifacts[artifact_path] != base_artifacts.get(artifact_path) + and target_artifacts[artifact_path] != base_artifacts.get(artifact_path) + and source_artifacts[artifact_path] != target_artifacts[artifact_path] + ) + payload = { + "schema_version": 1, + "project_id": project_id, + "source_branch": source_branch, + "target_branch": target_branch, + "base_run_id": base_run, + "source_run_id": source_run, + "target_run_id": target_run, + "source_diff": source_diff, + "target_diff": target_diff, + "claim_conflicts": claim_conflicts, + "artifact_conflicts": artifact_conflicts, + "base_ambiguity": len(base_candidates) > 1 and base_run not in {source_run, target_run}, + "review_receipts_invalidated": bool( + source_diff.get("review_invalidated") or target_diff.get("review_invalidated") + ), + "requires_scientific_review": True, + "status": "blocked-conflicts" if claim_conflicts or artifact_conflicts else "candidate", + "executed": False, + "created_at": created_at, + "evidence_boundary": "A merge plan compares immutable run records. It does not combine artifact bytes, resolve scientific disagreements, or mark a branch merged.", + } + fingerprint = _sha256_json(payload) + plan_id = f"merge-{fingerprint[:20]}" + connection.execute( + "INSERT OR IGNORE INTO merge_plans(project_id, plan_id, source_branch, target_branch, base_run_id, source_run_id, target_run_id, status, payload_json, created_at, fingerprint) VALUES(?,?,?,?,?,?,?,?,?,?,?)", + (project_id, plan_id, source_branch, target_branch, base_run, source_run, target_run, payload["status"], json.dumps(payload, sort_keys=True), created_at, fingerprint), + ) + self._append_event(connection, project_id, "merge-plan-created", {"plan_id": plan_id, "source_branch": source_branch, "target_branch": target_branch, "fingerprint": fingerprint}) + return {**payload, "plan_id": plan_id, "fingerprint": fingerprint} + + def summary(self, *, project_id: str) -> dict[str, Any]: + self.initialize() + with self.connect() as connection: + project = connection.execute("SELECT * FROM projects WHERE project_id=?", (project_id,)).fetchone() + if project is None: + raise ValueError(f"unknown project: {project_id}") + branches = [dict(row) for row in connection.execute("SELECT * FROM branches WHERE project_id=? ORDER BY branch_name", (project_id,))] + runs = [dict(row) for row in connection.execute("SELECT run_id, branch_name, parent_run_id, manifest_sha256, review_status, imported_at FROM runs WHERE project_id=? ORDER BY imported_at, run_id", (project_id,))] + assertion_count = int(connection.execute("SELECT COUNT(*) AS n FROM assertions WHERE project_id=?", (project_id,)).fetchone()["n"]) + merge_count = int(connection.execute("SELECT COUNT(*) AS n FROM merge_plans WHERE project_id=?", (project_id,)).fetchone()["n"]) + last_event = connection.execute("SELECT sequence, event_sha256, created_at FROM events WHERE project_id=? ORDER BY sequence DESC LIMIT 1", (project_id,)).fetchone() + return { + "schema_version": 1, + "project": dict(project), + "branches": branches, + "runs": runs, + "assertion_count": assertion_count, + "merge_plan_count": merge_count, + "event_chain_head": None if last_event is None else dict(last_event), + "evidence_boundary": "The project store indexes imported immutable run bundles; it is not a substitute for their manifests, reviews, or scientific evidence.", + } From 5b776191cf4e2d23f307b579021f8d08d62b402e Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:51:50 +0900 Subject: [PATCH 19/87] Add project evidence store CLI --- scripts/science_project.py | 170 +++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 scripts/science_project.py diff --git a/scripts/science_project.py b/scripts/science_project.py new file mode 100644 index 00000000..6b009113 --- /dev/null +++ b/scripts/science_project.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +"""Manage a local project evidence store over immutable Codex Science runs. + +Every subcommand writes its result to --output. The merge-plan command is +non-executing and never mutates imported run bundles. +""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.project_store import ProjectStore # noqa: E402 + + +def _write(path: Path, payload: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + print(f"Success! Data written to: {path.resolve()}") + + +def _json_object(value: str, label: str) -> dict[str, Any]: + try: + payload = json.loads(value) + except json.JSONDecodeError as error: + raise ValueError(f"{label} must be valid JSON: {error}") from error + if not isinstance(payload, dict): + raise ValueError(f"{label} must be a JSON object") + return payload + + +def create(args: argparse.Namespace) -> None: + store = ProjectStore(args.database) + _write(args.output, store.create_project(project_id=args.project_id, title=args.title, question=args.question)) + + +def import_run(args: argparse.Namespace) -> None: + store = ProjectStore(args.database) + record = store.import_run(project_id=args.project_id, manifest_path=args.manifest, branch_name=args.branch, parent_run_id=args.parent_run) + _write(args.output, record.to_dict()) + + +def fork(args: argparse.Namespace) -> None: + store = ProjectStore(args.database) + _write(args.output, store.fork_branch(project_id=args.project_id, source_run_id=args.source_run, branch_name=args.branch)) + + +def assertion(args: argparse.Namespace) -> None: + store = ProjectStore(args.database) + payload = store.add_assertion( + project_id=args.project_id, + run_id=args.run_id, + claim_id=args.claim_id, + source_id=args.source_id, + polarity=args.polarity, + locator=_json_object(args.locator, "--locator"), + independence_group=args.independence_group, + effect_measure=args.effect_measure, + estimate=args.estimate, + interval_low=args.interval_low, + interval_high=args.interval_high, + unit=args.unit, + sample_size=args.sample_size, + population=args.population, + risk_of_bias_ref=args.risk_of_bias_ref, + ) + _write(args.output, payload) + + +def compare(args: argparse.Namespace) -> None: + store = ProjectStore(args.database) + _write(args.output, store.compare_runs(project_id=args.project_id, previous_run_id=args.previous_run, current_run_id=args.current_run)) + + +def merge_plan(args: argparse.Namespace) -> None: + store = ProjectStore(args.database) + _write(args.output, store.prepare_merge_plan(project_id=args.project_id, source_branch=args.source_branch, target_branch=args.target_branch)) + + +def summary(args: argparse.Namespace) -> None: + store = ProjectStore(args.database) + _write(args.output, store.summary(project_id=args.project_id)) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--database", type=Path, required=True) + subcommands = parser.add_subparsers(dest="command", required=True) + + command = subcommands.add_parser("init", help="Initialize a project") + command.add_argument("--project-id", required=True) + command.add_argument("--title", required=True) + command.add_argument("--question", required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=create) + + command = subcommands.add_parser("import-run", help="Validate and import an immutable run") + command.add_argument("--project-id", required=True) + command.add_argument("--manifest", type=Path, required=True) + command.add_argument("--branch", required=True) + command.add_argument("--parent-run") + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=import_run) + + command = subcommands.add_parser("fork", help="Fork a project branch from an imported run") + command.add_argument("--project-id", required=True) + command.add_argument("--source-run", required=True) + command.add_argument("--branch", required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=fork) + + command = subcommands.add_parser("assert", help="Add a quantitative, located evidence assertion") + command.add_argument("--project-id", required=True) + command.add_argument("--run-id", required=True) + command.add_argument("--claim-id", required=True) + command.add_argument("--source-id", required=True) + command.add_argument("--polarity", choices=["supports", "contradicts", "qualifies", "neutral", "unavailable"], required=True) + command.add_argument("--locator", required=True, help="JSON object with artifact path/hash and an exact locator") + command.add_argument("--independence-group", required=True) + command.add_argument("--effect-measure") + command.add_argument("--estimate", type=float) + command.add_argument("--interval-low", type=float) + command.add_argument("--interval-high", type=float) + command.add_argument("--unit") + command.add_argument("--sample-size", type=int) + command.add_argument("--population") + command.add_argument("--risk-of-bias-ref") + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=assertion) + + command = subcommands.add_parser("compare", help="Compare two imported run manifests") + command.add_argument("--project-id", required=True) + command.add_argument("--previous-run", required=True) + command.add_argument("--current-run", required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=compare) + + command = subcommands.add_parser("merge-plan", help="Prepare a non-executing scientific merge plan") + command.add_argument("--project-id", required=True) + command.add_argument("--source-branch", required=True) + command.add_argument("--target-branch", required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=merge_plan) + + command = subcommands.add_parser("summary", help="Summarize project lineage and event-chain state") + command.add_argument("--project-id", required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=summary) + return parser + + +def main() -> int: + parser = build_parser() + args = parser.parse_args() + try: + args.handler(args) + except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc] + print(f"error: {error}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 5b728bdc44e9ff7b198919ddb2c2763bd4cec0a3 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:54:59 +0900 Subject: [PATCH 20/87] Add durable local and Slurm job backends --- src/codex_science/compute_backends.py | 719 ++++++++++++++++++++++++++ 1 file changed, 719 insertions(+) create mode 100644 src/codex_science/compute_backends.py diff --git a/src/codex_science/compute_backends.py b/src/codex_science/compute_backends.py new file mode 100644 index 00000000..5c3edabe --- /dev/null +++ b/src/codex_science/compute_backends.py @@ -0,0 +1,719 @@ +"""Durable local and Slurm scientific job execution contracts. + +The local backend is fully executable. The Slurm backend submits only after an +explicit, hash-bound approval receipt and uses an existing scheduler +configuration. Neither backend invents credentials, installs software, or +interprets scientific success from a zero process exit code. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import shlex +import shutil +import signal +import subprocess +import sys +import tempfile +import time +import uuid +from dataclasses import asdict, dataclass +from datetime import datetime, timezone +from pathlib import Path, PurePosixPath +from typing import Any, Mapping, Protocol + +from codex_science.artifact_store import describe_directory, stream_sha256 + + +TERMINAL_STATES = {"completed", "failed", "timed-out", "cancelled", "lost"} +ALL_STATES = {"submitted", "running", *TERMINAL_STATES} +SECRET_KEY_FRAGMENTS = {"token", "secret", "password", "credential", "private_key", "api_key", "apikey"} +SLURM_STATE_MAP = { + "COMPLETED": "completed", + "CANCELLED": "cancelled", + "TIMEOUT": "timed-out", + "FAILED": "failed", + "NODE_FAIL": "failed", + "OUT_OF_MEMORY": "failed", + "PREEMPTED": "failed", + "BOOT_FAIL": "failed", + "DEADLINE": "timed-out", + "RUNNING": "running", + "PENDING": "submitted", + "CONFIGURING": "submitted", + "COMPLETING": "running", +} + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _fingerprint(value: Mapping[str, Any]) -> str: + return hashlib.sha256(_canonical_bytes(value)).hexdigest() + + +def _atomic_json(path: Path, payload: Mapping[str, Any]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + descriptor, temporary_name = tempfile.mkstemp(prefix=".state-", suffix=".json", dir=path.parent) + os.close(descriptor) + temporary = Path(temporary_name) + try: + temporary.write_text(json.dumps(dict(payload), indent=2, sort_keys=True) + "\n", encoding="utf-8") + temporary.replace(path) + finally: + temporary.unlink(missing_ok=True) + + +def _read_json(path: Path) -> dict[str, Any]: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"JSON object required: {path}") + return payload + + +def _text(value: Any, label: str) -> str: + text = str(value).strip() + if not text: + raise ValueError(f"{label} is required") + return text + + +def _safe_relative(value: Any, label: str) -> str: + text = _text(value, label) + pure = PurePosixPath(text) + if pure.is_absolute() or ".." in pure.parts: + raise ValueError(f"{label} must be a safe relative path") + return pure.as_posix() + + +def _reject_secret_environment(environment: Mapping[str, Any]) -> dict[str, str]: + result: dict[str, str] = {} + for key, value in environment.items(): + key_text = _text(key, "environment key") + normalized = key_text.lower() + if any(fragment in normalized for fragment in SECRET_KEY_FRAGMENTS): + raise ValueError(f"secret-like environment keys cannot be recorded in a job spec: {key_text}") + result[key_text] = str(value) + return result + + +@dataclass(frozen=True) +class ResourceRequest: + cpus: int = 1 + memory_mb: int = 1024 + gpus: int = 0 + gpu_type: str | None = None + wall_time_seconds: int = 3600 + partition: str | None = None + account: str | None = None + qos: str | None = None + + def validate(self) -> None: + for field in ("cpus", "memory_mb", "wall_time_seconds"): + value = int(getattr(self, field)) + if value < 1: + raise ValueError(f"resource {field} must be positive") + if self.gpus < 0: + raise ValueError("resource gpus must be non-negative") + + @classmethod + def from_payload(cls, payload: Mapping[str, Any] | None) -> "ResourceRequest": + source = dict(payload or {}) + request = cls( + cpus=int(source.get("cpus", 1)), + memory_mb=int(source.get("memory_mb", 1024)), + gpus=int(source.get("gpus", 0)), + gpu_type=None if source.get("gpu_type") is None else str(source["gpu_type"]), + wall_time_seconds=int(source.get("wall_time_seconds", 3600)), + partition=None if source.get("partition") is None else str(source["partition"]), + account=None if source.get("account") is None else str(source["account"]), + qos=None if source.get("qos") is None else str(source["qos"]), + ) + request.validate() + return request + + +@dataclass(frozen=True) +class JobSpec: + schema_version: int + backend: str + name: str + command: tuple[str, ...] + working_directory: str + environment: dict[str, str] + inherit_environment: bool + inputs: tuple[dict[str, Any], ...] + outputs: tuple[str, ...] + resources: ResourceRequest + timeout_seconds: int + cost_cap: float | None + approval_required: bool + scientific_run_id: str | None + checkpoint_paths: tuple[str, ...] + + @classmethod + def from_payload(cls, payload: Mapping[str, Any]) -> "JobSpec": + if payload.get("schema_version", 1) != 1: + raise ValueError("unsupported job spec schema") + backend = _text(payload.get("backend"), "backend") + if backend not in {"local", "slurm"}: + raise ValueError(f"unsupported job backend: {backend}") + command_raw = payload.get("command") + if not isinstance(command_raw, list) or not command_raw: + raise ValueError("command must be a non-empty argv list") + command = tuple(_text(item, "command item") for item in command_raw) + environment_raw = payload.get("environment", {}) + if not isinstance(environment_raw, Mapping): + raise ValueError("environment must be an object") + inputs_raw = payload.get("inputs", []) + if not isinstance(inputs_raw, list) or not all(isinstance(item, Mapping) for item in inputs_raw): + raise ValueError("inputs must be object records") + outputs_raw = payload.get("outputs", []) + if not isinstance(outputs_raw, list): + raise ValueError("outputs must be a list") + checkpoints_raw = payload.get("checkpoint_paths", []) + if not isinstance(checkpoints_raw, list): + raise ValueError("checkpoint_paths must be a list") + timeout = int(payload.get("timeout_seconds", 3600)) + if timeout < 1: + raise ValueError("timeout_seconds must be positive") + cost_cap = None if payload.get("cost_cap") is None else float(payload["cost_cap"]) + if cost_cap is not None and cost_cap < 0: + raise ValueError("cost_cap must be non-negative") + spec = cls( + schema_version=1, + backend=backend, + name=_text(payload.get("name"), "name"), + command=command, + working_directory=str(Path(_text(payload.get("working_directory"), "working_directory")).resolve()), + environment=_reject_secret_environment(environment_raw), + inherit_environment=bool(payload.get("inherit_environment", True)), + inputs=tuple(dict(item) for item in inputs_raw), + outputs=tuple(_safe_relative(item, "output") for item in outputs_raw), + resources=ResourceRequest.from_payload(payload.get("resources") if isinstance(payload.get("resources"), Mapping) else None), + timeout_seconds=timeout, + cost_cap=cost_cap, + approval_required=bool(payload.get("approval_required", backend != "local")), + scientific_run_id=None if payload.get("scientific_run_id") is None else str(payload["scientific_run_id"]), + checkpoint_paths=tuple(_safe_relative(item, "checkpoint path") for item in checkpoints_raw), + ) + spec.validate() + return spec + + def validate(self) -> None: + if self.backend not in {"local", "slurm"}: + raise ValueError("unsupported backend") + if not self.command: + raise ValueError("job command cannot be empty") + self.resources.validate() + working = Path(self.working_directory) + if not working.is_dir(): + raise ValueError(f"working_directory is not a directory: {working}") + for item in self.inputs: + if not str(item.get("id", item.get("path", ""))).strip(): + raise ValueError("each input needs id or path") + if item.get("sha256") is not None: + digest = str(item["sha256"]).lower() + if len(digest) != 64 or any(character not in "0123456789abcdef" for character in digest): + raise ValueError("input sha256 is invalid") + + def to_dict(self) -> dict[str, Any]: + payload = asdict(self) + payload["command"] = list(self.command) + payload["inputs"] = [dict(item) for item in self.inputs] + payload["outputs"] = list(self.outputs) + payload["checkpoint_paths"] = list(self.checkpoint_paths) + return payload + + @property + def fingerprint(self) -> str: + return _fingerprint(self.to_dict()) + + +class JobBackend(Protocol): + def preflight(self, spec: JobSpec) -> dict[str, Any]: ... + def submit(self, spec: JobSpec, *, approval: Mapping[str, Any] | None = None) -> dict[str, Any]: ... + def status(self, job_id: str) -> dict[str, Any]: ... + def cancel(self, job_id: str) -> dict[str, Any]: ... + def collect(self, job_id: str) -> dict[str, Any]: ... + + +def build_approval_receipt(spec: JobSpec, *, approved_by: str, target: str, approved_at: str | None = None) -> dict[str, Any]: + material = { + "schema_version": 1, + "job_spec_sha256": spec.fingerprint, + "backend": spec.backend, + "target": _text(target, "target"), + "approved_by": _text(approved_by, "approved_by"), + "approved_at": approved_at or _now(), + "approved": True, + "resource_cap": asdict(spec.resources), + "cost_cap": spec.cost_cap, + "evidence_boundary": "Approval authorizes the described execution boundary; it does not endorse the scientific method or result.", + } + return {**material, "fingerprint": _fingerprint(material)} + + +def validate_approval_receipt(receipt: Mapping[str, Any], spec: JobSpec) -> None: + if receipt.get("schema_version") != 1 or receipt.get("approved") is not True: + raise ValueError("valid explicit approval receipt is required") + if receipt.get("job_spec_sha256") != spec.fingerprint or receipt.get("backend") != spec.backend: + raise ValueError("approval receipt covers a different job spec") + material = dict(receipt) + fingerprint = str(material.pop("fingerprint", "")).lower() + if _fingerprint(material) != fingerprint: + raise ValueError("approval receipt fingerprint mismatch") + + +def _state_material(spec: JobSpec, *, job_id: str, state: str, submitted_at: str, **updates: Any) -> dict[str, Any]: + if state not in ALL_STATES: + raise ValueError(f"invalid job state: {state}") + payload = { + "schema_version": 1, + "job_id": job_id, + "backend": spec.backend, + "name": spec.name, + "scientific_run_id": spec.scientific_run_id, + "job_spec_sha256": spec.fingerprint, + "state": state, + "submitted_at": submitted_at, + "started_at": None, + "finished_at": None, + "exit_code": None, + "failure_class": None, + "worker_pid": None, + "scheduler_job_id": None, + "stdout_path": "stdout.log", + "stderr_path": "stderr.log", + "resource_request": asdict(spec.resources), + "resource_usage": {}, + "cost": None, + "checkpoint_paths": list(spec.checkpoint_paths), + "outputs": [], + "message": "", + "updated_at": _now(), + } + payload.update(updates) + material = dict(payload) + payload["fingerprint"] = _fingerprint(material) + return payload + + +def validate_job_state(payload: Mapping[str, Any]) -> None: + if payload.get("schema_version") != 1 or payload.get("state") not in ALL_STATES: + raise ValueError("invalid job state record") + for field in ("job_id", "backend", "name", "job_spec_sha256", "submitted_at", "updated_at"): + _text(payload.get(field), field) + material = dict(payload) + fingerprint = str(material.pop("fingerprint", "")).lower() + if _fingerprint(material) != fingerprint: + raise ValueError("job state fingerprint mismatch") + + +def _process_alive(pid: int) -> bool: + if pid <= 0: + return False + try: + os.kill(pid, 0) + except ProcessLookupError: + return False + except PermissionError: + return True + return True + + +class LocalBackend: + def __init__(self, state_dir: Path) -> None: + self.state_dir = state_dir.resolve() + self.jobs_dir = self.state_dir / "jobs" + + def _job_dir(self, job_id: str) -> Path: + safe = _text(job_id, "job_id") + if any(character not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" for character in safe): + raise ValueError("job_id contains unsafe characters") + return self.jobs_dir / safe + + def preflight(self, spec: JobSpec) -> dict[str, Any]: + spec.validate() + executable = shutil.which(spec.command[0]) if not Path(spec.command[0]).is_absolute() else spec.command[0] + return { + "schema_version": 1, + "backend": "local", + "ready": executable is not None and Path(spec.working_directory).is_dir(), + "executable": executable, + "working_directory": spec.working_directory, + "job_spec_sha256": spec.fingerprint, + "resource_request": asdict(spec.resources), + "warnings": ["Local resource requests are recorded but are not a scheduler-enforced reservation."], + } + + def submit(self, spec: JobSpec, *, approval: Mapping[str, Any] | None = None) -> dict[str, Any]: + if spec.backend != "local": + raise ValueError("LocalBackend requires backend=local") + preflight = self.preflight(spec) + if not preflight["ready"]: + raise ValueError("local preflight failed") + if spec.approval_required: + if approval is None: + raise ValueError("job spec requires explicit approval") + validate_approval_receipt(approval, spec) + job_id = "local-" + uuid.uuid4().hex[:20] + job_dir = self._job_dir(job_id) + job_dir.mkdir(parents=True) + _atomic_json(job_dir / "spec.json", spec.to_dict()) + submitted_at = _now() + initial = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at) + _atomic_json(job_dir / "state.json", initial) + process = subprocess.Popen( + [sys.executable, str(Path(__file__).resolve()), "_worker", str(job_dir)], + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True, + close_fds=True, + ) + state = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at, worker_pid=process.pid, message="local worker started") + _atomic_json(job_dir / "state.json", state) + return state + + def status(self, job_id: str) -> dict[str, Any]: + job_dir = self._job_dir(job_id) + state = _read_json(job_dir / "state.json") + validate_job_state(state) + if state["state"] in {"submitted", "running"} and state.get("worker_pid") and not _process_alive(int(state["worker_pid"])): + # Give an atomically-written terminal state a short chance to land. + time.sleep(0.05) + state = _read_json(job_dir / "state.json") + validate_job_state(state) + if state["state"] in {"submitted", "running"}: + spec = JobSpec.from_payload(_read_json(job_dir / "spec.json")) + state = _state_material( + spec, + job_id=job_id, + state="lost", + submitted_at=str(state["submitted_at"]), + started_at=state.get("started_at"), + finished_at=_now(), + worker_pid=state.get("worker_pid"), + failure_class="worker-lost", + message="local worker exited without a terminal receipt", + ) + _atomic_json(job_dir / "state.json", state) + return state + + def wait(self, job_id: str, *, timeout_seconds: float, poll_seconds: float = 0.2) -> dict[str, Any]: + if timeout_seconds <= 0 or poll_seconds <= 0: + raise ValueError("wait timeout and poll interval must be positive") + deadline = time.monotonic() + timeout_seconds + while True: + state = self.status(job_id) + if state["state"] in TERMINAL_STATES: + return state + if time.monotonic() >= deadline: + raise TimeoutError(f"job did not reach a terminal state within {timeout_seconds} seconds") + time.sleep(poll_seconds) + + def cancel(self, job_id: str) -> dict[str, Any]: + job_dir = self._job_dir(job_id) + state = self.status(job_id) + if state["state"] in TERMINAL_STATES: + return state + pid = int(state.get("worker_pid") or 0) + if pid > 0 and _process_alive(pid): + try: + os.killpg(pid, signal.SIGTERM) + except ProcessLookupError: + pass + spec = JobSpec.from_payload(_read_json(job_dir / "spec.json")) + cancelled = _state_material( + spec, + job_id=job_id, + state="cancelled", + submitted_at=str(state["submitted_at"]), + started_at=state.get("started_at"), + finished_at=_now(), + worker_pid=pid or None, + failure_class="user-cancelled", + message="job cancelled", + ) + _atomic_json(job_dir / "state.json", cancelled) + return cancelled + + def collect(self, job_id: str) -> dict[str, Any]: + job_dir = self._job_dir(job_id) + state = self.status(job_id) + if state["state"] not in TERMINAL_STATES: + raise ValueError("cannot collect a nonterminal job") + spec = JobSpec.from_payload(_read_json(job_dir / "spec.json")) + root = Path(spec.working_directory).resolve() + outputs: list[dict[str, Any]] = [] + for relative in spec.outputs: + path = (root / relative).resolve() + if not path.is_relative_to(root): + raise ValueError(f"output escapes working directory: {relative}") + if path.is_dir(): + descriptor = describe_directory(path) + outputs.append({"path": relative, "artifact_type": "directory-tree", "sha256": descriptor.root_sha256, "size_bytes": descriptor.total_bytes, "entry_count": descriptor.entry_count}) + elif path.is_file(): + digest, size = stream_sha256(path) + outputs.append({"path": relative, "artifact_type": "file", "sha256": digest, "size_bytes": size, "entry_count": 1}) + else: + outputs.append({"path": relative, "artifact_type": "missing", "sha256": None, "size_bytes": None, "entry_count": 0}) + state = dict(state) + state.pop("fingerprint", None) + state["outputs"] = outputs + state["updated_at"] = _now() + state["fingerprint"] = _fingerprint(state) + _atomic_json(job_dir / "state.json", state) + return state + + +def _worker(job_dir: Path) -> int: + spec = JobSpec.from_payload(_read_json(job_dir / "spec.json")) + initial = _read_json(job_dir / "state.json") + job_id = str(initial["job_id"]) + submitted_at = str(initial["submitted_at"]) + worker_pid = os.getpid() + running = _state_material(spec, job_id=job_id, state="running", submitted_at=submitted_at, started_at=_now(), worker_pid=worker_pid, message="command running") + _atomic_json(job_dir / "state.json", running) + environment = os.environ.copy() if spec.inherit_environment else {} + environment.update(spec.environment) + stdout_path = job_dir / "stdout.log" + stderr_path = job_dir / "stderr.log" + started_monotonic = time.monotonic() + state = "failed" + exit_code: int | None = None + failure_class: str | None = None + message = "" + try: + with stdout_path.open("wb") as stdout, stderr_path.open("wb") as stderr: + completed = subprocess.run( + list(spec.command), + cwd=spec.working_directory, + env=environment, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + timeout=spec.timeout_seconds, + check=False, + ) + exit_code = int(completed.returncode) + state = "completed" if exit_code == 0 else "failed" + failure_class = None if exit_code == 0 else "nonzero-exit" + message = "command exited successfully" if exit_code == 0 else f"command exited with code {exit_code}" + except subprocess.TimeoutExpired: + state = "timed-out" + failure_class = "wall-time" + message = f"command exceeded timeout_seconds={spec.timeout_seconds}" + except FileNotFoundError as error: + state = "failed" + failure_class = "executable-not-found" + message = str(error) + except BaseException as error: + state = "failed" + failure_class = "worker-exception" + message = f"{type(error).__name__}: {error}" + final = _state_material( + spec, + job_id=job_id, + state=state, + submitted_at=submitted_at, + started_at=running["started_at"], + finished_at=_now(), + exit_code=exit_code, + failure_class=failure_class, + worker_pid=worker_pid, + resource_usage={"wall_seconds": round(time.monotonic() - started_monotonic, 6)}, + message=message, + ) + _atomic_json(job_dir / "state.json", final) + return 0 if state == "completed" else 1 + + +class SlurmBackend: + def __init__(self, state_dir: Path) -> None: + self.state_dir = state_dir.resolve() + self.jobs_dir = self.state_dir / "jobs" + + def _job_dir(self, job_id: str) -> Path: + safe = _text(job_id, "job_id") + if any(character not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" for character in safe): + raise ValueError("job_id contains unsafe characters") + return self.jobs_dir / safe + + def preflight(self, spec: JobSpec) -> dict[str, Any]: + spec.validate() + tools = {name: shutil.which(name) for name in ("sbatch", "squeue", "sacct", "scancel")} + return { + "schema_version": 1, + "backend": "slurm", + "ready": tools["sbatch"] is not None and tools["scancel"] is not None and (tools["sacct"] is not None or tools["squeue"] is not None), + "tools": tools, + "job_spec_sha256": spec.fingerprint, + "resource_request": asdict(spec.resources), + "warnings": [] if tools["sacct"] else ["sacct is unavailable; terminal accounting may be incomplete."], + } + + def render_script(self, spec: JobSpec, *, job_id: str) -> str: + if spec.backend != "slurm": + raise ValueError("SlurmBackend requires backend=slurm") + resources = spec.resources + hours, remainder = divmod(resources.wall_time_seconds, 3600) + minutes, seconds = divmod(remainder, 60) + directives = [ + f"#SBATCH --job-name={spec.name}", + f"#SBATCH --cpus-per-task={resources.cpus}", + f"#SBATCH --mem={resources.memory_mb}M", + f"#SBATCH --time={hours:02d}:{minutes:02d}:{seconds:02d}", + f"#SBATCH --output={shlex.quote(str(self._job_dir(job_id) / 'stdout.log'))}", + f"#SBATCH --error={shlex.quote(str(self._job_dir(job_id) / 'stderr.log'))}", + ] + if resources.gpus: + gres = f"gpu:{resources.gpu_type}:{resources.gpus}" if resources.gpu_type else f"gpu:{resources.gpus}" + directives.append(f"#SBATCH --gres={gres}") + for flag, value in (("partition", resources.partition), ("account", resources.account), ("qos", resources.qos)): + if value: + directives.append(f"#SBATCH --{flag}={value}") + exports = "\n".join(f"export {shlex.quote(key)}={shlex.quote(value)}" for key, value in sorted(spec.environment.items())) + command = shlex.join(spec.command) + return "\n".join([ + "#!/usr/bin/env bash", + *directives, + "set -euo pipefail", + f"cd {shlex.quote(spec.working_directory)}", + exports, + f"timeout --signal=TERM {spec.timeout_seconds}s {command}", + "", + ]) + + def submit(self, spec: JobSpec, *, approval: Mapping[str, Any] | None = None) -> dict[str, Any]: + if spec.backend != "slurm": + raise ValueError("SlurmBackend requires backend=slurm") + if approval is None: + raise ValueError("Slurm submission requires explicit approval") + validate_approval_receipt(approval, spec) + preflight = self.preflight(spec) + if not preflight["ready"]: + raise ValueError("Slurm preflight failed") + job_id = "slurm-local-" + uuid.uuid4().hex[:16] + job_dir = self._job_dir(job_id) + job_dir.mkdir(parents=True) + _atomic_json(job_dir / "spec.json", spec.to_dict()) + script_path = job_dir / "job.sh" + script_path.write_text(self.render_script(spec, job_id=job_id), encoding="utf-8") + script_path.chmod(0o700) + completed = subprocess.run(["sbatch", "--parsable", str(script_path)], text=True, capture_output=True, check=False) + if completed.returncode != 0: + raise ValueError(f"sbatch failed: {completed.stderr.strip() or completed.stdout.strip()}") + scheduler_job_id = completed.stdout.strip().split(";", 1)[0] + if not scheduler_job_id: + raise ValueError("sbatch returned no scheduler job ID") + submitted_at = _now() + state = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at, scheduler_job_id=scheduler_job_id, message="submitted to Slurm") + _atomic_json(job_dir / "state.json", state) + return state + + def status(self, job_id: str) -> dict[str, Any]: + job_dir = self._job_dir(job_id) + state = _read_json(job_dir / "state.json") + validate_job_state(state) + if state["state"] in TERMINAL_STATES: + return state + scheduler_job_id = str(state.get("scheduler_job_id") or "") + raw_state = "" + exit_code = None + if shutil.which("sacct"): + completed = subprocess.run(["sacct", "-n", "-P", "-j", scheduler_job_id, "--format=State,ExitCode,Elapsed,MaxRSS"], text=True, capture_output=True, check=False) + first = next((line for line in completed.stdout.splitlines() if line.strip()), "") + if first: + fields = first.split("|") + raw_state = fields[0].split("+", 1)[0].strip().upper() + if len(fields) > 1 and fields[1]: + try: + exit_code = int(fields[1].split(":", 1)[0]) + except ValueError: + exit_code = None + if not raw_state and shutil.which("squeue"): + completed = subprocess.run(["squeue", "-h", "-j", scheduler_job_id, "-o", "%T"], text=True, capture_output=True, check=False) + raw_state = completed.stdout.strip().splitlines()[0].upper() if completed.stdout.strip() else "" + if not raw_state: + return state + mapped = SLURM_STATE_MAP.get(raw_state, "running") + spec = JobSpec.from_payload(_read_json(job_dir / "spec.json")) + updated = _state_material( + spec, + job_id=job_id, + state=mapped, + submitted_at=str(state["submitted_at"]), + started_at=state.get("started_at") or (_now() if mapped == "running" else None), + finished_at=_now() if mapped in TERMINAL_STATES else None, + exit_code=exit_code, + failure_class=None if mapped == "completed" else (raw_state.lower() if mapped in TERMINAL_STATES else None), + scheduler_job_id=scheduler_job_id, + message=f"Slurm state {raw_state}", + ) + _atomic_json(job_dir / "state.json", updated) + return updated + + def cancel(self, job_id: str) -> dict[str, Any]: + job_dir = self._job_dir(job_id) + state = self.status(job_id) + if state["state"] in TERMINAL_STATES: + return state + completed = subprocess.run(["scancel", str(state["scheduler_job_id"])], text=True, capture_output=True, check=False) + if completed.returncode != 0: + raise ValueError(f"scancel failed: {completed.stderr.strip() or completed.stdout.strip()}") + spec = JobSpec.from_payload(_read_json(job_dir / "spec.json")) + cancelled = _state_material( + spec, + job_id=job_id, + state="cancelled", + submitted_at=str(state["submitted_at"]), + started_at=state.get("started_at"), + finished_at=_now(), + scheduler_job_id=state.get("scheduler_job_id"), + failure_class="user-cancelled", + message="Slurm job cancellation requested", + ) + _atomic_json(job_dir / "state.json", cancelled) + return cancelled + + def collect(self, job_id: str) -> dict[str, Any]: + # Shared-filesystem Slurm output collection uses the same hash contract + # as local execution. Remote object transfer belongs to a separate, + # approved staging route. + state = self.status(job_id) + if state["state"] not in TERMINAL_STATES: + raise ValueError("cannot collect a nonterminal Slurm job") + local = LocalBackend(self.state_dir) + return local.collect(job_id) + + +def load_spec(path: Path) -> JobSpec: + return JobSpec.from_payload(_read_json(path)) + + +def backend_for(name: str, state_dir: Path) -> JobBackend: + if name == "local": + return LocalBackend(state_dir) + if name == "slurm": + return SlurmBackend(state_dir) + raise ValueError(f"unsupported backend: {name}") + + +def _worker_main(argv: list[str]) -> int: + if len(argv) != 2 or argv[0] != "_worker": + raise ValueError("invalid worker invocation") + return _worker(Path(argv[1]).resolve()) + + +if __name__ == "__main__": + raise SystemExit(_worker_main(sys.argv[1:])) From df8f12d1c3d519320eb3f70932a600fe71a774e2 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:55:33 +0900 Subject: [PATCH 21/87] Add durable scientific job CLI --- scripts/science_job.py | 143 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 scripts/science_job.py diff --git a/scripts/science_job.py b/scripts/science_job.py new file mode 100644 index 00000000..e378fab5 --- /dev/null +++ b/scripts/science_job.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +"""Submit, inspect, cancel, and collect durable local or Slurm jobs. + +Non-local submission requires an explicit approval receipt produced by the +`approve` subcommand. Scientific completion must still be established by the +run's metrics, claims, provenance, and review. +""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.compute_backends import ( # noqa: E402 + LocalBackend, + SlurmBackend, + backend_for, + build_approval_receipt, + load_spec, +) + + +def _read_json(path: Path) -> dict[str, Any]: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"JSON object required: {path}") + return payload + + +def _write(path: Path, payload: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + print(f"Success! Data written to: {path.resolve()}") + + +def command_preflight(args: argparse.Namespace) -> None: + spec = load_spec(args.spec) + _write(args.output, backend_for(spec.backend, args.state_dir).preflight(spec)) + + +def command_approve(args: argparse.Namespace) -> None: + spec = load_spec(args.spec) + receipt = build_approval_receipt(spec, approved_by=args.approved_by, target=args.target) + _write(args.output, receipt) + + +def command_render_slurm(args: argparse.Namespace) -> None: + spec = load_spec(args.spec) + backend = SlurmBackend(args.state_dir) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(backend.render_script(spec, job_id=args.job_id), encoding="utf-8") + print(f"Success! Slurm script written to: {args.output.resolve()}") + + +def command_submit(args: argparse.Namespace) -> None: + spec = load_spec(args.spec) + approval = None if args.approval is None else _read_json(args.approval) + _write(args.output, backend_for(spec.backend, args.state_dir).submit(spec, approval=approval)) + + +def command_status(args: argparse.Namespace) -> None: + _write(args.output, backend_for(args.backend, args.state_dir).status(args.job_id)) + + +def command_wait(args: argparse.Namespace) -> None: + if args.backend != "local": + raise ValueError("wait is currently supported by the durable local backend; poll Slurm with status") + _write(args.output, LocalBackend(args.state_dir).wait(args.job_id, timeout_seconds=args.timeout, poll_seconds=args.poll)) + + +def command_cancel(args: argparse.Namespace) -> None: + _write(args.output, backend_for(args.backend, args.state_dir).cancel(args.job_id)) + + +def command_collect(args: argparse.Namespace) -> None: + _write(args.output, backend_for(args.backend, args.state_dir).collect(args.job_id)) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--state-dir", type=Path, required=True) + subcommands = parser.add_subparsers(dest="command", required=True) + + command = subcommands.add_parser("preflight") + command.add_argument("--spec", type=Path, required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=command_preflight) + + command = subcommands.add_parser("approve") + command.add_argument("--spec", type=Path, required=True) + command.add_argument("--approved-by", required=True) + command.add_argument("--target", required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=command_approve) + + command = subcommands.add_parser("render-slurm") + command.add_argument("--spec", type=Path, required=True) + command.add_argument("--job-id", default="preview") + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=command_render_slurm) + + command = subcommands.add_parser("submit") + command.add_argument("--spec", type=Path, required=True) + command.add_argument("--approval", type=Path) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=command_submit) + + for name, handler in (("status", command_status), ("cancel", command_cancel), ("collect", command_collect)): + command = subcommands.add_parser(name) + command.add_argument("--backend", choices=["local", "slurm"], required=True) + command.add_argument("--job-id", required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=handler) + + command = subcommands.add_parser("wait") + command.add_argument("--backend", choices=["local", "slurm"], required=True) + command.add_argument("--job-id", required=True) + command.add_argument("--timeout", type=float, required=True) + command.add_argument("--poll", type=float, default=0.2) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=command_wait) + return parser + + +def main() -> int: + parser = build_parser() + args = parser.parse_args() + try: + args.handler(args) + except (OSError, ValueError, TimeoutError, json.JSONDecodeError) as error: + print(f"error: {error}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From e373016e6ac4892399ada53fd5b128176ffe2b92 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:56:37 +0900 Subject: [PATCH 22/87] Add machine-readable native skill maturity auditing --- src/codex_science/skill_maturity.py | 277 ++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 src/codex_science/skill_maturity.py diff --git a/src/codex_science/skill_maturity.py b/src/codex_science/skill_maturity.py new file mode 100644 index 00000000..62486932 --- /dev/null +++ b/src/codex_science/skill_maturity.py @@ -0,0 +1,277 @@ +"""Machine-readable maturity auditing for first-party scientific skills.""" + +from __future__ import annotations + +import json +import re +from dataclasses import dataclass +from pathlib import Path, PurePosixPath +from typing import Any, Iterable, Mapping + + +LEVELS = {f"L{index}": index for index in range(5)} +ROLES = {"conductor", "retriever", "executor", "analyzer", "reviewer", "infrastructure"} +REQUIRED_DECISION_SECTIONS = ("## Decision contract", "## Workflow", "## Outputs", "## Boundaries") +CORE_SECTION_ALIASES = { + "codex-science": ("## Research contract and evidence graph", "## Workflow", "## Completion test", "## Boundaries"), + "science-provenance": ("## Run contract", "## Inputs and retrieval", "## Outputs and claims", "## Safety and integrity"), + "science-review": ("## Inputs and review mode", "## Review workflow", "## Receipt and independence", "## Boundary"), +} +FRONTMATTER_RE = re.compile(r"\A---\s*\n(?P.*?)\n---\s*\n", re.DOTALL) +FIELD_RE = re.compile(r"^(?P[A-Za-z0-9_-]+):\s*(?P.+?)\s*$", re.MULTILINE) + + +def _safe_repo_path(root: Path, value: Any, label: str) -> Path: + text = str(value).strip() + pure = PurePosixPath(text) + if not text or pure.is_absolute() or ".." in pure.parts: + raise ValueError(f"{label} must be a safe repository-relative path: {text}") + path = (root / pure.as_posix()).resolve() + if not path.is_relative_to(root.resolve()): + raise ValueError(f"{label} escapes repository root") + return path + + +def _frontmatter(text: str) -> dict[str, str]: + match = FRONTMATTER_RE.match(text) + if match is None: + return {} + return {item.group("key"): item.group("value").strip().strip('"\'') for item in FIELD_RE.finditer(match.group("body"))} + + +def _load_json(path: Path, label: str) -> dict[str, Any]: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"{label} must be a JSON object: {path}") + return payload + + +def _reference_contract(skill_dir: Path) -> tuple[bool, list[str]]: + index_path = skill_dir / "references" / "index.json" + if not index_path.is_file(): + return False, ["references/index.json is missing"] + try: + payload = _load_json(index_path, "reference index") + except (OSError, ValueError, json.JSONDecodeError) as error: + return False, [str(error)] + entries = payload.get("references") + if not isinstance(entries, list) or not entries: + return False, ["reference index contains no references"] + errors: list[str] = [] + ids: set[str] = set() + paths: set[str] = set() + for index, raw in enumerate(entries): + if not isinstance(raw, Mapping): + errors.append(f"reference {index} is not an object") + continue + reference_id = str(raw.get("id", "")).strip() + relative = str(raw.get("path", "")).strip() + if not reference_id or reference_id in ids: + errors.append(f"reference {index} has missing or duplicate id") + ids.add(reference_id) + if not relative or relative in paths: + errors.append(f"reference {index} has missing or duplicate path") + paths.add(relative) + try: + pure = PurePosixPath(relative) + if pure.is_absolute() or ".." in pure.parts or len(pure.parts) != 2 or pure.parts[0] != "references": + raise ValueError("must be a one-level references path") + target = skill_dir / relative + if not target.is_file(): + errors.append(f"indexed reference is missing: {relative}") + except ValueError as error: + errors.append(f"invalid reference path {relative}: {error}") + if not isinstance(raw.get("read_when"), list) or not raw.get("read_when"): + errors.append(f"reference {reference_id or index} has no read_when routes") + if raw.get("required_before") is None: + errors.append(f"reference {reference_id or index} has no required_before field") + if not str(raw.get("evidence_boundary", "")).strip(): + errors.append(f"reference {reference_id or index} has no evidence_boundary") + return not errors, errors + + +def _path_list(payload: Mapping[str, Any], field: str) -> list[str]: + value = payload.get(field, []) + if not isinstance(value, list) or not all(isinstance(item, str) and item.strip() for item in value): + raise ValueError(f"quality {field} must be a string list") + return [item.strip() for item in value] + + +def validate_quality_declaration(root: Path, skill_dir: Path, payload: Mapping[str, Any]) -> dict[str, Any]: + if payload.get("schema_version") != 1: + raise ValueError("unsupported quality declaration schema") + skill = str(payload.get("skill", "")).strip() + if not skill: + raise ValueError("quality skill is required") + roles = _path_list(payload, "roles") + unknown_roles = sorted(set(roles) - ROLES) + if unknown_roles: + raise ValueError(f"unknown skill roles: {', '.join(unknown_roles)}") + declared = str(payload.get("declared_maturity", "")).strip() + if declared not in LEVELS: + raise ValueError(f"invalid declared maturity: {declared}") + output_schemas = _path_list(payload, "output_schemas") + fixtures = _path_list(payload, "acceptance_fixtures") + seeded_failures = _path_list(payload, "seeded_failures") + tests = _path_list(payload, "test_files") + limitations = _path_list(payload, "limitations") + for path in fixtures + tests: + if not _safe_repo_path(root, path, "quality dependency").exists(): + raise ValueError(f"quality dependency does not exist: {path}") + if LEVELS[declared] >= 3 and not output_schemas: + raise ValueError("L3+ skills require output_schemas") + if LEVELS[declared] >= 4 and (not fixtures or not seeded_failures or not tests): + raise ValueError("L4 skills require acceptance fixtures, seeded failures, and tests") + return { + "schema_version": 1, + "skill": skill, + "roles": roles, + "declared_maturity": declared, + "output_schemas": output_schemas, + "acceptance_fixtures": fixtures, + "seeded_failures": seeded_failures, + "test_files": tests, + "limitations": limitations, + "quality_path": str((skill_dir / "quality.json").relative_to(root)), + } + + +def _sections_for(skill_dir: Path, name: str, text: str) -> tuple[bool, list[str]]: + required = CORE_SECTION_ALIASES.get(name, REQUIRED_DECISION_SECTIONS) + missing = [section for section in required if section not in text] + return not missing, missing + + +def evaluate_skill(root: Path, skill_dir: Path) -> dict[str, Any]: + skill_path = skill_dir / "SKILL.md" + relative = str(skill_dir.relative_to(root)) + record: dict[str, Any] = { + "path": relative, + "name": skill_dir.name, + "computed_maturity": "L0", + "declared_maturity": None, + "roles": [], + "reasons": [], + "output_schemas": [], + "acceptance_fixtures": [], + "seeded_failures": [], + "test_files": [], + "limitations": [], + "reference_contract": False, + } + if not skill_path.is_file(): + record["reasons"].append("SKILL.md is missing") + return record + text = skill_path.read_text(encoding="utf-8") + metadata = _frontmatter(text) + name = metadata.get("name", "").strip() + description = metadata.get("description", "").strip() + if not name or not description or "# " not in text: + record["reasons"].append("frontmatter name/description or title is missing") + return record + record["name"] = name + computed = 1 + complete_sections, missing_sections = _sections_for(skill_dir, skill_dir.name, text) + has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" + has_review = "$science-review" in text or skill_dir.name == "science-review" + if complete_sections and has_provenance and has_review: + computed = 2 + else: + record["reasons"].extend(f"missing instruction contract: {section}" for section in missing_sections) + if not has_provenance: + record["reasons"].append("science provenance handoff is missing") + if not has_review: + record["reasons"].append("science review handoff is missing") + reference_ok, reference_errors = _reference_contract(skill_dir) + record["reference_contract"] = reference_ok + if not reference_ok and (skill_dir / "references").is_dir(): + record["reasons"].extend(reference_errors) + quality_path = skill_dir / "quality.json" + declaration: dict[str, Any] | None = None + if quality_path.is_file(): + try: + declaration = validate_quality_declaration(root, skill_dir, _load_json(quality_path, "quality declaration")) + except (OSError, ValueError, json.JSONDecodeError) as error: + record["reasons"].append(f"invalid quality declaration: {error}") + if declaration: + record.update({key: declaration[key] for key in ("declared_maturity", "roles", "output_schemas", "acceptance_fixtures", "seeded_failures", "test_files", "limitations")}) + if computed >= 2 and reference_ok and declaration["output_schemas"]: + computed = 3 + if computed >= 3 and declaration["acceptance_fixtures"] and declaration["seeded_failures"] and declaration["test_files"]: + computed = 4 + record["computed_maturity"] = f"L{computed}" + if record["declared_maturity"] and LEVELS[record["computed_maturity"]] < LEVELS[record["declared_maturity"]]: + record["reasons"].append(f"declared {record['declared_maturity']} but computed {record['computed_maturity']}") + return record + + +def load_policy(root: Path, path: Path | None = None) -> dict[str, Any]: + policy_path = path or root / "catalog" / "native-skill-policy.json" + payload = _load_json(policy_path, "native skill policy") + if payload.get("schema_version") != 1 or not isinstance(payload.get("requirements"), list): + raise ValueError("unsupported native skill policy") + return payload + + +def audit_native_skills(root: Path, *, policy_path: Path | None = None) -> dict[str, Any]: + root = root.resolve() + records = [] + for parent in (root / "skills", root / "authored-skills"): + if not parent.is_dir(): + continue + for skill_dir in sorted(path for path in parent.iterdir() if path.is_dir() and (path / "SKILL.md").is_file()): + records.append(evaluate_skill(root, skill_dir)) + by_name = {record["name"]: record for record in records} + policy = load_policy(root, policy_path) + findings: list[dict[str, str]] = [] + for requirement in policy["requirements"]: + if not isinstance(requirement, Mapping): + raise ValueError("native skill policy requirements must be objects") + name = str(requirement.get("skill", "")) + minimum = str(requirement.get("minimum_maturity", "")) + if minimum not in LEVELS: + raise ValueError(f"invalid policy maturity: {minimum}") + record = by_name.get(name) + if record is None: + findings.append({"code": "missing-policy-skill", "severity": "major", "skill": name, "message": f"Policy skill is missing: {name}"}) + continue + if LEVELS[record["computed_maturity"]] < LEVELS[minimum]: + findings.append({"code": "skill-below-maturity", "severity": "major", "skill": name, "message": f"{name} computed {record['computed_maturity']} below required {minimum}."}) + required_roles = set(requirement.get("required_roles", [])) + missing_roles = sorted(required_roles - set(record["roles"])) + if missing_roles: + findings.append({"code": "missing-skill-role", "severity": "major", "skill": name, "message": f"{name} is missing roles: {', '.join(missing_roles)}"}) + if bool(requirement.get("require_reference_contract", False)) and not record["reference_contract"]: + findings.append({"code": "missing-reference-contract", "severity": "major", "skill": name, "message": f"{name} has no valid progressive reference contract."}) + summary = {level: sum(record["computed_maturity"] == level for record in records) for level in LEVELS} + return { + "schema_version": 1, + "skill_count": len(records), + "summary": summary, + "status": "findings" if findings else "passed", + "findings": sorted(findings, key=lambda item: (item["code"], item["skill"])), + "skills": records, + "policy": policy, + } + + +def render_maturity_markdown(report: Mapping[str, Any]) -> str: + lines = [ + "# Native skill maturity", + "", + f"Status: **{report['status']}** · skills: **{report['skill_count']}**", + "", + "| Skill | Path | Computed | Declared | Roles | References | Findings |", + "| --- | --- | ---: | ---: | --- | --- | --- |", + ] + for record in report["skills"]: + reasons = "; ".join(record["reasons"]).replace("|", "\\|") + lines.append( + f"| {record['name']} | `{record['path']}` | {record['computed_maturity']} | {record['declared_maturity'] or ''} | " + f"{', '.join(record['roles'])} | {'yes' if record['reference_contract'] else 'no'} | {reasons} |" + ) + if report["findings"]: + lines.extend(["", "## Policy findings", ""]) + lines.extend(f"- **{item['code']}** `{item['skill']}` — {item['message']}" for item in report["findings"]) + lines.append("") + return "\n".join(lines) From 0f87a317c873c2414d0821bdf28ab498ddea678d Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:56:53 +0900 Subject: [PATCH 23/87] Add native skill maturity audit CLI --- scripts/audit_native_skill_maturity.py | 47 ++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 scripts/audit_native_skill_maturity.py diff --git a/scripts/audit_native_skill_maturity.py b/scripts/audit_native_skill_maturity.py new file mode 100644 index 00000000..0b5600e3 --- /dev/null +++ b/scripts/audit_native_skill_maturity.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Audit first-party skills against machine-readable L0-L4 maturity policy.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.skill_maturity import audit_native_skills, render_maturity_markdown # noqa: E402 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=ROOT) + parser.add_argument("--policy", type=Path) + parser.add_argument("--output", type=Path) + parser.add_argument("--markdown", type=Path) + parser.add_argument("--require-clean", action="store_true") + args = parser.parse_args() + try: + report = audit_native_skills(args.root.resolve(), policy_path=args.policy) + except (OSError, ValueError, json.JSONDecodeError) as error: + print(f"error: {error}", file=sys.stderr) + return 1 + rendered = json.dumps(report, indent=2, sort_keys=True, ensure_ascii=False) + "\n" + if args.output: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(rendered, encoding="utf-8") + print(f"Success! Data written to: {args.output.resolve()}") + else: + print(rendered, end="") + if args.markdown: + args.markdown.parent.mkdir(parents=True, exist_ok=True) + args.markdown.write_text(render_maturity_markdown(report), encoding="utf-8") + print(f"Success! Report written to: {args.markdown.resolve()}") + if args.require_clean and report["status"] != "passed": + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From df5b0bb07da9455a7449ef4916ace12a3172a0f8 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:57:05 +0900 Subject: [PATCH 24/87] Add native skill maturity policy --- catalog/native-skill-policy.json | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 catalog/native-skill-policy.json diff --git a/catalog/native-skill-policy.json b/catalog/native-skill-policy.json new file mode 100644 index 00000000..4b8d420c --- /dev/null +++ b/catalog/native-skill-policy.json @@ -0,0 +1,41 @@ +{ + "schema_version": 1, + "requirements": [ + { + "skill": "codex-science", + "minimum_maturity": "L3", + "required_roles": ["conductor"], + "require_reference_contract": true + }, + { + "skill": "science-provenance", + "minimum_maturity": "L3", + "required_roles": ["infrastructure"], + "require_reference_contract": true + }, + { + "skill": "science-review", + "minimum_maturity": "L3", + "required_roles": ["reviewer"], + "require_reference_contract": true + }, + { + "skill": "literature-review", + "minimum_maturity": "L4", + "required_roles": ["conductor", "analyzer"], + "require_reference_contract": true + }, + { + "skill": "docking-validation", + "minimum_maturity": "L4", + "required_roles": ["analyzer", "reviewer"], + "require_reference_contract": true + }, + { + "skill": "remote-scientific-compute", + "minimum_maturity": "L3", + "required_roles": ["executor", "infrastructure"], + "require_reference_contract": true + } + ] +} From f08c2a5ed64f02c27c04be9e958b145a5242f594 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:57:20 +0900 Subject: [PATCH 25/87] Declare coordinator skill maturity --- skills/codex-science/quality.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 skills/codex-science/quality.json diff --git a/skills/codex-science/quality.json b/skills/codex-science/quality.json new file mode 100644 index 00000000..1ae679d5 --- /dev/null +++ b/skills/codex-science/quality.json @@ -0,0 +1,20 @@ +{ + "schema_version": 1, + "skill": "codex-science", + "roles": ["conductor"], + "declared_maturity": "L3", + "output_schemas": [ + "research-contract-v1", + "checkpoint-v4", + "artifact-manifest-v1", + "lane-receipt-v1", + "reference-use-ledger-v1" + ], + "acceptance_fixtures": [], + "seeded_failures": [], + "test_files": [], + "limitations": [ + "The coordinator delegates domain validity to specialist skills and independent review.", + "Task persistence depends on the installed Codex host and trusted hooks." + ] +} From 89ea736d2f2cf1d9f619c520b405ee4537b8da50 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:57:37 +0900 Subject: [PATCH 26/87] Declare provenance skill maturity --- skills/science-provenance/quality.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 skills/science-provenance/quality.json diff --git a/skills/science-provenance/quality.json b/skills/science-provenance/quality.json new file mode 100644 index 00000000..cba6f8c8 --- /dev/null +++ b/skills/science-provenance/quality.json @@ -0,0 +1,22 @@ +{ + "schema_version": 1, + "skill": "science-provenance", + "roles": ["infrastructure"], + "declared_maturity": "L3", + "output_schemas": [ + "artifact-manifest-v1", + "claim-register-v1", + "evidence-graph-v1-v2", + "reference-use-ledger-v1", + "artifact-descriptor-v1", + "artifact-selection-v1", + "transform-proposal-v1" + ], + "acceptance_fixtures": [], + "seeded_failures": [], + "test_files": [], + "limitations": [ + "A provenance bundle can be internally valid while the scientific design remains invalid.", + "Large or proprietary external inputs may be represented only by immutable references and checksums." + ] +} From ff4b48723906eb4c84bfbcb79fdcd7865d6e36c8 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:57:47 +0900 Subject: [PATCH 27/87] Declare review skill maturity --- skills/science-review/quality.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 skills/science-review/quality.json diff --git a/skills/science-review/quality.json b/skills/science-review/quality.json new file mode 100644 index 00000000..85dc4531 --- /dev/null +++ b/skills/science-review/quality.json @@ -0,0 +1,18 @@ +{ + "schema_version": 1, + "skill": "science-review", + "roles": ["reviewer"], + "declared_maturity": "L3", + "output_schemas": [ + "review-receipt-v2", + "scientific-finding-v1", + "review-resolution-v1" + ], + "acceptance_fixtures": [], + "seeded_failures": [], + "test_files": [], + "limitations": [ + "Deterministic review checks do not replace domain-expert judgment.", + "Independent reproduction is claimed only after a separate execution recreates the material result." + ] +} From a32f3698302f9b995badfeef77fe47237831da2a Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:58:02 +0900 Subject: [PATCH 28/87] Declare literature review L4 maturity --- .../literature-review/quality.json | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 authored-skills/literature-review/quality.json diff --git a/authored-skills/literature-review/quality.json b/authored-skills/literature-review/quality.json new file mode 100644 index 00000000..cc2990d8 --- /dev/null +++ b/authored-skills/literature-review/quality.json @@ -0,0 +1,35 @@ +{ + "schema_version": 1, + "skill": "literature-review", + "roles": ["conductor", "analyzer"], + "declared_maturity": "L4", + "output_schemas": [ + "literature-protocol-v1", + "query-ledger-v1", + "study-table-v1", + "study-family-v1", + "risk-of-bias-v1", + "claim-register-v1", + "evidence-graph-v2", + "literature-snapshot-v1-v2" + ], + "acceptance_fixtures": [ + "examples/literature-review-reviewed-run/manifest.json", + "examples/literature-review-reviewed-run/snapshot.previous.json", + "examples/literature-review-reviewed-run/snapshot.current.json" + ], + "seeded_failures": [ + "duplicate-study-identity", + "citation-mismatch", + "unsupported-conclusion", + "dependent-evidence-presented-as-replication" + ], + "test_files": [ + "tests/test_evidence_sidecars.py", + "tests/test_collaboration_literature_v2.py" + ], + "limitations": [ + "The checked-in reviewed run is a deterministic fixture rather than a live systematic-review conclusion.", + "Full-text licensing and source availability remain publication-specific." + ] +} From 6a2c04f3ca42097f13033f0cb33ade345f1a1ea5 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:58:14 +0900 Subject: [PATCH 29/87] Declare docking validation L4 maturity --- .../docking-validation/quality.json | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 authored-skills/docking-validation/quality.json diff --git a/authored-skills/docking-validation/quality.json b/authored-skills/docking-validation/quality.json new file mode 100644 index 00000000..98cad50d --- /dev/null +++ b/authored-skills/docking-validation/quality.json @@ -0,0 +1,35 @@ +{ + "schema_version": 1, + "skill": "docking-validation", + "roles": ["analyzer", "reviewer"], + "declared_maturity": "L4", + "output_schemas": [ + "sbdd-benchmark-v1", + "numeric-metric-v1", + "pose-table-v1", + "model-receipt-v2", + "review-receipt-v2" + ], + "acceptance_fixtures": [ + "examples/sbdd-acceptance/benchmark.json", + "examples/sbdd-acceptance/seeded-leaks.json", + "examples/sbdd-executable/input.json" + ], + "seeded_failures": [ + "held-out-pocket-leak", + "analog-series-leak", + "scaffold-leak", + "cold-target-violation", + "known-training-overlap", + "missing-subgroup-analysis", + "affinity-overclaim" + ], + "test_files": [ + "tests/test_sbdd_acceptance.py", + "tests/test_sbdd_execution.py" + ], + "limitations": [ + "The deterministic executable fixture validates contract handling rather than prospective docking accuracy.", + "Docking scores and model confidence are not experimental affinity or mechanism." + ] +} From 61794b078f3e9b4539769fd6190f463ca352f67d Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:58:32 +0900 Subject: [PATCH 30/87] Declare remote compute skill maturity --- .../remote-scientific-compute/quality.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 authored-skills/remote-scientific-compute/quality.json diff --git a/authored-skills/remote-scientific-compute/quality.json b/authored-skills/remote-scientific-compute/quality.json new file mode 100644 index 00000000..40887a56 --- /dev/null +++ b/authored-skills/remote-scientific-compute/quality.json @@ -0,0 +1,19 @@ +{ + "schema_version": 1, + "skill": "remote-scientific-compute", + "roles": ["executor", "infrastructure"], + "declared_maturity": "L3", + "output_schemas": [ + "job-spec-v1", + "job-approval-receipt-v1", + "job-state-v1", + "collection-receipt-v1" + ], + "acceptance_fixtures": [], + "seeded_failures": [], + "test_files": [], + "limitations": [ + "Local execution is directly supported; Slurm submission requires an existing scheduler configuration and explicit approval.", + "TES, WES, SSH staging, and cloud-provider adapters remain separate future backends." + ] +} From 22612df5f6fc7f966c8198bf10eb07d5a707098a Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 12:59:36 +0900 Subject: [PATCH 31/87] Document native artifact runtime usage and boundaries --- .../references/artifact-runtime.md | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 skills/science-provenance/references/artifact-runtime.md diff --git a/skills/science-provenance/references/artifact-runtime.md b/skills/science-provenance/references/artifact-runtime.md new file mode 100644 index 00000000..f3130dc4 --- /dev/null +++ b/skills/science-provenance/references/artifact-runtime.md @@ -0,0 +1,230 @@ +# Native scientific artifact runtime + +Read this reference before describing, rendering, selecting, or proposing a change to a scientific artifact. The runtime is bounded, local, hash-aware, and non-mutating. + +## What the runtime does + +The runtime produces three distinct records: + +1. `artifact-runtime-descriptor` — a bounded derived view of validated bytes; +2. `artifact-selection` — a typed pointer bound to the artifact path and SHA-256; +3. `transform-proposal` — a non-executing request that identifies affected steps and expected outputs. + +Never collapse these records into one action. A view does not validate an interpretation, a selection does not endorse a claim, and a proposal does not edit or rerun anything. + +## Supported viewers + +| Viewer | Typical formats | Typed selections | Current boundary | +| --- | --- | --- | --- | +| `structure-3d` | PDB, mmCIF | atom, residue, chain, ligand, spatial region | PDB coordinates are normalized from a bounded prefix; mmCIF is category/row preview only | +| `molecule-2d` | SDF, MOL, MOL2, SMILES | atom, bond, substructure, record | no aromaticity, stereochemistry, or valence inference beyond the source record | +| `genome-track` | VCF, BED, GFF, GTF, WIG, bigWig | variant, locus, interval, track record | bigWig is binary metadata only without an indexed reader | +| `single-cell` | H5AD, Loom | cell, cell set, feature, cluster, donor, layer | HDF5 container detection only; dimensions and layers are never guessed | +| `table` | CSV, TSV, Parquet, Arrow | row, column, cell, range | delimited text rows are bounded; binary columnar schema needs an Arrow backend | +| `figure` | PNG, JPEG, SVG, TIFF, WebP | region, axis, legend, panel | SVG markup is escaped and never executed | +| `evidence-graph` | evidence graph JSON | node, edge, claim, component | requires complete JSON inside the byte bound for exact counts | +| `trajectory` | XYZ, DCD, XTC, TRR, NetCDF | frame, frame range, atom, atom set | XYZ frames are previewed; binary trajectories expose metadata only | +| `directory` | Zarr, dataset directory, output tree | entry, entry set | directory Merkle descriptor is authoritative, view is entry bounded | +| `text` / `binary` | other files | line, JSON pointer, byte range, record | fallback view; no domain interpretation | + +## CLI overview + +All commands require explicit output files. No command edits the artifact. + +```bash +python scripts/render_artifact_runtime.py describe ... +python scripts/render_artifact_runtime.py render ... +python scripts/render_artifact_runtime.py select ... +python scripts/render_artifact_runtime.py propose ... +``` + +### Describe an artifact + +```bash +python scripts/render_artifact_runtime.py describe \ + artifacts/run-001/receptor.pdb \ + --artifact-path receptor.pdb \ + --sha256 8e7f...64-hex... \ + --kind receptor-structure \ + --artifact-type file \ + --media-type chemical/x-pdb \ + --max-bytes 1048576 \ + --max-records 200 \ + --output artifacts/run-001/receptor.runtime.json +``` + +Required arguments: + +- positional artifact path: local file or directory; +- `--artifact-path`: safe path relative to the run bundle; +- `--kind`: manifest artifact kind; +- `--max-bytes`: positive byte ceiling; +- `--max-records`: positive record ceiling; +- `--output`: descriptor destination. + +Recommended arguments: + +- `--sha256`: manifest digest; the command fails if bytes do not match; +- `--artifact-type`: `file`, `chunked-file`, `directory-tree`, or another declared manifest type; +- `--media-type`: explicit media type when filename inference is insufficient. + +Output fields: + +```json +{ + "schema_version": 1, + "artifact_path": "receptor.pdb", + "artifact_sha256": "...", + "artifact_kind": "receptor-structure", + "artifact_type": "file", + "media_type": "chemical/x-pdb", + "size_bytes": 481239, + "viewer": "structure-3d", + "generated_at": "2026-07-21T00:00:00Z", + "bounds": {"max_bytes": 1048576, "max_records": 200}, + "preview": {}, + "warnings": [], + "evidence_boundary": "...", + "fingerprint": "..." +} +``` + +The fingerprint covers the descriptor, including the artifact hash and view bounds. Changing the byte or record ceiling creates a different descriptor. + +### Render an offline view + +```bash +python scripts/render_artifact_runtime.py render \ + artifacts/run-001/receptor.runtime.json \ + --title "Receptor state inspection" \ + --output artifacts/run-001/receptor.runtime.html +``` + +The HTML uses no hosted assets and escapes source-controlled text. It is a derived navigation artifact and must not be cited as primary evidence. + +### Create a typed selection + +```bash +python scripts/render_artifact_runtime.py select \ + artifacts/run-001/receptor.runtime.json \ + --selector-type residue \ + --selector '{"chain":"A","residue_number":145,"insertion_code":""}' \ + --selected-by reviewer-agent-2 \ + --reason "Alternate conformation conflicts with the declared inactive receptor state." \ + --label "A:145" \ + --output artifacts/run-001/selections/residue-A-145.json +``` + +Selection output: + +```json +{ + "schema_version": 1, + "selection_id": "selection-...", + "artifact_path": "receptor.pdb", + "artifact_sha256": "...", + "viewer": "structure-3d", + "selector_type": "residue", + "selector": {"chain":"A","residue_number":145}, + "selected_by": "reviewer-agent-2", + "reason": "...", + "label": "A:145", + "created_at": "...", + "status": "active", + "fingerprint": "..." +} +``` + +A selection becomes stale when the current manifest digest for `artifact_path` differs. Never silently transfer a selector to changed bytes, even when the filename is unchanged. + +### Propose a transform + +```bash +python scripts/render_artifact_runtime.py propose \ + artifacts/run-001/selections/residue-A-145.json \ + --operation exclude-alternate-conformation \ + --parameters '{"altloc":"B","retain":"highest-occupancy"}' \ + --reason "Use one receptor microstate before docking." \ + --affected-step receptor-preparation \ + --affected-step docking \ + --affected-step interaction-analysis \ + --expected-output prepared/receptor.pdbqt \ + --expected-output docking/poses.sdf \ + --proposed-by analyst-agent-1 \ + --approval-boundary "structure mutation and downstream rerun" \ + --output artifacts/run-001/proposals/exclude-A-145-altloc-B.json +``` + +The proposal must retain: + +```json +{ + "status": "proposed", + "executed": false, + "requires_approval": true +} +``` + +A proposal is an input to impact analysis and selective rerun planning. It is not a shell command, patch, or approval receipt. + +## Manifest sidecar kinds + +Record generated JSON files as ordinary hashed artifacts: + +- runtime descriptor: `artifact-runtime-descriptor`; +- selection: `artifact-selection`; +- proposal: `transform-proposal`. + +The offline HTML is a derived view and normally should not be primary claim evidence. + +## Required workflow + +1. Validate the complete run bundle. +2. Resolve the artifact path, kind, media type, and manifest SHA-256. +3. Set explicit byte and record bounds appropriate to the format. +4. Generate and save the descriptor. +5. Inspect warnings before selecting anything. +6. Create the smallest typed selection that identifies the issue. +7. Create a non-executing transform proposal. +8. Run impact propagation and selective rerun planning. +9. Request approval if bytes, remote state, cost, or scientific scope will change. +10. Execute through the normal provenance and compute route. +11. Diff old and new artifacts and invalidate covered reviews. +12. Re-review the changed claims. + +## Search patterns + +Use these exact searches in this reference when needed: + +- `### Describe an artifact` +- `### Create a typed selection` +- `### Propose a transform` +- `## Manifest sidecar kinds` +- `## Failure handling` +- `## Common mistakes` + +## Failure handling + +| Failure | Required response | +| --- | --- | +| digest mismatch | stop; resolve the correct manifest or artifact bytes | +| unsupported format | use bounded `binary` or `text` view and state the missing reader | +| truncated JSON | increase the explicit bound or use a format-specific indexed reader; do not infer missing records | +| stale selection | create a new descriptor and selection against the new hash | +| active SVG content | keep escaped; never execute script, external image, or event handlers | +| H5AD/Parquet/bigWig schema unavailable | load an approved format-specific backend; never infer dimensions or columns from the filename | +| proposal affects unlisted downstream work | revise the dependency graph before execution | + +## Common mistakes + +- Rendering before bundle/hash validation. +- Treating the bounded prefix as a complete atom, variant, cell, or row count. +- Calling a model-imagined depiction a view of the artifact. +- Selecting by filename without the artifact SHA-256. +- Applying a natural-language edit directly instead of producing a typed proposal. +- Marking the proposal executed before a real command and receipt exist. +- Reusing a review receipt after any covered artifact changes. + +## Evidence boundary + +The runtime improves inspection, collaboration, and rerun precision. It does not establish chemical validity, structural correctness, genome-build compatibility, cell-type identity, statistical validity, or scientific truth without the relevant domain checks and evidence. From d5789fe39ddbb19d7d817b9126f20b84b877a5f9 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:00:30 +0900 Subject: [PATCH 32/87] Document project evidence store and branch workflow --- .../references/project-evidence-store.md | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 skills/codex-science/references/project-evidence-store.md diff --git a/skills/codex-science/references/project-evidence-store.md b/skills/codex-science/references/project-evidence-store.md new file mode 100644 index 00000000..9c7b5731 --- /dev/null +++ b/skills/codex-science/references/project-evidence-store.md @@ -0,0 +1,306 @@ +# Project evidence store and experiment forks + +Read this reference before importing multiple scientific runs into one project, creating a hypothesis branch, recording a quantitative evidence assertion, comparing runs, or preparing a merge plan. + +## Authority model + +The SQLite project store is an index and append-only decision ledger. It is **not** the source of truth for scientific bytes. + +Authority order: + +1. run-local artifact bytes and their SHA-256 values; +2. run-local `manifest.json`; +3. hash-covered sidecars and review receipts; +4. the project evidence store's imported hashes and lineage index; +5. derived project summaries, comparisons, and merge plans. + +If an imported manifest changes on disk, project comparison stops. Re-import it under a new run ID or restore the original bytes. Never update an existing run row to point at changed manifest bytes. + +## Data model + +The store records: + +- projects; +- immutable imported runs; +- branch base and head runs; +- run artifact hashes and claim records; +- quantitative evidence assertions with exact artifact locators; +- non-executing merge plans; +- a SHA-256 chained event log. + +The event chain detects accidental or unauthorized mutation of project-index history. It does not cryptographically identify the human or agent who caused an event. + +## CLI overview + +```bash +python scripts/science_project.py --database project/evidence.sqlite init ... +python scripts/science_project.py --database project/evidence.sqlite import-run ... +python scripts/science_project.py --database project/evidence.sqlite fork ... +python scripts/science_project.py --database project/evidence.sqlite assert ... +python scripts/science_project.py --database project/evidence.sqlite compare ... +python scripts/science_project.py --database project/evidence.sqlite merge-plan ... +python scripts/science_project.py --database project/evidence.sqlite summary ... +``` + +Every subcommand requires `--output` and writes a machine-readable JSON receipt. + +## Initialize a project + +```bash +python scripts/science_project.py \ + --database projects/egfr/evidence.sqlite \ + init \ + --project-id egfr-resistance \ + --title "EGFR resistance mechanism evaluation" \ + --question "Which receptor state and compound series best discriminate the resistance hypotheses?" \ + --output projects/egfr/project.created.json +``` + +The command is idempotent only when project ID, title, and question are identical. + +## Import a reviewed run + +```bash +python scripts/science_project.py \ + --database projects/egfr/evidence.sqlite \ + import-run \ + --project-id egfr-resistance \ + --manifest artifacts/run-001/manifest.json \ + --branch main \ + --output projects/egfr/import-run-001.json +``` + +For a continuation: + +```bash +python scripts/science_project.py \ + --database projects/egfr/evidence.sqlite \ + import-run \ + --project-id egfr-resistance \ + --manifest artifacts/run-002/manifest.json \ + --branch main \ + --parent-run run-001 \ + --output projects/egfr/import-run-002.json +``` + +Import performs full bundle validation before indexing. It records: + +```json +{ + "project_id": "egfr-resistance", + "run_id": "run-002", + "branch_name": "main", + "parent_run_id": "run-001", + "manifest_path": "/absolute/local/path/artifacts/run-002/manifest.json", + "manifest_sha256": "...", + "review_status": "passed", + "imported_at": "..." +} +``` + +The project database is local-machine state. For portable collaboration, export project receipts and retain the original run bundles; do not assume absolute manifest paths are valid on another machine. + +## Fork a hypothesis branch + +```bash +python scripts/science_project.py \ + --database projects/egfr/evidence.sqlite \ + fork \ + --project-id egfr-resistance \ + --source-run run-002 \ + --branch receptor-state-inactive \ + --output projects/egfr/fork-inactive.json +``` + +A fork points its base and head at an already imported run. It does not copy artifacts. The next imported run on that branch should name the branch and normally use the prior head as parent. + +Recommended branch names describe the scientific fork, not the intended winner: + +```text +receptor-state-inactive +receptor-state-active +microstate-neutral +microstate-cationic +assay-readout-biochemical +assay-readout-cellular +``` + +Avoid names such as `successful-model` or `best-compounds`; they encode an answer before the evidence is compared. + +## Add a quantitative evidence assertion + +An evidence assertion links one imported run claim to a source and an exact location in a hash-validated artifact. + +```bash +python scripts/science_project.py \ + --database projects/egfr/evidence.sqlite \ + assert \ + --project-id egfr-resistance \ + --run-id run-002 \ + --claim-id C-17 \ + --source-id PMID:12345678 \ + --polarity supports \ + --locator '{ + "artifact_path":"studies.json", + "artifact_sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "record_id":"study-42", + "json_pointer":"/studies/3/effect" + }' \ + --independence-group cohort-A \ + --effect-measure hazard_ratio \ + --estimate 0.72 \ + --interval-low 0.61 \ + --interval-high 0.85 \ + --sample-size 842 \ + --population "advanced NSCLC" \ + --risk-of-bias-ref ROB-31 \ + --output projects/egfr/assertion-C17-study42.json +``` + +Required fields: + +- imported `run_id` and `claim_id`; +- source identifier; +- polarity: `supports`, `contradicts`, `qualifies`, `neutral`, or `unavailable`; +- locator with imported artifact path and exact SHA-256; +- at least one exact locator field: page, table, figure, cell, JSON pointer, line range, or record ID; +- independence group. + +Optional quantitative fields: + +- effect measure; +- estimate; +- lower and upper interval; +- unit; +- sample size; +- population; +- risk-of-bias record. + +The store verifies that the locator path and digest occur in the imported run. It does not verify that the cited table cell was extracted correctly; source and method review must do that. + +## Compare two runs + +```bash +python scripts/science_project.py \ + --database projects/egfr/evidence.sqlite \ + compare \ + --project-id egfr-resistance \ + --previous-run run-002 \ + --current-run run-007 \ + --output projects/egfr/run-002-to-007.diff.json +``` + +The comparison re-hashes each manifest before loading it and reports: + +- added, removed, and changed artifacts; +- added, removed, and changed claims; +- code changes; +- environment changes; +- whether prior review is invalidated. + +A run comparison is not a statistical comparison. It reports record changes, not which scientific conclusion is better. + +## Prepare a merge plan + +```bash +python scripts/science_project.py \ + --database projects/egfr/evidence.sqlite \ + merge-plan \ + --project-id egfr-resistance \ + --source-branch receptor-state-inactive \ + --target-branch main \ + --output projects/egfr/merge-inactive-to-main.json +``` + +Output includes: + +```json +{ + "base_run_id": "run-002", + "source_run_id": "run-007", + "target_run_id": "run-006", + "source_diff": {}, + "target_diff": {}, + "claim_conflicts": [], + "artifact_conflicts": [], + "base_ambiguity": false, + "review_receipts_invalidated": true, + "requires_scientific_review": true, + "status": "candidate", + "executed": false +} +``` + +A conflict is reported when source and target both changed the same claim or artifact differently from the base. The merge planner never resolves the conflict by preferring recency, review status, or a desired conclusion. + +If branch ancestry has no unambiguous base, `base_ambiguity` is true. Resolve lineage before treating the plan as a three-way comparison. + +## Summarize project state + +```bash +python scripts/science_project.py \ + --database projects/egfr/evidence.sqlite \ + summary \ + --project-id egfr-resistance \ + --output projects/egfr/summary.json +``` + +The summary contains project metadata, branches, runs, assertion count, merge-plan count, and the event-chain head. + +## Recommended project layout + +```text +projects/egfr/ + evidence.sqlite + receipts/ + comparisons/ + merge-plans/ +artifacts/ + run-001/ + run-002/ + run-006/ + run-007/ +``` + +Keep the database outside individual run bundles. Do not add mutable SQLite files to a run's artifact manifest as scientific evidence. + +## Search patterns + +Use these exact searches when reading this reference: + +- `## Import a reviewed run` +- `## Fork a hypothesis branch` +- `## Add a quantitative evidence assertion` +- `## Compare two runs` +- `## Prepare a merge plan` +- `## Failure handling` +- `## Common mistakes` + +## Failure handling + +| Failure | Required response | +| --- | --- | +| imported manifest digest changed | stop; restore original bytes or import changed work under a new run ID | +| bundle validation fails | fix or preserve the run as failed/inconclusive; do not index it as reviewed evidence | +| duplicate run ID with different digest | create a new run ID; never overwrite project history | +| branch already exists | reuse it only if it represents the same fork; otherwise choose a distinct scientific name | +| assertion artifact digest mismatch | resolve the exact source artifact and locator before recording the assertion | +| interval only partly supplied | provide both bounds or omit the interval | +| merge conflict | create a decision/review task; do not auto-merge | +| ambiguous base | repair lineage or explicitly create a new comparison contract | +| database copied to another machine | verify every manifest path and digest before using summaries | + +## Common mistakes + +- Treating SQLite rows as a replacement for manifests and artifacts. +- Re-importing changed bytes under the same run ID. +- Forking after observing an outcome without preserving the original decision event. +- Recording an assertion without an exact artifact locator. +- Using publication count as independence count. +- Resolving claim conflicts by selecting the branch with a passed process receipt. +- Calling a merge plan an executed merge. +- Marking a branch merged without a new reviewed synthesis run. + +## Evidence boundary + +The project store improves lineage, comparison, quantitative evidence location, and experimental branching. It does not establish causality, choose the correct hypothesis, authenticate human identity, or replace independent scientific review. From f647b583fa5bf1c39de471f9c0aa6d595467f076 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:01:31 +0900 Subject: [PATCH 33/87] Document local and Slurm job runtime usage --- .../references/job-runtime.md | 396 ++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 authored-skills/remote-scientific-compute/references/job-runtime.md diff --git a/authored-skills/remote-scientific-compute/references/job-runtime.md b/authored-skills/remote-scientific-compute/references/job-runtime.md new file mode 100644 index 00000000..5249680b --- /dev/null +++ b/authored-skills/remote-scientific-compute/references/job-runtime.md @@ -0,0 +1,396 @@ +# Durable job runtime: local and Slurm + +Read this reference before constructing a job spec, approving a non-local job, submitting local or Slurm work, polling state, cancelling, or collecting outputs. + +## Backend boundary + +Implemented execution backends: + +- `local` — durable worker process, terminal receipt, wait, cancel, and hash-based output collection; +- `slurm` — script rendering, approved `sbatch` submission, `sacct`/`squeue` polling, `scancel`, and shared-filesystem output collection. + +Not implemented by this runtime: + +- implicit SSH credential discovery; +- remote object-store transfer; +- cloud-provider provisioning; +- GA4GH TES/WES submission; +- software installation; +- scheduler account or partition discovery by trial submission. + +Those routes require separate adapters and approval contracts. Do not pass an unsupported backend name and do not disguise a remote shell command as `local`. + +## Job spec + +A job spec is a JSON object: + +```json +{ + "schema_version": 1, + "backend": "local", + "name": "compute-rmsd", + "command": [ + "python", + "scripts/measure_pose.py", + "--input", + "poses.sdf", + "--output", + "metrics.json" + ], + "working_directory": "/absolute/path/project", + "environment": { + "OMP_NUM_THREADS": "4" + }, + "inherit_environment": true, + "inputs": [ + { + "id": "poses", + "path": "poses.sdf", + "sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "outputs": [ + "metrics.json" + ], + "resources": { + "cpus": 4, + "memory_mb": 8192, + "gpus": 0, + "gpu_type": null, + "wall_time_seconds": 1800, + "partition": null, + "account": null, + "qos": null + }, + "timeout_seconds": 1800, + "cost_cap": null, + "approval_required": false, + "scientific_run_id": "run-014", + "checkpoint_paths": [] +} +``` + +### Required semantics + +- `command` is an argv list, never a shell command string; +- `working_directory` must already exist; +- outputs and checkpoints are safe paths relative to the working directory; +- input hashes are recorded when available; +- secret-like environment keys are rejected; +- timeout is positive; +- local resource requests are recorded but not scheduler reservations; +- non-local submission requires approval even when `approval_required` is false in an invalid user-supplied spec. + +The spec fingerprint covers all fields, including command, environment values, resource request, input hashes, outputs, timeout, and cost cap. + +## CLI overview + +```bash +python scripts/science_job.py --state-dir .science-jobs preflight ... +python scripts/science_job.py --state-dir .science-jobs approve ... +python scripts/science_job.py --state-dir .science-jobs render-slurm ... +python scripts/science_job.py --state-dir .science-jobs submit ... +python scripts/science_job.py --state-dir .science-jobs status ... +python scripts/science_job.py --state-dir .science-jobs wait ... +python scripts/science_job.py --state-dir .science-jobs cancel ... +python scripts/science_job.py --state-dir .science-jobs collect ... +``` + +Every command writes a JSON receipt to `--output`. + +## Local execution + +### Preflight + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + preflight \ + --spec artifacts/run-014/job-spec.json \ + --output artifacts/run-014/job-preflight.json +``` + +Preflight checks: + +- backend matches; +- executable is resolvable; +- working directory exists; +- spec validates; +- resource request and spec fingerprint are recorded. + +A successful preflight does not prove that imports, model weights, databases, input semantics, or scientific assumptions are correct. + +### Submit + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + submit \ + --spec artifacts/run-014/job-spec.json \ + --output artifacts/run-014/job-submitted.json +``` + +The command starts a detached Python worker and returns a state receipt: + +```json +{ + "schema_version": 1, + "job_id": "local-...", + "backend": "local", + "job_spec_sha256": "...", + "state": "submitted", + "worker_pid": 12345, + "stdout_path": "stdout.log", + "stderr_path": "stderr.log", + "fingerprint": "..." +} +``` + +The job directory contains: + +```text +jobs/jobs/local-.../ + spec.json + state.json + stdout.log + stderr.log +``` + +`state.json` is written atomically. It is mutable operational state, not scientific evidence. Save terminal receipts and output hashes in the run manifest when they support a claim. + +### Status and wait + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + status \ + --backend local \ + --job-id local-... \ + --output artifacts/run-014/job-status.json + +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + wait \ + --backend local \ + --job-id local-... \ + --timeout 120 \ + --poll 0.2 \ + --output artifacts/run-014/job-terminal.json +``` + +States: + +```text +submitted +running +completed +failed +timed-out +cancelled +lost +``` + +Failure classes distinguish at least: + +```text +nonzero-exit +wall-time +executable-not-found +worker-exception +worker-lost +user-cancelled +``` + +A process exit code of zero means only that the command completed according to the operating system. It is not a scientific acceptance result. + +### Cancel + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + cancel \ + --backend local \ + --job-id local-... \ + --output artifacts/run-014/job-cancelled.json +``` + +Cancellation is idempotent for terminal jobs. The local backend sends `SIGTERM` to the worker process group and records `user-cancelled`. + +### Collect outputs + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + collect \ + --backend local \ + --job-id local-... \ + --output artifacts/run-014/job-collected.json +``` + +Collection is allowed only after a terminal state. Declared outputs are resolved inside the working directory: + +- files receive streaming SHA-256 and size; +- directories receive a Merkle root, total bytes, and entry count; +- missing outputs remain explicit with `artifact_type: missing`. + +Do not remove a missing output from the receipt to make a run appear complete. + +## Approval receipt + +Create approval for a spec only after reviewing target, resource, cost, transfer, cancellation, and scientific scope. + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + approve \ + --spec artifacts/run-014/slurm-job-spec.json \ + --approved-by jaemin \ + --target hpc-cluster-a \ + --output artifacts/run-014/slurm-approval.json +``` + +The receipt binds: + +```json +{ + "job_spec_sha256": "...", + "backend": "slurm", + "target": "hpc-cluster-a", + "approved": true, + "resource_cap": {}, + "cost_cap": null, + "fingerprint": "..." +} +``` + +Changing the job spec invalidates the approval. Approval authorizes execution boundaries; it does not endorse scientific validity. + +## Slurm execution + +### Render before submitting + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + render-slurm \ + --spec artifacts/run-014/slurm-job-spec.json \ + --job-id preview \ + --output artifacts/run-014/slurm-preview.sh +``` + +Inspect: + +- command and quoting; +- working directory; +- CPU, memory, GPU, time, partition, account, and QoS; +- stdout/stderr destination; +- environment exports; +- timeout wrapper. + +Rendering is safe and does not submit. + +### Submit + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + submit \ + --spec artifacts/run-014/slurm-job-spec.json \ + --approval artifacts/run-014/slurm-approval.json \ + --output artifacts/run-014/slurm-submitted.json +``` + +Preconditions: + +- `sbatch` and `scancel` available; +- `sacct` or `squeue` available; +- approval fingerprint matches the exact spec; +- scheduler account/partition already known and approved; +- working directory is a shared path visible to compute nodes; +- all required data are already staged by an approved route. + +The backend does not copy data or credentials. + +### Status + +```bash +python scripts/science_job.py \ + --state-dir artifacts/run-014/jobs \ + status \ + --backend slurm \ + --job-id slurm-local-... \ + --output artifacts/run-014/slurm-status.json +``` + +The adapter normalizes scheduler states while retaining the raw state in the message. `OUT_OF_MEMORY`, `NODE_FAIL`, `PREEMPTED`, `TIMEOUT`, and `CANCELLED` are not merged into one generic failure. + +### Cancel and collect + +```bash +python scripts/science_job.py --state-dir artifacts/run-014/jobs cancel \ + --backend slurm --job-id slurm-local-... --output cancelled.json + +python scripts/science_job.py --state-dir artifacts/run-014/jobs collect \ + --backend slurm --job-id slurm-local-... --output collected.json +``` + +Collection assumes a shared filesystem. Remote object storage or SSH transfer requires a separate approved staging backend. + +## Checkpoint and resume + +`checkpoint_paths` identify files expected to make a later attempt resumable. The runtime records and collects them but does not infer a program-specific resume command. + +Required procedure: + +1. verify checkpoint bytes and compatibility with code/config/model revision; +2. create a new job spec for the resumed attempt; +3. include the prior job and checkpoint hashes in run provenance; +4. obtain new approval when target, resources, cost, or command changes; +5. preserve the failed or preempted attempt. + +Never rewrite a failed job receipt into a successful resumed job. + +## Search patterns + +Use these exact searches when reading this reference: + +- `## Job spec` +- `## Local execution` +- `## Approval receipt` +- `## Slurm execution` +- `## Checkpoint and resume` +- `## Failure handling` +- `## Common mistakes` + +## Failure handling + +| Failure | Required response | +| --- | --- | +| executable missing | stop; use the approved environment or installation gate | +| local worker lost | preserve logs/state, inspect cause, create a new attempt | +| wall-time | preserve checkpoint, revise resource/timeout contract, obtain approval if required | +| nonzero exit | inspect stderr and scientific inputs; do not retry unchanged indefinitely | +| OOM | reduce workload or request reviewed resources; record OOM separately | +| node failure or preemption | resume only from verified compatible checkpoint | +| `sbatch` error | do not probe random accounts/partitions; resolve scheduler configuration | +| output missing | keep explicit missing record and leave dependent plan steps incomplete | +| output escapes working directory | reject collection; correct the job spec | +| approval mismatch | create a new approval for the exact spec | +| stale input hash | create a new run or attempt contract before execution | + +## Common mistakes + +- Passing a shell pipeline as one command string instead of an argv list. +- Recording tokens, passwords, or private keys in the job environment. +- Treating requested local resources as enforced limits. +- Submitting Slurm work before reviewing the rendered script. +- Using a shared login node for heavy computation instead of the scheduler. +- Busy-polling a waiting job. +- Treating exit code zero as scientific success. +- Omitting failed attempts, missing outputs, or cancellation receipts. +- Reusing approval after changing code, command, resources, target, or input hashes. +- Claiming remote durability when state files exist only on an ephemeral local disk. + +## Evidence boundary + +The job runtime provides durable execution and collection receipts. It does not establish reproducibility across environments, validate model or database licensing, authenticate scheduler identity, measure cloud cost unless an adapter reports it, or prove a scientific result. From 6c9f7479efc1fc40c4dd7236068bb0fc2616b686 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:03:14 +0900 Subject: [PATCH 34/87] Test native artifact runtime contracts --- tests/test_artifact_runtime.py | 121 +++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 tests/test_artifact_runtime.py diff --git a/tests/test_artifact_runtime.py b/tests/test_artifact_runtime.py new file mode 100644 index 00000000..8f4fb9b2 --- /dev/null +++ b/tests/test_artifact_runtime.py @@ -0,0 +1,121 @@ +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.artifact_runtime import ( + build_selection, + build_transform_proposal, + describe_runtime, + render_runtime_html, + stale_selection, + validate_runtime_descriptor, + validate_selection, + validate_transform_proposal, +) + + +class ArtifactRuntimeTests(unittest.TestCase): + def test_structure_descriptor_selection_and_proposal_are_hash_bound(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + structure = root / "receptor.pdb" + structure.write_text( + "ATOM 1 N GLY A 145 11.104 13.207 9.101 1.00 20.00 N \n" + "ATOM 2 CA GLY A 145 12.101 12.101 9.500 1.00 20.00 C \n" + "HETATM 3 C1 LIG B 1 14.000 10.000 8.000 1.00 20.00 C \n", + encoding="utf-8", + ) + descriptor = describe_runtime( + structure, + artifact_path="receptor.pdb", + kind="receptor-structure", + media_type="chemical/x-pdb", + max_bytes=4096, + max_records=20, + generated_at="2026-07-21T00:00:00Z", + ).to_dict() + validate_runtime_descriptor(descriptor) + self.assertEqual("structure-3d", descriptor["viewer"]) + self.assertEqual(["A", "B"], descriptor["preview"]["chains_seen"]) + self.assertEqual(["LIG"], descriptor["preview"]["ligands_seen"]) + + selection = build_selection( + descriptor, + selector_type="residue", + selector={"chain": "A", "residue_number": 145}, + selected_by="reviewer", + reason="Inspect the alternate receptor state.", + created_at="2026-07-21T00:01:00Z", + ) + validate_selection(selection, {"receptor.pdb": descriptor["artifact_sha256"]}) + self.assertEqual("active", stale_selection(selection, {"receptor.pdb": descriptor["artifact_sha256"]})["status"]) + self.assertEqual("stale-anchor", stale_selection(selection, {"receptor.pdb": "0" * 64})["status"]) + + proposal = build_transform_proposal( + selection, + operation="exclude-alternate-conformation", + parameters={"altloc": "B"}, + reason="Use one receptor microstate.", + affected_steps=["receptor-preparation", "docking"], + expected_outputs=["prepared/receptor.pdbqt"], + proposed_by="analyst", + created_at="2026-07-21T00:02:00Z", + ) + validate_transform_proposal(proposal, selection) + self.assertFalse(proposal["executed"]) + self.assertTrue(proposal["requires_approval"]) + + def test_table_view_is_bounded_and_html_escapes_title(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + table = root / "metrics.csv" + table.write_text("metric,value\nrmsd,1.2\npr_auc,0.9\nextra,3\n", encoding="utf-8") + descriptor = describe_runtime( + table, + artifact_path="metrics.csv", + kind="metrics", + media_type="text/csv", + max_bytes=1024, + max_records=2, + generated_at="2026-07-21T00:00:00Z", + ).to_dict() + self.assertEqual("table", descriptor["viewer"]) + self.assertEqual(["metric", "value"], descriptor["preview"]["columns"]) + self.assertEqual(2, len(descriptor["preview"]["rows_preview"])) + html = render_runtime_html(descriptor, title="") + self.assertNotIn("", html) + self.assertIn("<script>alert(1)</script>", html) + + def test_unsupported_selector_and_digest_mismatch_fail_closed(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + path = Path(tempdir) / "data.txt" + path.write_text("hello\n", encoding="utf-8") + descriptor = describe_runtime( + path, + artifact_path="data.txt", + kind="text", + max_bytes=100, + max_records=10, + ).to_dict() + with self.assertRaises(ValueError): + build_selection( + descriptor, + selector_type="residue", + selector={"chain": "A"}, + selected_by="reviewer", + reason="wrong viewer", + ) + with self.assertRaises(ValueError): + describe_runtime( + path, + artifact_path="data.txt", + artifact_sha256="f" * 64, + kind="text", + max_bytes=100, + max_records=10, + ) + + +if __name__ == "__main__": + unittest.main() From 49ce0dfe22195f321aedaaaf1a5846f078ccd3bb Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:03:50 +0900 Subject: [PATCH 35/87] Test project evidence memory and experiment forks --- tests/test_project_store.py | 138 ++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 tests/test_project_store.py diff --git a/tests/test_project_store.py b/tests/test_project_store.py new file mode 100644 index 00000000..586d014e --- /dev/null +++ b/tests/test_project_store.py @@ -0,0 +1,138 @@ +import hashlib +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.artifacts import add_artifact, new_manifest, write_manifest +from codex_science.project_store import ProjectStore + + +def make_run(root: Path, run_id: str, claim_text: str, metric_value: float) -> tuple[Path, str]: + run_dir = root / run_id + run_dir.mkdir(parents=True) + metrics = run_dir / "metrics.json" + metrics.write_text(json.dumps({"metric": "effect", "value": metric_value}) + "\n", encoding="utf-8") + digest = hashlib.sha256(metrics.read_bytes()).hexdigest() + manifest = new_manifest( + run_id, + "Which branch best supports the prespecified hypothesis?", + [{"id": "analyze", "description": "Compute the prespecified metric", "status": "completed"}], + ) + manifest["claims"] = [{"id": "C1", "text": claim_text, "evidence": ["metrics.json"]}] + manifest["executions"] = [{"command": "fixture", "exit_code": 0}] + manifest["review"] = {"status": "passed", "findings": []} + add_artifact(manifest, "metrics.json", kind="metrics", sha256=digest) + manifest_path = run_dir / "manifest.json" + write_manifest(manifest, manifest_path) + return manifest_path, digest + + +class ProjectStoreTests(unittest.TestCase): + def test_import_fork_assert_compare_and_merge_plan_preserve_run_authority(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + database = root / "project" / "evidence.sqlite" + store = ProjectStore(database) + project = store.create_project( + project_id="project-1", + title="Hypothesis comparison", + question="Which branch is supported?", + ) + self.assertEqual("project-1", project["project_id"]) + + base_manifest, _base_digest = make_run(root, "run-base", "Baseline claim", 0.5) + source_manifest, source_digest = make_run(root, "run-source", "Source branch claim", 0.8) + target_manifest, _target_digest = make_run(root, "run-target", "Target branch claim", 0.2) + + base = store.import_run(project_id="project-1", manifest_path=base_manifest, branch_name="main") + self.assertEqual("run-base", base.run_id) + store.fork_branch(project_id="project-1", source_run_id="run-base", branch_name="experiment") + source = store.import_run(project_id="project-1", manifest_path=source_manifest, branch_name="experiment") + target = store.import_run(project_id="project-1", manifest_path=target_manifest, branch_name="main") + self.assertEqual("run-base", source.parent_run_id) + self.assertEqual("run-base", target.parent_run_id) + + assertion = store.add_assertion( + project_id="project-1", + run_id="run-source", + claim_id="C1", + source_id="study-1", + polarity="supports", + locator={ + "artifact_path": "metrics.json", + "artifact_sha256": source_digest, + "json_pointer": "/value", + }, + independence_group="cohort-A", + effect_measure="difference", + estimate=0.8, + interval_low=0.6, + interval_high=1.0, + sample_size=42, + population="fixture population", + ) + self.assertTrue(assertion["assertion_id"].startswith("assertion-")) + + comparison = store.compare_runs( + project_id="project-1", + previous_run_id="run-base", + current_run_id="run-source", + ) + self.assertEqual(["metrics.json"], comparison["artifacts"]["changed"]) + self.assertEqual(["C1"], comparison["claims"]["changed"]) + self.assertTrue(comparison["review_invalidated"]) + + plan = store.prepare_merge_plan( + project_id="project-1", + source_branch="experiment", + target_branch="main", + ) + self.assertEqual("blocked-conflicts", plan["status"]) + self.assertEqual(["C1"], plan["claim_conflicts"]) + self.assertEqual(["metrics.json"], plan["artifact_conflicts"]) + self.assertFalse(plan["executed"]) + self.assertTrue(plan["requires_scientific_review"]) + + summary = store.summary(project_id="project-1") + self.assertEqual(3, len(summary["runs"])) + self.assertEqual(1, summary["assertion_count"]) + self.assertEqual(1, summary["merge_plan_count"]) + self.assertIsNotNone(summary["event_chain_head"]) + + def test_changed_manifest_bytes_cannot_be_reused_under_same_run_id(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + store = ProjectStore(root / "project.sqlite") + store.create_project(project_id="p", title="P", question="Q") + manifest_path, _digest = make_run(root, "run-1", "Claim", 1.0) + store.import_run(project_id="p", manifest_path=manifest_path, branch_name="main") + payload = json.loads(manifest_path.read_text(encoding="utf-8")) + payload["question"] = "Changed question" + manifest_path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") + with self.assertRaisesRegex(ValueError, "different manifest bytes"): + store.import_run(project_id="p", manifest_path=manifest_path, branch_name="main") + with self.assertRaisesRegex(ValueError, "changed on disk"): + store.compare_runs(project_id="p", previous_run_id="run-1", current_run_id="run-1") + + def test_assertion_requires_exact_imported_artifact_hash(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + store = ProjectStore(root / "project.sqlite") + store.create_project(project_id="p", title="P", question="Q") + manifest_path, _digest = make_run(root, "run-1", "Claim", 1.0) + store.import_run(project_id="p", manifest_path=manifest_path, branch_name="main") + with self.assertRaisesRegex(ValueError, "does not match"): + store.add_assertion( + project_id="p", + run_id="run-1", + claim_id="C1", + source_id="study", + polarity="supports", + locator={"artifact_path": "metrics.json", "artifact_sha256": "0" * 64, "json_pointer": "/value"}, + independence_group="cohort", + ) + + +if __name__ == "__main__": + unittest.main() From 3edab87c40c6ee0c931346bf3455ff07e57ea461 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:04:25 +0900 Subject: [PATCH 36/87] Test durable local and Slurm job contracts --- tests/test_compute_backends.py | 148 +++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 tests/test_compute_backends.py diff --git a/tests/test_compute_backends.py b/tests/test_compute_backends.py new file mode 100644 index 00000000..11ea07d2 --- /dev/null +++ b/tests/test_compute_backends.py @@ -0,0 +1,148 @@ +import json +import sys +import tempfile +import unittest +from pathlib import Path + +from codex_science.compute_backends import ( + JobSpec, + LocalBackend, + SlurmBackend, + build_approval_receipt, + validate_approval_receipt, +) + + +def local_spec(working: Path, *, command: list[str], outputs: list[str], timeout: int = 10) -> JobSpec: + return JobSpec.from_payload( + { + "schema_version": 1, + "backend": "local", + "name": "fixture-job", + "command": command, + "working_directory": str(working), + "environment": {"OMP_NUM_THREADS": "1"}, + "inherit_environment": True, + "inputs": [], + "outputs": outputs, + "resources": {"cpus": 1, "memory_mb": 256, "gpus": 0, "wall_time_seconds": timeout}, + "timeout_seconds": timeout, + "cost_cap": None, + "approval_required": False, + "scientific_run_id": "run-fixture", + "checkpoint_paths": [], + } + ) + + +class ComputeBackendTests(unittest.TestCase): + def test_local_job_is_durable_and_outputs_are_hash_collected(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + working = root / "work" + working.mkdir() + state_dir = root / "state" + spec = local_spec( + working, + command=[sys.executable, "-c", "from pathlib import Path; Path('result.json').write_text('{\\\"value\\\": 7}\\n')"], + outputs=["result.json"], + ) + backend = LocalBackend(state_dir) + preflight = backend.preflight(spec) + self.assertTrue(preflight["ready"]) + submitted = backend.submit(spec) + self.assertEqual("submitted", submitted["state"]) + terminal = backend.wait(submitted["job_id"], timeout_seconds=15, poll_seconds=0.05) + self.assertEqual("completed", terminal["state"], json.dumps(terminal, indent=2)) + self.assertEqual(0, terminal["exit_code"]) + collected = backend.collect(submitted["job_id"]) + self.assertEqual("file", collected["outputs"][0]["artifact_type"]) + self.assertEqual(64, len(collected["outputs"][0]["sha256"])) + self.assertEqual(1, collected["outputs"][0]["entry_count"]) + self.assertTrue((state_dir / "jobs" / submitted["job_id"] / "stdout.log").is_file()) + self.assertTrue((state_dir / "jobs" / submitted["job_id"] / "stderr.log").is_file()) + + def test_timeout_remains_a_distinct_terminal_failure(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + working = root / "work" + working.mkdir() + backend = LocalBackend(root / "state") + spec = local_spec( + working, + command=[sys.executable, "-c", "import time; time.sleep(2)"], + outputs=[], + timeout=1, + ) + submitted = backend.submit(spec) + terminal = backend.wait(submitted["job_id"], timeout_seconds=10, poll_seconds=0.05) + self.assertEqual("timed-out", terminal["state"]) + self.assertEqual("wall-time", terminal["failure_class"]) + + def test_approval_is_bound_to_exact_spec(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + working = Path(tempdir) + spec = JobSpec.from_payload( + { + "backend": "slurm", + "name": "approved-job", + "command": ["python", "analysis.py"], + "working_directory": str(working), + "environment": {}, + "inputs": [], + "outputs": ["result.json"], + "resources": {"cpus": 2, "memory_mb": 1024, "gpus": 1, "gpu_type": "a100", "wall_time_seconds": 600, "partition": "gpu"}, + "timeout_seconds": 600, + "approval_required": True, + } + ) + receipt = build_approval_receipt(spec, approved_by="jaemin", target="cluster-a", approved_at="2026-07-21T00:00:00Z") + validate_approval_receipt(receipt, spec) + changed = JobSpec.from_payload({**spec.to_dict(), "timeout_seconds": 601}) + with self.assertRaisesRegex(ValueError, "different job spec"): + validate_approval_receipt(receipt, changed) + + def test_slurm_render_is_nonexecuting_and_resource_explicit(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + spec = JobSpec.from_payload( + { + "backend": "slurm", + "name": "dock", + "command": ["python", "dock.py", "--input", "input.json"], + "working_directory": str(root), + "environment": {"OMP_NUM_THREADS": "4"}, + "inputs": [], + "outputs": ["poses.sdf"], + "resources": {"cpus": 4, "memory_mb": 8192, "gpus": 1, "gpu_type": "a100", "wall_time_seconds": 7200, "partition": "gpu", "account": "science"}, + "timeout_seconds": 7200, + "approval_required": True, + } + ) + backend = SlurmBackend(root / "state") + script = backend.render_script(spec, job_id="preview") + self.assertIn("#SBATCH --cpus-per-task=4", script) + self.assertIn("#SBATCH --mem=8192M", script) + self.assertIn("#SBATCH --gres=gpu:a100:1", script) + self.assertIn("#SBATCH --partition=gpu", script) + self.assertIn("#SBATCH --account=science", script) + self.assertIn("timeout --signal=TERM 7200s", script) + self.assertIn("python dock.py --input input.json", script) + + def test_secret_like_environment_keys_are_rejected(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + with self.assertRaisesRegex(ValueError, "secret-like"): + JobSpec.from_payload( + { + "backend": "local", + "name": "bad", + "command": ["echo", "x"], + "working_directory": tempdir, + "environment": {"API_TOKEN": "secret"}, + "outputs": [], + } + ) + + +if __name__ == "__main__": + unittest.main() From 2bdf48ff5b08014c88c4762e1a399849f843a13f Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:04:57 +0900 Subject: [PATCH 37/87] Test native skill maturity policy and declarations --- tests/test_skill_maturity.py | 152 +++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 tests/test_skill_maturity.py diff --git a/tests/test_skill_maturity.py b/tests/test_skill_maturity.py new file mode 100644 index 00000000..312db7a3 --- /dev/null +++ b/tests/test_skill_maturity.py @@ -0,0 +1,152 @@ +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.skill_maturity import audit_native_skills, evaluate_skill + + +SKILL_TEXT = """--- +name: demo-skill +description: Run a bounded scientific demo workflow. +--- + +# Demo skill + +## Decision contract + +Record the question, scope, and acceptance criteria. + +## Reference usage + +Read [the command reference](references/commands.md) before execution. + +## Workflow + +Use `$science-provenance`, execute the fixture, and use `$science-review`. + +## Outputs + +Write a machine-readable result. + +## Boundaries + +Do not overclaim the fixture. +""" + + +class SkillMaturityTests(unittest.TestCase): + def test_repository_policy_passes_for_declared_tier_one_skills(self) -> None: + root = Path(__file__).resolve().parents[1] + report = audit_native_skills(root) + self.assertEqual("passed", report["status"], json.dumps(report["findings"], indent=2)) + by_name = {item["name"]: item for item in report["skills"]} + self.assertEqual("L4", by_name["literature-review"]["computed_maturity"]) + self.assertEqual("L4", by_name["docking-validation"]["computed_maturity"]) + self.assertGreaterEqual(int(by_name["codex-science"]["computed_maturity"][1:]), 3) + + def test_synthetic_l4_requires_references_outputs_fixture_failures_and_tests(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + skill = root / "skills" / "demo" + references = skill / "references" + references.mkdir(parents=True) + (skill / "SKILL.md").write_text(SKILL_TEXT, encoding="utf-8") + (references / "commands.md").write_text("# Commands\n\n## Usage\n", encoding="utf-8") + (references / "index.json").write_text( + json.dumps( + { + "schema_version": 1, + "skill": "demo-skill", + "references": [ + { + "id": "commands", + "path": "references/commands.md", + "purpose": "Exact command contract.", + "read_when": ["before execution"], + "required_before": ["running the demo"], + "search_patterns": ["## Usage"], + "authority": "first-party", + "version": "1", + "evidence_boundary": "Commands do not prove scientific validity." + } + ] + } + ), + encoding="utf-8", + ) + fixture = root / "fixtures" / "case.json" + test_file = root / "tests" / "test_demo.py" + fixture.parent.mkdir() + test_file.parent.mkdir() + fixture.write_text("{}\n", encoding="utf-8") + test_file.write_text("# fixture test\n", encoding="utf-8") + (skill / "quality.json").write_text( + json.dumps( + { + "schema_version": 1, + "skill": "demo-skill", + "roles": ["executor", "reviewer"], + "declared_maturity": "L4", + "output_schemas": ["demo-result-v1"], + "acceptance_fixtures": ["fixtures/case.json"], + "seeded_failures": ["wrong-unit"], + "test_files": ["tests/test_demo.py"], + "limitations": ["Fixture only."] + } + ), + encoding="utf-8", + ) + record = evaluate_skill(root, skill) + self.assertEqual("L4", record["computed_maturity"], record["reasons"]) + self.assertEqual(["executor", "reviewer"], record["roles"]) + + def test_invalid_l4_declaration_is_not_silently_downgraded(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + skill = root / "skills" / "demo" + references = skill / "references" + references.mkdir(parents=True) + (skill / "SKILL.md").write_text(SKILL_TEXT, encoding="utf-8") + (references / "commands.md").write_text("# Commands\n", encoding="utf-8") + (references / "index.json").write_text( + json.dumps( + { + "schema_version": 1, + "skill": "demo-skill", + "references": [ + { + "id": "commands", + "path": "references/commands.md", + "read_when": ["execution"], + "required_before": ["execution"], + "evidence_boundary": "Fixture only." + } + ] + } + ), + encoding="utf-8", + ) + (skill / "quality.json").write_text( + json.dumps( + { + "schema_version": 1, + "skill": "demo-skill", + "roles": ["executor"], + "declared_maturity": "L4", + "output_schemas": ["demo-v1"], + "acceptance_fixtures": [], + "seeded_failures": [], + "test_files": [], + "limitations": [] + } + ), + encoding="utf-8", + ) + record = evaluate_skill(root, skill) + self.assertLess(int(record["computed_maturity"][1:]), 4) + self.assertTrue(any("invalid quality declaration" in reason for reason in record["reasons"])) + + +if __name__ == "__main__": + unittest.main() From 1e7fceb0a32dd1e24dc30954a9e9e76fd0c9b213 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:07:50 +0900 Subject: [PATCH 38/87] Add idempotent native runtime integration script --- scripts/integrate_native_runtime_v4.py | 430 +++++++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 scripts/integrate_native_runtime_v4.py diff --git a/scripts/integrate_native_runtime_v4.py b/scripts/integrate_native_runtime_v4.py new file mode 100644 index 00000000..dbdac93d --- /dev/null +++ b/scripts/integrate_native_runtime_v4.py @@ -0,0 +1,430 @@ +#!/usr/bin/env python3 +"""One-time idempotent integration for native runtime v4 source additions.""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[1] + + +def write_json(path: Path, payload: dict[str, Any]) -> None: + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + + +def update_reference_index(path: Path, entry: dict[str, Any]) -> None: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise SystemExit(f"reference index must be an object: {path}") + key = "references" if "references" in payload else "entries" if "entries" in payload else "references" + entries = payload.setdefault(key, []) + if not isinstance(entries, list): + raise SystemExit(f"reference index {key} must be a list: {path}") + existing = next((item for item in entries if isinstance(item, dict) and item.get("id") == entry["id"]), None) + if existing is None: + entries.append(entry) + elif existing != entry: + existing.clear() + existing.update(entry) + entries.sort(key=lambda item: str(item.get("id", "")) if isinstance(item, dict) else "") + write_json(path, payload) + + +def add_reference_paragraph(path: Path, marker: str, paragraph: str) -> None: + text = path.read_text(encoding="utf-8") + if marker in text: + return + heading = "## Reference usage" + if heading in text: + start = text.index(heading) + len(heading) + next_heading = text.find("\n## ", start) + if next_heading < 0: + text = text.rstrip() + "\n\n" + paragraph + "\n" + else: + text = text[:next_heading].rstrip() + "\n\n" + paragraph + "\n" + text[next_heading:] + else: + anchor = "## Workflow" + if anchor not in text: + raise SystemExit(f"cannot find reference insertion point: {path}") + text = text.replace(anchor, f"## Reference usage\n\n{paragraph}\n\n{anchor}", 1) + path.write_text(text, encoding="utf-8") + + +def append_once(path: Path, marker: str, content: str) -> None: + text = path.read_text(encoding="utf-8") + if marker in text: + return + path.write_text(text.rstrip() + "\n\n" + content.strip() + "\n", encoding="utf-8") + + +def replace_once(path: Path, old: str, new: str, label: str) -> None: + text = path.read_text(encoding="utf-8") + if new in text: + return + if old not in text: + raise SystemExit(f"missing {label} marker in {path}") + path.write_text(text.replace(old, new, 1), encoding="utf-8") + + +def patch_reference_indexes_and_skills() -> None: + update_reference_index( + ROOT / "skills" / "science-provenance" / "references" / "index.json", + { + "id": "artifact-runtime", + "path": "references/artifact-runtime.md", + "purpose": "Bounded native views, hash-bound selections, and non-executing transform proposals for scientific artifacts.", + "read_when": [ + "before rendering a scientific artifact", + "before creating an atom, residue, locus, cell, table, graph, figure, or trajectory selection", + "before converting an annotation into a proposed edit or rerun" + ], + "required_before": [ + "calling scripts/render_artifact_runtime.py", + "recording artifact-runtime-descriptor, artifact-selection, or transform-proposal sidecars" + ], + "search_patterns": [ + "### Describe an artifact", + "### Create a typed selection", + "### Propose a transform", + "## Failure handling", + "## Common mistakes" + ], + "authority": "Codex Science first-party runtime contract", + "version": "1", + "evidence_boundary": "A bounded runtime view improves inspection but does not validate a scientific interpretation or execute a transform." + }, + ) + add_reference_paragraph( + ROOT / "skills" / "science-provenance" / "SKILL.md", + "references/artifact-runtime.md", + "Before rendering, selecting, or proposing a change to a structure, molecule, genome track, single-cell object, table, figure, evidence graph, trajectory, or large dataset, **MUST** read [the native artifact runtime contract](references/artifact-runtime.md). Generate a hash-bound descriptor and typed selection before any transform proposal; never mutate artifact bytes from a visual annotation alone.", + ) + + update_reference_index( + ROOT / "skills" / "codex-science" / "references" / "index.json", + { + "id": "project-evidence-store", + "path": "references/project-evidence-store.md", + "purpose": "Project-level immutable run imports, evidence assertions, hypothesis forks, comparisons, and non-executing merge plans.", + "read_when": [ + "before importing more than one run into a project", + "before creating a hypothesis or experiment branch", + "before recording a quantitative evidence assertion", + "before comparing or merging scientific branches" + ], + "required_before": [ + "calling scripts/science_project.py", + "treating project-level state as scientific lineage" + ], + "search_patterns": [ + "## Import a reviewed run", + "## Fork a hypothesis branch", + "## Add a quantitative evidence assertion", + "## Prepare a merge plan", + "## Failure handling" + ], + "authority": "Codex Science first-party project lineage contract", + "version": "1", + "evidence_boundary": "The project store indexes immutable runs and decisions; it does not replace manifests, evidence, or independent review." + }, + ) + add_reference_paragraph( + ROOT / "skills" / "codex-science" / "SKILL.md", + "references/project-evidence-store.md", + "For a multi-run research program, hypothesis fork, quantitative evidence assertion, or branch comparison, **MUST** read [the project evidence store contract](references/project-evidence-store.md) before calling `scripts/science_project.py`. Imported manifests and artifact hashes remain authoritative; merge plans are non-executing and always require a new scientific synthesis and review.", + ) + + update_reference_index( + ROOT / "authored-skills" / "remote-scientific-compute" / "references" / "index.json", + { + "id": "job-runtime", + "path": "references/job-runtime.md", + "purpose": "Durable local and Slurm job specs, approvals, submission, status, cancellation, checkpoint, and output collection receipts.", + "read_when": [ + "before constructing a durable job spec", + "before submitting, polling, cancelling, or collecting a local or Slurm job", + "before approving a non-local resource allocation" + ], + "required_before": [ + "calling scripts/science_job.py", + "submitting work through LocalBackend or SlurmBackend" + ], + "search_patterns": [ + "## Job spec", + "## Local execution", + "## Approval receipt", + "## Slurm execution", + "## Checkpoint and resume", + "## Failure handling" + ], + "authority": "Codex Science first-party compute runtime contract", + "version": "1", + "evidence_boundary": "A job receipt proves execution state and collected hashes, not scientific success." + }, + ) + add_reference_paragraph( + ROOT / "authored-skills" / "remote-scientific-compute" / "SKILL.md", + "references/job-runtime.md", + "Before constructing, approving, submitting, polling, cancelling, or collecting a durable local or Slurm job, **MUST** read [the job runtime contract](references/job-runtime.md). Use argv lists, exact resource and timeout caps, hash-bound approvals for Slurm, and terminal output receipts; a zero exit code is not scientific acceptance.", + ) + + +def patch_source_bugs() -> None: + replace_once( + ROOT / "src" / "codex_science" / "artifact_runtime.py", + " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n", + " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n", + "artifact runtime warning tuple", + ) + project_cli = ROOT / "scripts" / "science_project.py" + text = project_cli.read_text(encoding="utf-8") + if "import sqlite3\n" not in text: + text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) + text = text.replace( + " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", + " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", + ) + project_cli.write_text(text, encoding="utf-8") + maturity = ROOT / "src" / "codex_science" / "skill_maturity.py" + text = maturity.read_text(encoding="utf-8") + text = text.replace( + ' entries = payload.get("references")\n', + ' entries = payload.get("references", payload.get("entries"))\n', + ) + maturity.write_text(text, encoding="utf-8") + + +def patch_advanced_sidecars() -> None: + path = ROOT / "src" / "codex_science" / "advanced_sidecars.py" + append_once( + path, + "# Native artifact runtime sidecar extension v1", + r''' +# Native artifact runtime sidecar extension v1 +from codex_science.artifact_runtime import ( + stale_selection as _runtime_stale_selection, + validate_runtime_descriptor as _validate_runtime_descriptor, + validate_selection as _validate_runtime_selection, + validate_transform_proposal as _validate_transform_proposal, +) + +_base_validate_advanced_sidecars = validate_advanced_sidecars +_base_review_advanced_sidecars = review_advanced_sidecars + + +def validate_advanced_sidecars( + manifest: dict[str, Any], + run_dir: Path, + verified: dict[str, Path], + *, + base_sidecars: dict[str, Any] | None = None, +) -> dict[str, Any]: + result = _base_validate_advanced_sidecars( + manifest, run_dir, verified, base_sidecars=base_sidecars + ) + result.setdefault("runtime_descriptors", []) + result.setdefault("artifact_selections", []) + result.setdefault("transform_proposals", []) + result.setdefault("runtime_descriptor_stale", []) + result.setdefault("runtime_paths", {}) + artifact_hashes = result.get("artifact_hashes", {}) + selections_by_id: dict[str, dict[str, Any]] = {} + pending_proposals: list[dict[str, Any]] = [] + for record in manifest.get("artifacts", []): + kind = str(record.get("kind", "")) + relative = str(record.get("path", "")) + if kind not in { + "artifact-runtime-descriptor", + "artifact-selection", + "transform-proposal", + }: + continue + payload = _load(verified[relative], kind) + result["runtime_paths"].setdefault(kind, []).append(relative) + if kind == "artifact-runtime-descriptor": + _validate_runtime_descriptor(payload) + if artifact_hashes.get(str(payload["artifact_path"])) != str( + payload["artifact_sha256"] + ).lower(): + result["runtime_descriptor_stale"].append(payload) + result["runtime_descriptors"].append(payload) + elif kind == "artifact-selection": + _validate_runtime_selection(payload) + selection = _runtime_stale_selection(payload, artifact_hashes) + result["artifact_selections"].append(selection) + selections_by_id[str(selection["selection_id"])] = selection + else: + pending_proposals.append(payload) + for proposal in pending_proposals: + selection = selections_by_id.get(str(proposal.get("selection_id", ""))) + _validate_transform_proposal(proposal, selection) + result["transform_proposals"].append(proposal) + return result + + +def review_advanced_sidecars( + sidecars: dict[str, Any], *, registry_path: Path | None = None +) -> list[dict[str, str]]: + findings = list( + _base_review_advanced_sidecars(sidecars, registry_path=registry_path) + ) + for descriptor in sidecars.get("runtime_descriptor_stale", []): + findings.append( + { + "code": "stale-runtime-descriptor", + "severity": "minor", + "message": ( + "Artifact runtime descriptor covers changed or missing bytes: " + f"{descriptor.get('artifact_path', '')}" + ), + } + ) + for selection in sidecars.get("artifact_selections", []): + if selection.get("status") == "stale-anchor": + findings.append( + { + "code": "stale-artifact-selection", + "severity": "minor", + "message": ( + f"Artifact selection {selection.get('selection_id')} points to " + "changed or missing bytes." + ), + } + ) + selection_ids = { + str(item.get("selection_id")) + for item in sidecars.get("artifact_selections", []) + } + for proposal in sidecars.get("transform_proposals", []): + if str(proposal.get("selection_id")) not in selection_ids: + findings.append( + { + "code": "missing-transform-selection", + "severity": "major", + "message": ( + f"Transform proposal {proposal.get('proposal_id')} references " + "a selection not present in the bundle." + ), + } + ) + unique = { + (item["code"], item.get("claim_id", ""), item["message"]): item + for item in findings + } + return sorted( + unique.values(), + key=lambda item: ( + item.get("severity", ""), + item["code"], + item.get("claim_id", ""), + item["message"], + ), + ) +''', + ) + + +def patch_validation_gates() -> None: + check = ROOT / "scripts" / "check.sh" + text = check.read_text(encoding="utf-8") + command = " uv run python scripts/audit_native_skill_maturity.py --require-clean\n" + if command not in text: + marker = " uv run python scripts/audit_skill_references.py --require-clean\n" + if marker in text: + text = text.replace(marker, marker + command, 1) + else: + echo = ' echo "scientific contracts: ok"\n' + if echo not in text: + raise SystemExit("cannot locate scientific contract gate in check.sh") + text = text.replace(echo, command + echo, 1) + check.write_text(text, encoding="utf-8") + + candidate = ROOT / "scripts" / "candidate_contract_check.py" + text = candidate.read_text(encoding="utf-8") + needle = 'run([python, "scripts/audit_native_skill_maturity.py", "--require-clean"], cwd=root)' + if needle not in text: + marker = 'run([python, "scripts/audit_skill_references.py", "--require-clean"], cwd=root)' + if marker in text: + text = text.replace(marker, marker + "\n " + needle, 1) + else: + marker = 'run([python, "scripts/generate_wrappers.py", "--check"], cwd=root)' + if marker not in text: + raise SystemExit("cannot locate candidate reference/wrapper gate") + text = text.replace(marker, marker + "\n " + needle, 1) + candidate.write_text(text, encoding="utf-8") + + +def patch_docs() -> None: + append_once( + ROOT / "docs" / "PLATFORM_CONTRACTS.md", + "## Native artifact runtime and project evidence memory", + ''' +## Native artifact runtime and project evidence memory + +Codex Science separates bounded artifact views, typed selections, and non-executing transform proposals. Read `skills/science-provenance/references/artifact-runtime.md` before using `scripts/render_artifact_runtime.py`. Runtime descriptors and selections are bound to the manifest artifact SHA-256; changed bytes make them stale. + +Multi-run programs use `scripts/science_project.py` and the contract in `skills/codex-science/references/project-evidence-store.md`. The SQLite database indexes immutable run manifests, branch lineage, quantitative evidence assertions, comparisons, and merge plans. Run bundles remain authoritative and merge plans never combine bytes automatically. + +## Durable compute runtime + +`scripts/science_job.py` implements a durable local backend and an approval-gated Slurm backend. Read `authored-skills/remote-scientific-compute/references/job-runtime.md` before constructing or submitting a job. Job state, logs, cancellation, checkpoints, and output hashes are operational provenance; process success is not scientific acceptance. + +## Native skill maturity + +`scripts/audit_native_skill_maturity.py --require-clean` audits first-party skills against `catalog/native-skill-policy.json`. L3 requires the full decision contract, progressive reference index, and machine-readable output declarations. L4 additionally requires checked-in acceptance fixtures, seeded failure classes, and test files. A declared level above the computed level is a blocking policy finding. +''', + ) + append_once( + ROOT / "docs" / "NATIVE_SKILL_STANDARD.md", + "## Machine-readable maturity declarations", + ''' +## Machine-readable maturity declarations + +A decision-bearing first-party skill may include `quality.json` beside `SKILL.md`: + +```json +{ + "schema_version": 1, + "skill": "example-skill", + "roles": ["executor", "reviewer"], + "declared_maturity": "L4", + "output_schemas": ["example-result-v1"], + "acceptance_fixtures": ["examples/example/input.json"], + "seeded_failures": ["wrong-unit"], + "test_files": ["tests/test_example.py"], + "limitations": ["Fixture-only validation."] +} +``` + +The repository policy is `catalog/native-skill-policy.json`; the deterministic auditor is `scripts/audit_native_skill_maturity.py`. Declarations do not grant maturity. The auditor computes maturity from the actual instruction contract, progressive references, declared outputs, existing fixtures, seeded failures, and tests. +''', + ) + append_once( + ROOT / "skills" / "science-provenance" / "references" / "artifact-contract.md", + "### Runtime collaboration sidecars", + ''' +### Runtime collaboration sidecars + +- `artifact-runtime-descriptor`: bounded native view bound to source artifact path and SHA-256; +- `artifact-selection`: typed atom, residue, locus, cell, table, figure, graph, frame, text, or byte selection bound to artifact SHA-256; +- `transform-proposal`: non-executing operation, parameters, affected steps, expected outputs, and approval boundary bound to a selection. + +Changed source bytes make descriptors and selections stale. A proposal never authorizes or executes a mutation and cannot be used as evidence that a rerun occurred. +''', + ) + + +def main() -> int: + patch_reference_indexes_and_skills() + patch_source_bugs() + patch_advanced_sidecars() + patch_validation_gates() + patch_docs() + print("native runtime v4 integration: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From b8f9de89b1e147adcb74f44a7401ea7aac931743 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:08:11 +0900 Subject: [PATCH 39/87] Integrate and validate native runtime v4 --- .../workflows/integrate-native-runtime-v4.yml | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/integrate-native-runtime-v4.yml diff --git a/.github/workflows/integrate-native-runtime-v4.yml b/.github/workflows/integrate-native-runtime-v4.yml new file mode 100644 index 00000000..4cae6051 --- /dev/null +++ b/.github/workflows/integrate-native-runtime-v4.yml @@ -0,0 +1,54 @@ +name: Integrate native runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +jobs: + integrate: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Integrate references and validation routes + run: uv run python scripts/integrate_native_runtime_v4.py + - name: Compile new runtime modules + run: uv run python -m compileall -q src scripts + - name: Run focused runtime tests + run: | + uv run python -m unittest \ + tests.test_artifact_runtime \ + tests.test_project_store \ + tests.test_compute_backends \ + tests.test_skill_maturity + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove one-time integration resources + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f scripts/integrate_native_runtime_v4.py + - name: Commit integrated implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No integration changes" + exit 0 + fi + git commit -m "Integrate native artifact, project, compute, and skill maturity runtimes" + git push origin HEAD:"$branch" From 2fbf19ebe8a1fa5a8b105a81480790abdf68b5a6 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:11:06 +0900 Subject: [PATCH 40/87] Add final native runtime correctness patches --- scripts/finalize_native_runtime_v4.py | 103 ++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 scripts/finalize_native_runtime_v4.py diff --git a/scripts/finalize_native_runtime_v4.py b/scripts/finalize_native_runtime_v4.py new file mode 100644 index 00000000..e5db7c2f --- /dev/null +++ b/scripts/finalize_native_runtime_v4.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Idempotent final correctness patches for native runtime v4.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def replace_if_present(path: Path, old: str, new: str) -> None: + text = path.read_text(encoding="utf-8") + if new in text: + return + if old not in text: + raise SystemExit(f"expected patch marker missing: {path}: {old[:80]!r}") + path.write_text(text.replace(old, new, 1), encoding="utf-8") + + +def main() -> int: + integration = ROOT / "scripts" / "integrate_native_runtime_v4.py" + if integration.is_file(): + subprocess.run([sys.executable, str(integration)], cwd=ROOT, check=True) + + artifact_runtime = ROOT / "src" / "codex_science" / "artifact_runtime.py" + replace_if_present( + artifact_runtime, + " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n", + " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n", + ) + + project_cli = ROOT / "scripts" / "science_project.py" + text = project_cli.read_text(encoding="utf-8") + if "import sqlite3\n" not in text: + text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) + text = text.replace( + " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", + " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", + ) + project_cli.write_text(text, encoding="utf-8") + + maturity = ROOT / "src" / "codex_science" / "skill_maturity.py" + text = maturity.read_text(encoding="utf-8") + text = text.replace( + ' entries = payload.get("references")\n', + ' entries = payload.get("references", payload.get("entries"))\n', + ) + old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" + has_review = "$science-review" in text or skill_dir.name == "science-review" +''' + new = ''' if skill_dir.name in CORE_SECTION_ALIASES: + # The three core skills collectively implement the provenance/review + # handoff; do not require them to invoke themselves by token. + has_provenance = True + has_review = True + else: + has_provenance = "$science-provenance" in text + has_review = "$science-review" in text +''' + if new not in text: + if old not in text: + raise SystemExit("skill maturity handoff marker is missing") + text = text.replace(old, new, 1) + maturity.write_text(text, encoding="utf-8") + + compute = ROOT / "src" / "codex_science" / "compute_backends.py" + text = compute.read_text(encoding="utf-8") + old = ''' state = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at, worker_pid=process.pid, message="local worker started") + _atomic_json(job_dir / "state.json", state) + return state +''' + new = ''' current = _read_json(job_dir / "state.json") + # A short command may already have written running or terminal state. + # Add the PID only while the original submitted state is still current; + # never overwrite a newer worker receipt. + if current.get("state") == "submitted": + state = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + worker_pid=process.pid, + message="local worker started", + ) + _atomic_json(job_dir / "state.json", state) + else: + state = current + return state +''' + if new not in text: + if old not in text: + raise SystemExit("local submit race marker is missing") + text = text.replace(old, new, 1) + compute.write_text(text, encoding="utf-8") + + print("native runtime v4 finalization: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From d97932283020879150b0a05e0f9ba25713ebf1cf Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:11:26 +0900 Subject: [PATCH 41/87] Finalize and validate native runtime v4 --- .../workflows/finalize-native-runtime-v4.yml | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/finalize-native-runtime-v4.yml diff --git a/.github/workflows/finalize-native-runtime-v4.yml b/.github/workflows/finalize-native-runtime-v4.yml new file mode 100644 index 00000000..a78ff18d --- /dev/null +++ b/.github/workflows/finalize-native-runtime-v4.yml @@ -0,0 +1,59 @@ +name: Finalize native runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: native-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + finalize: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Finalize source and progressive references + run: uv run python scripts/finalize_native_runtime_v4.py + - name: Compile runtime + run: uv run python -m compileall -q src scripts + - name: Run focused runtime tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove one-time integration resources + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + - name: Commit finalized runtime + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No finalization changes" + exit 0 + fi + git commit -m "Finalize native artifact, project, compute, and skill maturity runtimes" + git push origin HEAD:"$branch" From 9599baa48a3fe485cc2d4548e677cb89898f40e4 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:14:07 +0900 Subject: [PATCH 42/87] Test runtime sidecar validation and staleness --- tests/test_artifact_runtime_sidecars.py | 119 ++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 tests/test_artifact_runtime_sidecars.py diff --git a/tests/test_artifact_runtime_sidecars.py b/tests/test_artifact_runtime_sidecars.py new file mode 100644 index 00000000..d95e53fa --- /dev/null +++ b/tests/test_artifact_runtime_sidecars.py @@ -0,0 +1,119 @@ +import hashlib +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.artifact_runtime import build_selection, build_transform_proposal, describe_runtime +from codex_science.artifacts import add_artifact, new_manifest, validate_bundle, write_manifest +from codex_science.review import review_manifest + + +def digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +class ArtifactRuntimeSidecarTests(unittest.TestCase): + def test_runtime_descriptor_selection_and_proposal_validate_as_hashed_sidecars(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir) + data = run_dir / "metrics.csv" + data.write_text("metric,value\nrmsd,1.2\n", encoding="utf-8") + descriptor = describe_runtime( + data, + artifact_path="metrics.csv", + kind="metrics", + media_type="text/csv", + max_bytes=1024, + max_records=20, + generated_at="2026-07-21T00:00:00Z", + ).to_dict() + selection = build_selection( + descriptor, + selector_type="cell", + selector={"row": 1, "column": "value"}, + selected_by="reviewer", + reason="Inspect the RMSD threshold input.", + created_at="2026-07-21T00:01:00Z", + ) + proposal = build_transform_proposal( + selection, + operation="recalculate-metric", + parameters={"symmetry_aware": True}, + reason="Use the preregistered symmetry-aware definition.", + affected_steps=["compute-metrics", "review"], + expected_outputs=["metrics.csv"], + proposed_by="reviewer", + created_at="2026-07-21T00:02:00Z", + ) + descriptor_path = run_dir / "metrics.runtime.json" + selection_path = run_dir / "metric.selection.json" + proposal_path = run_dir / "metric.proposal.json" + for path, payload in ((descriptor_path, descriptor), (selection_path, selection), (proposal_path, proposal)): + path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + manifest = new_manifest( + "runtime-sidecar-run", + "Does the metric artifact satisfy its declared definition?", + [{"id": "review", "description": "Inspect metric definition", "status": "completed"}], + ) + manifest["claims"] = [{"id": "C1", "text": "The metric is recorded.", "evidence": ["metrics.csv"]}] + manifest["review"] = {"status": "passed", "findings": []} + add_artifact(manifest, "metrics.csv", kind="metrics", sha256=digest(data)) + add_artifact(manifest, descriptor_path.name, kind="artifact-runtime-descriptor", sha256=digest(descriptor_path)) + add_artifact(manifest, selection_path.name, kind="artifact-selection", sha256=digest(selection_path)) + add_artifact(manifest, proposal_path.name, kind="transform-proposal", sha256=digest(proposal_path)) + write_manifest(manifest, run_dir / "manifest.json") + + sidecars = validate_bundle(manifest, run_dir) + self.assertEqual(1, len(sidecars["runtime_descriptors"])) + self.assertEqual(1, len(sidecars["artifact_selections"])) + self.assertEqual(1, len(sidecars["transform_proposals"])) + self.assertEqual([], sidecars["advanced_findings"]) + self.assertEqual("passed", review_manifest(manifest, run_dir, sidecars=sidecars)["status"]) + + def test_changed_target_bytes_make_runtime_records_stale_without_rewriting_them(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir) + data = run_dir / "metrics.csv" + data.write_text("metric,value\nrmsd,1.2\n", encoding="utf-8") + descriptor = describe_runtime( + data, + artifact_path="metrics.csv", + kind="metrics", + max_bytes=1024, + max_records=20, + generated_at="2026-07-21T00:00:00Z", + ).to_dict() + selection = build_selection( + descriptor, + selector_type="cell", + selector={"row": 1, "column": "value"}, + selected_by="reviewer", + reason="Inspect old bytes.", + created_at="2026-07-21T00:01:00Z", + ) + data.write_text("metric,value\nrmsd,2.4\n", encoding="utf-8") + descriptor_path = run_dir / "metrics.runtime.json" + selection_path = run_dir / "metric.selection.json" + descriptor_path.write_text(json.dumps(descriptor, sort_keys=True) + "\n", encoding="utf-8") + selection_path.write_text(json.dumps(selection, sort_keys=True) + "\n", encoding="utf-8") + + manifest = new_manifest( + "stale-runtime-run", + "Did the inspected metric bytes change?", + [{"id": "inspect", "description": "Inspect stale anchor", "status": "completed"}], + ) + manifest["claims"] = [{"id": "C1", "text": "The current metric is recorded.", "evidence": ["metrics.csv"]}] + manifest["review"] = {"status": "findings", "findings": []} + add_artifact(manifest, "metrics.csv", kind="metrics", sha256=digest(data)) + add_artifact(manifest, descriptor_path.name, kind="artifact-runtime-descriptor", sha256=digest(descriptor_path)) + add_artifact(manifest, selection_path.name, kind="artifact-selection", sha256=digest(selection_path)) + sidecars = validate_bundle(manifest, run_dir) + codes = {item["code"] for item in sidecars["advanced_findings"]} + self.assertIn("stale-runtime-descriptor", codes) + self.assertIn("stale-artifact-selection", codes) + + +if __name__ == "__main__": + unittest.main() From c42c5da2415736f48b96f470f51ed853f2271a4d Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:16:24 +0900 Subject: [PATCH 43/87] Complete native runtime with regenerated inventory and wrappers --- .../workflows/complete-native-runtime-v4.yml | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 .github/workflows/complete-native-runtime-v4.yml diff --git a/.github/workflows/complete-native-runtime-v4.yml b/.github/workflows/complete-native-runtime-v4.yml new file mode 100644 index 00000000..4b1583bf --- /dev/null +++ b/.github/workflows/complete-native-runtime-v4.yml @@ -0,0 +1,118 @@ +name: Complete native runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: complete-native-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + complete: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Integrate source and progressive references + run: | + if [ -f scripts/integrate_native_runtime_v4.py ]; then + uv run python scripts/integrate_native_runtime_v4.py + fi + if [ -f scripts/finalize_native_runtime_v4.py ]; then + uv run python scripts/finalize_native_runtime_v4.py + fi + - name: Make local submission receipt deterministic + run: | + python - <<'PY' + from pathlib import Path + path = Path("src/codex_science/compute_backends.py") + text = path.read_text(encoding="utf-8") + old = ''' current = _read_json(job_dir / "state.json") + # A short command may already have written running or terminal state. + # Add the PID only while the original submitted state is still current; + # never overwrite a newer worker receipt. + if current.get("state") == "submitted": + state = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + worker_pid=process.pid, + message="local worker started", + ) + _atomic_json(job_dir / "state.json", state) + else: + state = current + return state + ''' + old = '\n'.join(line[10:] if line.startswith(' ') else line for line in old.splitlines()) + '\n' + new = ''' current = _read_json(job_dir / "state.json") + # A short command may already have written running or terminal state. + # Persist the PID only while the original submitted state is current, + # but always return the historical submission receipt to the caller. + submitted_receipt = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + worker_pid=process.pid, + message="local worker started", + ) + if current.get("state") == "submitted": + _atomic_json(job_dir / "state.json", submitted_receipt) + return submitted_receipt + ''' + new = '\n'.join(line[10:] if line.startswith(' ') else line for line in new.splitlines()) + '\n' + if new not in text: + if old not in text: + raise SystemExit("local submission receipt marker is missing") + path.write_text(text.replace(old, new, 1), encoding="utf-8") + PY + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile runtime + run: uv run python -m compileall -q src scripts + - name: Run focused runtime tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove one-time resources + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + - name: Commit completed implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No completion changes" + exit 0 + fi + git commit -m "Complete native artifact, project, compute, and skill maturity runtimes" + git push origin HEAD:"$branch" From e24bf4f32b1b21132c845d83aacd3e48cf8786c3 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:17:15 +0900 Subject: [PATCH 44/87] Add robust final native runtime applicator --- scripts/apply_complete_native_runtime_v4.py | 105 ++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 scripts/apply_complete_native_runtime_v4.py diff --git a/scripts/apply_complete_native_runtime_v4.py b/scripts/apply_complete_native_runtime_v4.py new file mode 100644 index 00000000..8f5c3b5e --- /dev/null +++ b/scripts/apply_complete_native_runtime_v4.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +"""Apply all native runtime v4 integrations and correctness patches idempotently.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def run_optional(path: Path) -> None: + if path.is_file(): + subprocess.run([sys.executable, str(path)], cwd=ROOT, check=True) + + +def patch_compute_submission() -> None: + path = ROOT / "src" / "codex_science" / "compute_backends.py" + text = path.read_text(encoding="utf-8") + class_start = text.index("class LocalBackend:") + submit_start = text.index(" def submit(", class_start) + process_start = text.index(" process = subprocess.Popen(", submit_start) + status_start = text.index("\n def status(", process_start) + section = text[process_start:status_start] + if "return submitted_receipt" in section: + return + candidates = [index for token in ("\n current = _read_json", "\n state = _state_material") if (index := section.find(token)) >= 0] + if not candidates: + raise SystemExit("cannot locate LocalBackend submission receipt tail") + tail_start = min(candidates) + replacement = ''' + current = _read_json(job_dir / "state.json") + # A short command may already have written running or terminal state. + # Persist the PID only while the original submitted state is current, + # but always return the historical submission receipt to the caller. + submitted_receipt = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + worker_pid=process.pid, + message="local worker started", + ) + if current.get("state") == "submitted": + _atomic_json(job_dir / "state.json", submitted_receipt) + return submitted_receipt +''' + new_section = section[:tail_start] + replacement + path.write_text(text[:process_start] + new_section + text[status_start:], encoding="utf-8") + + +def patch_core_source() -> None: + artifact = ROOT / "src" / "codex_science" / "artifact_runtime.py" + text = artifact.read_text(encoding="utf-8") + old = " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n" + new = " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n" + if old in text: + artifact.write_text(text.replace(old, new, 1), encoding="utf-8") + elif new not in text: + raise SystemExit("artifact runtime descriptor construction marker is missing") + + cli = ROOT / "scripts" / "science_project.py" + text = cli.read_text(encoding="utf-8") + if "import sqlite3\n" not in text: + text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) + text = text.replace( + " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", + " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", + ) + cli.write_text(text, encoding="utf-8") + + maturity = ROOT / "src" / "codex_science" / "skill_maturity.py" + text = maturity.read_text(encoding="utf-8") + text = text.replace(' entries = payload.get("references")\n', ' entries = payload.get("references", payload.get("entries"))\n') + old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" + has_review = "$science-review" in text or skill_dir.name == "science-review" +''' + new = ''' if skill_dir.name in CORE_SECTION_ALIASES: + # Core coordinator/provenance/reviewer skills collectively implement + # the handoff and are not required to invoke themselves by token. + has_provenance = True + has_review = True + else: + has_provenance = "$science-provenance" in text + has_review = "$science-review" in text +''' + if old in text: + text = text.replace(old, new, 1) + elif new not in text: + raise SystemExit("skill maturity core handoff marker is missing") + maturity.write_text(text, encoding="utf-8") + + +def main() -> int: + run_optional(ROOT / "scripts" / "integrate_native_runtime_v4.py") + run_optional(ROOT / "scripts" / "finalize_native_runtime_v4.py") + patch_core_source() + patch_compute_submission() + print("complete native runtime v4 source: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 335b5afa205100268d2f7852c2bf3cb569c33e78 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:17:39 +0900 Subject: [PATCH 45/87] Apply complete native runtime v4 implementation --- .../apply-complete-native-runtime-v4.yml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/apply-complete-native-runtime-v4.yml diff --git a/.github/workflows/apply-complete-native-runtime-v4.yml b/.github/workflows/apply-complete-native-runtime-v4.yml new file mode 100644 index 00000000..0dc6e89f --- /dev/null +++ b/.github/workflows/apply-complete-native-runtime-v4.yml @@ -0,0 +1,67 @@ +name: Apply complete native runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: apply-complete-native-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + apply: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Apply source and reference integration + run: uv run python scripts/apply_complete_native_runtime_v4.py + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile runtime + run: uv run python -m compileall -q src scripts + - name: Run focused runtime tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove all one-time runtime resources + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + - name: Commit clean implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Complete native artifact, project, compute, and skill maturity runtimes" + git push origin HEAD:"$branch" From 3df146c8e9c32b10adb437c88c091493e0dd5566 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:19:38 +0900 Subject: [PATCH 46/87] Add native runtime quickstart --- docs/NATIVE_RUNTIME_QUICKSTART.md | 119 ++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 docs/NATIVE_RUNTIME_QUICKSTART.md diff --git a/docs/NATIVE_RUNTIME_QUICKSTART.md b/docs/NATIVE_RUNTIME_QUICKSTART.md new file mode 100644 index 00000000..bcf748f8 --- /dev/null +++ b/docs/NATIVE_RUNTIME_QUICKSTART.md @@ -0,0 +1,119 @@ +# Native runtime quickstart + +Codex Science 0.4 uses progressive references: keep the decision flow in `SKILL.md`, then load the exact command/schema reference only when the route is selected. Do not guess a command or output field from a skill summary. + +## 1. Inspect and annotate an artifact + +Read first: + +```text +skills/science-provenance/references/artifact-runtime.md +``` + +Then: + +```bash +python scripts/render_artifact_runtime.py describe \ + artifacts/run/receptor.pdb \ + --artifact-path receptor.pdb \ + --sha256 \ + --kind receptor-structure \ + --artifact-type file \ + --media-type chemical/x-pdb \ + --max-bytes 1048576 \ + --max-records 200 \ + --output artifacts/run/receptor.runtime.json + +python scripts/render_artifact_runtime.py render \ + artifacts/run/receptor.runtime.json \ + --output artifacts/run/receptor.runtime.html + +python scripts/render_artifact_runtime.py select \ + artifacts/run/receptor.runtime.json \ + --selector-type residue \ + --selector '{"chain":"A","residue_number":145}' \ + --selected-by reviewer \ + --reason "Inspect receptor-state consistency." \ + --output artifacts/run/residue-A145.selection.json + +python scripts/render_artifact_runtime.py propose \ + artifacts/run/residue-A145.selection.json \ + --operation exclude-alternate-conformation \ + --parameters '{"altloc":"B"}' \ + --reason "Use one receptor microstate before docking." \ + --affected-step receptor-preparation \ + --affected-step docking \ + --expected-output prepared/receptor.pdbqt \ + --proposed-by analyst \ + --output artifacts/run/exclude-A145.proposal.json +``` + +The proposal does not edit or rerun anything. Feed it into impact analysis, approval, execution, diff, and re-review. + +## 2. Manage hypothesis branches + +Read first: + +```text +skills/codex-science/references/project-evidence-store.md +``` + +Then: + +```bash +python scripts/science_project.py --database project/evidence.sqlite init \ + --project-id project-1 --title "Project" --question "Question" \ + --output project/created.json + +python scripts/science_project.py --database project/evidence.sqlite import-run \ + --project-id project-1 --manifest artifacts/run-001/manifest.json \ + --branch main --output project/import-001.json + +python scripts/science_project.py --database project/evidence.sqlite fork \ + --project-id project-1 --source-run run-001 \ + --branch receptor-state-inactive --output project/fork.json +``` + +Import each later branch run as a new immutable run. Use `compare` and `merge-plan`; never overwrite or auto-merge scientific claims. + +## 3. Execute a durable job + +Read first: + +```text +authored-skills/remote-scientific-compute/references/job-runtime.md +``` + +Then: + +```bash +python scripts/science_job.py --state-dir artifacts/run/jobs preflight \ + --spec artifacts/run/job-spec.json --output artifacts/run/preflight.json + +python scripts/science_job.py --state-dir artifacts/run/jobs submit \ + --spec artifacts/run/job-spec.json --output artifacts/run/submitted.json + +python scripts/science_job.py --state-dir artifacts/run/jobs wait \ + --backend local --job-id --timeout 300 --poll 0.2 \ + --output artifacts/run/terminal.json + +python scripts/science_job.py --state-dir artifacts/run/jobs collect \ + --backend local --job-id --output artifacts/run/collected.json +``` + +For Slurm, render the script and create an approval receipt before `submit --approval`. + +## 4. Audit skill maturity + +```bash +python scripts/audit_native_skill_maturity.py \ + --output /tmp/native-skill-quality.json \ + --markdown /tmp/native-skill-quality.md \ + --require-clean +``` + +Maturity is computed from actual instructions, progressive references, declared output schemas, checked fixtures, seeded failures, and tests. `quality.json` is a declaration, not an automatic promotion. + +## Completion boundary + +These runtime commands improve inspection, lineage, execution, and auditability. Scientific completion still requires a prespecified decision contract, valid domain methods, material evidence, uncertainty, and independent review. From 430caf4cce43db803f64b71a41878fa1855910c7 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:25:50 +0900 Subject: [PATCH 47/87] Add final fail-closed runtime repairs --- scripts/repair_native_runtime_v4.py | 196 ++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 scripts/repair_native_runtime_v4.py diff --git a/scripts/repair_native_runtime_v4.py b/scripts/repair_native_runtime_v4.py new file mode 100644 index 00000000..83623323 --- /dev/null +++ b/scripts/repair_native_runtime_v4.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +"""Final idempotent repairs for native runtime v4 before repository validation.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def run_optional(name: str) -> None: + path = ROOT / "scripts" / name + if path.is_file() and path.name != Path(__file__).name: + subprocess.run([sys.executable, str(path)], cwd=ROOT, check=True) + + +def replace(path: Path, old: str, new: str, label: str) -> None: + text = path.read_text(encoding="utf-8") + if new in text: + return + if old not in text: + raise SystemExit(f"missing {label} marker: {path}") + path.write_text(text.replace(old, new, 1), encoding="utf-8") + + +def patch_skill_maturity() -> None: + path = ROOT / "src" / "codex_science" / "skill_maturity.py" + text = path.read_text(encoding="utf-8") + # Use an unambiguous string literal containing both quote characters. + text = text.replace(".strip('\\\"\\\\\'')", '.strip("\\\"\'")') + text = text.replace(".strip('\\\"\\\'')", '.strip("\\\"\'")') + text = text.replace(".strip('\\\"\'')", '.strip("\\\"\'")') + if 'strip("\\\"\'")' not in text: + old_line_start = ' return {item.group("key"):' + for line in text.splitlines(): + if line.startswith(old_line_start): + text = text.replace(line, ' return {item.group("key"): item.group("value").strip().strip("\\\"\'") for item in FIELD_RE.finditer(match.group("body"))}', 1) + break + text = text.replace( + ' entries = payload.get("references")\n', + ' entries = payload.get("references", payload.get("entries"))\n', + ) + old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" + has_review = "$science-review" in text or skill_dir.name == "science-review" +''' + new = ''' if skill_dir.name in CORE_SECTION_ALIASES: + # Core coordinator/provenance/reviewer skills collectively implement + # the handoff and are not required to invoke themselves by token. + has_provenance = True + has_review = True + else: + has_provenance = "$science-provenance" in text + has_review = "$science-review" in text +''' + if old in text: + text = text.replace(old, new, 1) + path.write_text(text, encoding="utf-8") + + +def patch_remote_sections() -> None: + path = ROOT / "authored-skills" / "remote-scientific-compute" / "SKILL.md" + text = path.read_text(encoding="utf-8") + additions: list[str] = [] + if "## Decision contract" not in text: + additions.append( + "## Decision contract\n\n" + "Before execution, record the target, data-transfer boundary, command or workflow, input hashes, runtime or container, requested resources, wall-time and cost caps, cancellation route, checkpoint policy, expected outputs, and scientific acceptance criteria. Remote writes or allocation require one explicit approval receipt covering the exact job spec." + ) + if "## Outputs" not in text: + additions.append( + "## Outputs\n\n" + "Save the job spec, preflight and approval receipts, scheduler or local job ID, state transitions, stdout and stderr hashes, resource use when reported, checkpoint references, cancellation or failure class, collected output hashes, and the downstream claim/review handoff with `$science-provenance`." + ) + if "## Boundaries" not in text: + additions.append( + "## Boundaries\n\n" + "Do not discover credentials by probing, submit to unapproved accounts or partitions, stage sensitive data without an approved route, busy-poll a scheduler, hide failed attempts, or treat a zero exit code as scientific success. Run `$science-review` after material outputs are collected." + ) + if additions: + path.write_text(text.rstrip() + "\n\n" + "\n\n".join(additions) + "\n", encoding="utf-8") + + +def patch_compute_worker() -> None: + path = ROOT / "src" / "codex_science" / "compute_backends.py" + text = path.read_text(encoding="utf-8") + old = ''' process = subprocess.Popen( + [sys.executable, str(Path(__file__).resolve()), "_worker", str(job_dir)], + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True, + close_fds=True, + ) +''' + new = ''' worker_environment = os.environ.copy() + source_root = str(Path(__file__).resolve().parents[1]) + existing_pythonpath = worker_environment.get("PYTHONPATH", "") + worker_environment["PYTHONPATH"] = source_root + ( + os.pathsep + existing_pythonpath if existing_pythonpath else "" + ) + process = subprocess.Popen( + [sys.executable, "-m", "codex_science.compute_backends", "_worker", str(job_dir)], + env=worker_environment, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True, + close_fds=True, + ) +''' + if old in text: + text = text.replace(old, new, 1) + # Replace any earlier race-safe tail with a deterministic submitted receipt. + class_start = text.index("class LocalBackend:") + submit_start = text.index(" def submit(", class_start) + process_start = text.index(" process = subprocess.Popen(", submit_start) + status_start = text.index("\n def status(", process_start) + section = text[process_start:status_start] + if "return submitted_receipt" not in section: + candidates = [index for token in ("\n current = _read_json", "\n state = _state_material") if (index := section.find(token)) >= 0] + if not candidates: + raise SystemExit("cannot locate LocalBackend submission receipt tail") + tail_start = min(candidates) + replacement = ''' + current = _read_json(job_dir / "state.json") + submitted_receipt = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + worker_pid=process.pid, + message="local worker started", + ) + if current.get("state") == "submitted": + _atomic_json(job_dir / "state.json", submitted_receipt) + return submitted_receipt +''' + section = section[:tail_start] + replacement + text = text[:process_start] + section + text[status_start:] + path.write_text(text, encoding="utf-8") + + +def patch_advanced_sidecars() -> None: + path = ROOT / "src" / "codex_science" / "advanced_sidecars.py" + if not path.is_file(): + return + text = path.read_text(encoding="utf-8") + text = text.replace( + ' selections_by_id[str(selection["selection_id"])] = selection\n', + ' selections_by_id[str(selection["selection_id"])] = payload\n', + ) + path.write_text(text, encoding="utf-8") + + +def patch_project_branch_base() -> None: + path = ROOT / "src" / "codex_science" / "project_store.py" + text = path.read_text(encoding="utf-8") + old = ''' if branch is None: + connection.execute( + "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", + (project_id, branch_name, run_id, run_id, "active", imported_at, imported_at), + ) +''' + new = ''' if branch is None: + branch_base = parent_run_id or run_id + connection.execute( + "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", + (project_id, branch_name, branch_base, run_id, "active", imported_at, imported_at), + ) +''' + if old in text: + text = text.replace(old, new, 1) + path.write_text(text, encoding="utf-8") + + +def main() -> int: + for name in ( + "integrate_native_runtime_v4.py", + "finalize_native_runtime_v4.py", + "apply_complete_native_runtime_v4.py", + ): + run_optional(name) + patch_skill_maturity() + patch_remote_sections() + patch_compute_worker() + patch_advanced_sidecars() + patch_project_branch_base() + subprocess.run([sys.executable, "-m", "compileall", "-q", "src", "scripts"], cwd=ROOT, check=True) + print("native runtime v4 repairs: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From b5121a4249758ca9d9524a8c293ae8f38077fa18 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:26:52 +0900 Subject: [PATCH 48/87] Add function-boundary native runtime sealing script --- scripts/seal_native_runtime_v4.py | 227 ++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 scripts/seal_native_runtime_v4.py diff --git a/scripts/seal_native_runtime_v4.py b/scripts/seal_native_runtime_v4.py new file mode 100644 index 00000000..6ae42534 --- /dev/null +++ b/scripts/seal_native_runtime_v4.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +"""Seal native runtime v4 using function-boundary rewrites, then compile it.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def run_optional(name: str) -> None: + path = ROOT / "scripts" / name + if path.is_file() and path.name != Path(__file__).name: + subprocess.run([sys.executable, str(path)], cwd=ROOT, check=True) + + +def replace_region(path: Path, start_marker: str, end_marker: str, replacement: str) -> None: + text = path.read_text(encoding="utf-8") + start = text.index(start_marker) + end = text.index(end_marker, start) + current = text[start:end] + if current == replacement: + return + path.write_text(text[:start] + replacement + text[end:], encoding="utf-8") + + +def patch_artifact_runtime() -> None: + path = ROOT / "src" / "codex_science" / "artifact_runtime.py" + text = path.read_text(encoding="utf-8") + old = " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n" + new = " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n" + if old in text: + path.write_text(text.replace(old, new, 1), encoding="utf-8") + elif new not in text: + raise SystemExit("artifact descriptor construction marker is missing") + + +def patch_project_cli() -> None: + path = ROOT / "scripts" / "science_project.py" + text = path.read_text(encoding="utf-8") + if "import sqlite3\n" not in text: + text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) + text = text.replace( + " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", + " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", + ) + path.write_text(text, encoding="utf-8") + + +def patch_skill_maturity() -> None: + path = ROOT / "src" / "codex_science" / "skill_maturity.py" + frontmatter = '''def _frontmatter(text: str) -> dict[str, str]: + match = FRONTMATTER_RE.match(text) + if match is None: + return {} + result: dict[str, str] = {} + for item in FIELD_RE.finditer(match.group("body")): + value = item.group("value").strip() + if len(value) >= 2 and value[0] == value[-1] and value[0] in {chr(34), chr(39)}: + value = value[1:-1] + result[item.group("key")] = value + return result +''' + replace_region(path, "def _frontmatter", "\n\ndef _load_json", frontmatter) + text = path.read_text(encoding="utf-8") + text = text.replace( + ' entries = payload.get("references")\n', + ' entries = payload.get("references", payload.get("entries"))\n', + ) + old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" + has_review = "$science-review" in text or skill_dir.name == "science-review" +''' + new = ''' if skill_dir.name in CORE_SECTION_ALIASES: + # Core coordinator/provenance/reviewer skills collectively implement + # the handoff and are not required to invoke themselves by token. + has_provenance = True + has_review = True + else: + has_provenance = "$science-provenance" in text + has_review = "$science-review" in text +''' + if old in text: + text = text.replace(old, new, 1) + elif new not in text: + raise SystemExit("skill maturity handoff marker is missing") + path.write_text(text, encoding="utf-8") + + +def patch_remote_skill() -> None: + path = ROOT / "authored-skills" / "remote-scientific-compute" / "SKILL.md" + text = path.read_text(encoding="utf-8") + sections: list[str] = [] + if "## Decision contract" not in text: + sections.append( + "## Decision contract\n\n" + "Before execution, record the target, transfer boundary, argv or workflow, input hashes, runtime or container, resource and wall-time caps, cost cap, checkpoint and cancellation routes, expected outputs, and scientific acceptance criteria. Remote writes or allocation require one explicit approval receipt covering the exact job spec." + ) + if "## Outputs" not in text: + sections.append( + "## Outputs\n\n" + "Save the job spec, preflight and approval receipts, job ID and state transitions, stdout and stderr hashes, failure class, resource use when reported, checkpoint references, collected output hashes, and the `$science-provenance` and `$science-review` handoff." + ) + if "## Boundaries" not in text: + sections.append( + "## Boundaries\n\n" + "Do not probe for credentials, submit to unapproved accounts or partitions, stage sensitive data without an approved route, busy-poll, hide failed attempts, or treat process exit zero as scientific success." + ) + if sections: + path.write_text(text.rstrip() + "\n\n" + "\n\n".join(sections) + "\n", encoding="utf-8") + + +def patch_local_submit() -> None: + path = ROOT / "src" / "codex_science" / "compute_backends.py" + replacement = ''' def submit(self, spec: JobSpec, *, approval: Mapping[str, Any] | None = None) -> dict[str, Any]: + if spec.backend != "local": + raise ValueError("LocalBackend requires backend=local") + preflight = self.preflight(spec) + if not preflight["ready"]: + raise ValueError("local preflight failed") + if spec.approval_required: + if approval is None: + raise ValueError("job spec requires explicit approval") + validate_approval_receipt(approval, spec) + job_id = "local-" + uuid.uuid4().hex[:20] + job_dir = self._job_dir(job_id) + job_dir.mkdir(parents=True) + _atomic_json(job_dir / "spec.json", spec.to_dict()) + submitted_at = _now() + initial = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + ) + _atomic_json(job_dir / "state.json", initial) + worker_environment = os.environ.copy() + source_root = str(Path(__file__).resolve().parents[1]) + existing_pythonpath = worker_environment.get("PYTHONPATH", "") + worker_environment["PYTHONPATH"] = source_root + ( + os.pathsep + existing_pythonpath if existing_pythonpath else "" + ) + process = subprocess.Popen( + [ + sys.executable, + "-m", + "codex_science.compute_backends", + "_worker", + str(job_dir), + ], + env=worker_environment, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True, + close_fds=True, + ) + current = _read_json(job_dir / "state.json") + submitted_receipt = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + worker_pid=process.pid, + message="local worker started", + ) + if current.get("state") == "submitted": + _atomic_json(job_dir / "state.json", submitted_receipt) + return submitted_receipt +''' + text = path.read_text(encoding="utf-8") + local_start = text.index("class LocalBackend:") + submit_start = text.index(" def submit(", local_start) + status_start = text.index("\n def status(", submit_start) + path.write_text(text[:submit_start] + replacement + text[status_start:], encoding="utf-8") + + +def patch_advanced_sidecars() -> None: + path = ROOT / "src" / "codex_science" / "advanced_sidecars.py" + if not path.is_file(): + return + text = path.read_text(encoding="utf-8") + text = text.replace( + ' selections_by_id[str(selection["selection_id"])] = selection\n', + ' selections_by_id[str(selection["selection_id"])] = payload\n', + ) + path.write_text(text, encoding="utf-8") + + +def patch_project_branch_base() -> None: + path = ROOT / "src" / "codex_science" / "project_store.py" + text = path.read_text(encoding="utf-8") + old = ''' if branch is None: + connection.execute( + "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", + (project_id, branch_name, run_id, run_id, "active", imported_at, imported_at), + ) +''' + new = ''' if branch is None: + branch_base = parent_run_id or run_id + connection.execute( + "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", + (project_id, branch_name, branch_base, run_id, "active", imported_at, imported_at), + ) +''' + if old in text: + text = text.replace(old, new, 1) + path.write_text(text, encoding="utf-8") + + +def main() -> int: + run_optional("integrate_native_runtime_v4.py") + patch_artifact_runtime() + patch_project_cli() + patch_skill_maturity() + patch_remote_skill() + patch_local_submit() + patch_advanced_sidecars() + patch_project_branch_base() + subprocess.run([sys.executable, "-m", "compileall", "-q", "src", "scripts"], cwd=ROOT, check=True) + print("native runtime v4 sealed: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From d949215b82b9c473371917ef17f8b7613fbf3d20 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:27:17 +0900 Subject: [PATCH 49/87] Seal and validate native runtime v4 --- .github/workflows/seal-native-runtime-v4.yml | 72 ++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/workflows/seal-native-runtime-v4.yml diff --git a/.github/workflows/seal-native-runtime-v4.yml b/.github/workflows/seal-native-runtime-v4.yml new file mode 100644 index 00000000..58ef4403 --- /dev/null +++ b/.github/workflows/seal-native-runtime-v4.yml @@ -0,0 +1,72 @@ +name: Seal native runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: seal-native-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + seal: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Seal source and progressive references + run: uv run python scripts/seal_native_runtime_v4.py + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile runtime + run: uv run python -m compileall -q src scripts + - name: Run focused runtime tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove one-time runtime resources + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f .github/workflows/seal-native-runtime-v4.yml + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + rm -f scripts/repair_native_runtime_v4.py + rm -f scripts/seal_native_runtime_v4.py + - name: Commit clean implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Seal native artifact, project, compute, and skill maturity runtimes" + git fetch origin "$branch" + git rebase "origin/$branch" + git push origin HEAD:"$branch" From c04bd99aca289f967f13f07b93997ca43e39b89c Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:29:45 +0900 Subject: [PATCH 50/87] Add reviewed-run to skill-draft compiler --- src/codex_science/pipeline_compiler.py | 386 +++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 src/codex_science/pipeline_compiler.py diff --git a/src/codex_science/pipeline_compiler.py b/src/codex_science/pipeline_compiler.py new file mode 100644 index 00000000..418090ed --- /dev/null +++ b/src/codex_science/pipeline_compiler.py @@ -0,0 +1,386 @@ +"""Promote a passed scientific run into a reviewable skill draft. + +The compiler creates a draft only. It never modifies the audited catalog, +activates a skill, copies private inputs, or claims that one successful run is a +validated reusable method. +""" + +from __future__ import annotations + +import hashlib +import json +import re +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Mapping + +from codex_science.artifact_store import stream_sha256 +from codex_science.artifacts import validate_bundle +from codex_science.review import review_manifest + + +SKILL_NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$") + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _fingerprint(value: Mapping[str, Any]) -> str: + return hashlib.sha256(_canonical_bytes(value)).hexdigest() + + +def _text(value: Any, label: str) -> str: + text = str(value).strip() + if not text: + raise ValueError(f"{label} is required") + return text + + +def _write_json(path: Path, payload: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + + +def _artifact_summary(manifest: Mapping[str, Any]) -> list[dict[str, Any]]: + return [ + { + "path": str(item.get("path", "")), + "kind": str(item.get("kind", "artifact")), + "sha256": str(item.get("sha256", "")), + "artifact_type": str(item.get("artifact_type", "file")), + } + for item in manifest.get("artifacts", []) + if isinstance(item, Mapping) + ] + + +def _schema_inputs(manifest: Mapping[str, Any]) -> dict[str, Any]: + return { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "input.schema.json", + "title": "Reusable scientific pipeline input", + "type": "object", + "properties": { + "question": {"type": "string", "minLength": 1}, + "parameters": {"type": "object", "additionalProperties": True}, + "inputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": {"type": "string", "minLength": 1}, + "path": {"type": "string"}, + "identifier": {"type": "string"}, + "sha256": {"type": "string", "pattern": "^[0-9a-fA-F]{64}$"}, + "source": {"type": "string"}, + "release": {"type": "string"} + }, + "required": ["id"], + "anyOf": [{"required": ["path"]}, {"required": ["identifier"]}], + "additionalProperties": True + } + }, + "acceptance": { + "type": "object", + "properties": { + "criteria": {"type": "array", "items": {"type": "object"}}, + "review_required": {"const": True} + }, + "required": ["criteria", "review_required"], + "additionalProperties": False + } + }, + "required": ["question", "parameters", "inputs", "acceptance"], + "additionalProperties": False, + "x-source-run-inputs": list(manifest.get("inputs", [])) + } + + +def _schema_outputs() -> dict[str, Any]: + return { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "output.schema.json", + "title": "Reusable scientific pipeline output", + "type": "object", + "properties": { + "run_id": {"type": "string", "minLength": 1}, + "manifest_path": {"type": "string", "minLength": 1}, + "manifest_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "claims": {"type": "array", "items": {"type": "object"}}, + "artifacts": {"type": "array", "items": {"type": "object"}}, + "review": { + "type": "object", + "properties": {"status": {"const": "passed"}}, + "required": ["status"], + "additionalProperties": True + }, + "limitations": {"type": "array", "items": {"type": "string"}} + }, + "required": ["run_id", "manifest_path", "manifest_sha256", "claims", "artifacts", "review", "limitations"], + "additionalProperties": False + } + + +def _skill_text(name: str, description: str, source_run: str, workflow_steps: list[dict[str, Any]]) -> str: + step_lines = [] + for index, step in enumerate(workflow_steps, 1): + step_id = str(step.get("id", f"step-{index}")) + detail = str(step.get("description", step_id)).strip() + step_lines.append(f"{index}. `{step_id}` — {detail}") + steps = "\n".join(step_lines) or "1. Reconstruct the approved workflow from a new decision contract." + return f'''--- +name: {name} +description: "{description.replace('"', "'")}" +--- + +# {name} + +> Draft generated from reviewed run `{source_run}`. It is not active, cataloged, or validated for new inputs until its fixtures, seeded failures, environment, and independent review pass. + +## Decision contract + +Record the new question, intended decision, population or system, input identities and hashes, parameter boundaries, primary acceptance criteria, uncertainty rule, non-goals, model/source revisions, cost and approval boundaries, and what would falsify each material claim. Do not inherit the source run's conclusion. + +## Reference usage + +Before executing, **MUST** read [the generated pipeline contract](references/pipeline.md). Do not guess commands, input fields, environment, or output semantics from this summary. The source run is precedent for structure only, not evidence for a new run. + +## Workflow + +{steps} + +Create a new run ID, initialize provenance, execute only after required approvals, preserve failed attempts and null results, and run `$science-review` against the new artifacts. Use `$science-provenance` for every command, environment, input, output, claim, and receipt. + +## Outputs + +Return a new hash-validated artifact manifest satisfying `output.schema.json`, with explicit claims, uncertainty, limitations, model/source revisions, execution logs, and an independent review receipt when independence is available. Never return the source run as the new output. + +## Boundaries + +- Do not activate or catalog this draft automatically. +- Do not copy private, sensitive, proprietary, or credential-bearing source inputs into the draft. +- Do not assume the source environment, model, database, or endpoint is still available. +- Do not reuse a source review receipt for changed inputs or code. +- Do not claim generalization from one successful run. +- Do not execute destructive, remote, paid, or write-capable operations without the normal approval gate. +''' + + +def _reference_text( + *, + name: str, + source_manifest_path: Path, + source_manifest_sha256: str, + manifest: Mapping[str, Any], + command_contract: list[list[str]], + limitations: list[str], +) -> str: + commands = "\n".join(f"- `{json.dumps(command, ensure_ascii=False)}`" for command in command_contract) or "- No command was promoted. Reconstruct commands from an approved method before validation." + steps = "\n".join( + f"- `{step.get('id', '')}` — {step.get('description', step.get('id', ''))}" + for step in manifest.get("plan", []) + if isinstance(step, Mapping) + ) or "- No source plan steps were recorded." + limitations_text = "\n".join(f"- {item}" for item in limitations) + return f'''# Generated pipeline contract + +This reference was generated from a reviewed run and is a **draft implementation contract**, not scientific evidence and not an activation decision. + +## Source record + +- source run: `{manifest.get('run_id')}` +- source manifest: `{source_manifest_path}` +- source manifest SHA-256: `{source_manifest_sha256}` +- source review status: `{manifest.get('review', {{}}).get('status', 'unknown')}` +- generated skill: `{name}` + +The source manifest path may be local-machine-specific. The SHA-256 is the stable identity. Never use this reference to bypass source bundle validation. + +## Inputs + +Use `input.schema.json`. Each new input needs an ID plus either a local path or canonical external identifier. Record a checksum when practical, source/release for external data, and all transformations. Do not reuse source paths blindly. + +## Environment + +Reconstruct and review the runtime, lockfile/container, code revision, model and weight revisions, databases, hardware, seed, determinism settings, and material non-secret environment variables. The source environment was: + +```json +{json.dumps(manifest.get('environment', {{}}), indent=2, sort_keys=True, ensure_ascii=False)} +``` + +## Source workflow shape + +{steps} + +## Command contract + +Commands are represented as argv arrays. They are not executed by reading this file. + +{commands} + +Before using a promoted command: + +1. resolve every executable and file path; +2. replace source-run paths with new run inputs; +3. pin or record model, database, and code revisions; +4. run the smallest smoke test; +5. verify exit status and output schema; +6. preserve logs and failures; +7. evaluate scientific acceptance separately from process success. + +## Output contract + +Use `output.schema.json`. The new run must contain a different run ID and manifest hash, current claims and limitations, current artifact hashes, and a current passed review. A process receipt alone is not an output contract. + +## Validation and promotion + +The generated draft starts below active maturity. To promote it: + +1. add a redistributable acceptance fixture; +2. seed material failure cases; +3. add deterministic tests; +4. record exact source/model/database drift behavior; +5. run independent review; +6. add `quality.json` with an honest maturity declaration; +7. pass the repository skill, reference, inventory, wrapper, and contract gates; +8. activate it only through normal catalog policy. + +## Failure handling + +| Failure | Required response | +| --- | --- | +| source manifest changed | stop; restore or identify the reviewed source bytes | +| command contains unavailable paths | revise the draft and test in the approved environment | +| model/database revision unavailable | select a reviewed replacement and create a new acceptance result | +| output schema mismatch | keep the failed run and correct code/schema before retry | +| source result does not reproduce | report non-reproduction; do not lower the threshold post hoc | +| private input referenced | replace it with an approved immutable reference or remove the dependency | +| new operation requires approval | stop at the approval gate | + +## Common mistakes + +- Treating a generated draft as active. +- Copying a source conclusion into the new question. +- Reusing source review receipts. +- Reconstructing commands from prose while ignoring argv and environment details. +- Omitting failed source steps because the final run passed. +- Claiming generalization from a single fixture. + +## Known limitations + +{limitations_text} + +## Evidence boundary + +This reference preserves workflow shape and explicit source lineage. It does not validate new inputs, authenticate the producer, guarantee reproducibility, or establish scientific truth. +''' + + +def compile_pipeline_draft( + *, + manifest_path: Path, + output_dir: Path, + name: str, + description: str, + command_contract: list[list[str]] | None = None, + limitations: list[str] | None = None, + generated_at: str | None = None, +) -> dict[str, Any]: + name = _text(name, "name") + if not SKILL_NAME_RE.fullmatch(name): + raise ValueError("name must be lowercase kebab-case") + description = _text(description, "description") + manifest_path = manifest_path.resolve() + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + if not isinstance(manifest, dict): + raise ValueError("manifest must be an object") + sidecars = validate_bundle(manifest, manifest_path.parent) + review = review_manifest(manifest, manifest_path.parent, sidecars=sidecars) + if manifest.get("review", {}).get("status") != "passed" or review.get("status") != "passed": + raise ValueError("only a currently passed run may be promoted to a skill draft") + if any(step.get("status") != "completed" for step in manifest.get("plan", []) if isinstance(step, Mapping)): + raise ValueError("source run has incomplete plan steps") + output_dir = output_dir.resolve() + if output_dir.exists() and any(output_dir.iterdir()): + raise ValueError(f"output directory is not empty: {output_dir}") + output_dir.mkdir(parents=True, exist_ok=True) + references = output_dir / "references" + references.mkdir() + manifest_sha, _size = stream_sha256(manifest_path) + commands = command_contract or [] + for index, command in enumerate(commands): + if not isinstance(command, list) or not command or not all(isinstance(item, str) and item for item in command): + raise ValueError(f"command_contract[{index}] must be a non-empty argv list") + limit_values = limitations or [ + "Generated from one reviewed run and not validated for a new input distribution.", + "External sources, models, weights, databases, and endpoints may drift.", + "The draft contains no automatic activation or remote/write permission." + ] + (output_dir / "SKILL.md").write_text( + _skill_text(name, description, str(manifest.get("run_id", "unknown")), [dict(item) for item in manifest.get("plan", []) if isinstance(item, Mapping)]), + encoding="utf-8", + ) + _write_json(output_dir / "input.schema.json", _schema_inputs(manifest)) + _write_json(output_dir / "output.schema.json", _schema_outputs()) + (references / "pipeline.md").write_text( + _reference_text( + name=name, + source_manifest_path=manifest_path, + source_manifest_sha256=manifest_sha, + manifest=manifest, + command_contract=commands, + limitations=limit_values, + ), + encoding="utf-8", + ) + _write_json( + references / "index.json", + { + "schema_version": 1, + "skill": name, + "references": [ + { + "id": "pipeline", + "path": "references/pipeline.md", + "purpose": "Exact generated workflow shape, input/output schemas, promotion path, failure handling, and source lineage.", + "read_when": ["before using the generated draft", "before reconstructing commands", "before validating or promoting the draft"], + "required_before": ["executing any promoted command", "declaring the draft reusable", "adding it to an audited catalog"], + "search_patterns": ["## Command contract", "## Output contract", "## Validation and promotion", "## Failure handling"], + "authority": "Generated from a hash-validated Codex Science run", + "version": "1", + "evidence_boundary": "The generated reference preserves workflow structure but is not evidence for a new run." + } + ] + }, + ) + promotion_material = { + "schema_version": 1, + "status": "draft", + "activated": False, + "cataloged": False, + "generated_at": generated_at or _now(), + "skill": name, + "source_run_id": manifest.get("run_id"), + "source_manifest_path": str(manifest_path), + "source_manifest_sha256": manifest_sha, + "source_review_status": manifest.get("review", {}).get("status"), + "source_artifacts": _artifact_summary(manifest), + "command_count": len(commands), + "limitations": limit_values, + "required_next_steps": [ + "add redistributable acceptance fixture", + "seed material failures", + "add deterministic tests", + "run independent review", + "declare honest maturity", + "pass catalog activation policy" + ] + } + promotion = {**promotion_material, "fingerprint": _fingerprint(promotion_material)} + _write_json(output_dir / "promotion.json", promotion) + return promotion From f2ee2eba89c59cdb29232563d51bce8deac30fef Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:29:57 +0900 Subject: [PATCH 51/87] Add reviewed run promotion CLI --- scripts/promote_run_to_skill.py | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 scripts/promote_run_to_skill.py diff --git a/scripts/promote_run_to_skill.py b/scripts/promote_run_to_skill.py new file mode 100644 index 00000000..a2e9bd76 --- /dev/null +++ b/scripts/promote_run_to_skill.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""Generate a non-active skill draft from a currently passed scientific run.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.pipeline_compiler import compile_pipeline_draft # noqa: E402 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("manifest", type=Path) + parser.add_argument("output", type=Path) + parser.add_argument("--name", required=True) + parser.add_argument("--description", required=True) + parser.add_argument("--commands", type=Path, help="JSON array of argv arrays") + parser.add_argument("--limitation", action="append", default=[]) + parser.add_argument("--receipt", type=Path, required=True) + args = parser.parse_args() + try: + commands = None + if args.commands: + payload = json.loads(args.commands.read_text(encoding="utf-8")) + if not isinstance(payload, list): + raise ValueError("--commands must contain a JSON array") + commands = payload + receipt = compile_pipeline_draft( + manifest_path=args.manifest, + output_dir=args.output, + name=args.name, + description=args.description, + command_contract=commands, + limitations=args.limitation or None, + ) + args.receipt.parent.mkdir(parents=True, exist_ok=True) + args.receipt.write_text(json.dumps(receipt, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + except (OSError, ValueError, json.JSONDecodeError) as error: + print(f"error: {error}", file=sys.stderr) + return 1 + print(f"Success! Draft written to: {args.output.resolve()}") + print(f"Success! Receipt written to: {args.receipt.resolve()}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From b8ddf5eb8aa8399a1b70e0f7abefe1fe415595d1 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:30:50 +0900 Subject: [PATCH 52/87] Add deterministic next-experiment planner --- src/codex_science/experiment_planner.py | 305 ++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 src/codex_science/experiment_planner.py diff --git a/src/codex_science/experiment_planner.py b/src/codex_science/experiment_planner.py new file mode 100644 index 00000000..5bb609e7 --- /dev/null +++ b/src/codex_science/experiment_planner.py @@ -0,0 +1,305 @@ +"""Deterministic, non-executing multi-objective next-experiment planning. + +The planner ranks only declared candidate properties. It does not infer chemical +similarity, biological validity, or calibrated information gain from a model +name. Controls and scientific constraints remain explicit inputs. +""" + +from __future__ import annotations + +import hashlib +import json +from collections import Counter, defaultdict +from datetime import datetime, timezone +from typing import Any, Iterable, Mapping + + +DIRECTIONS = {"maximize", "minimize"} + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _fingerprint(value: Mapping[str, Any]) -> str: + return hashlib.sha256(_canonical_bytes(value)).hexdigest() + + +def _text(value: Any, label: str) -> str: + text = str(value).strip() + if not text: + raise ValueError(f"{label} is required") + return text + + +def _number(value: Any, label: str) -> float: + if isinstance(value, bool): + raise ValueError(f"{label} must be numeric") + try: + result = float(value) + except (TypeError, ValueError) as error: + raise ValueError(f"{label} must be numeric") from error + if result != result or result in {float("inf"), float("-inf")}: + raise ValueError(f"{label} must be finite") + return result + + +def _validate_objectives(raw: Any) -> list[dict[str, Any]]: + if not isinstance(raw, list) or not raw: + raise ValueError("objectives must be a non-empty list") + objectives: list[dict[str, Any]] = [] + names: set[str] = set() + for index, item in enumerate(raw): + if not isinstance(item, Mapping): + raise ValueError(f"objective {index} must be an object") + name = _text(item.get("name"), f"objective {index} name") + if name in names: + raise ValueError(f"duplicate objective: {name}") + names.add(name) + direction = _text(item.get("direction"), f"objective {name} direction") + if direction not in DIRECTIONS: + raise ValueError(f"invalid objective direction: {direction}") + weight = _number(item.get("weight", 1.0), f"objective {name} weight") + if weight < 0: + raise ValueError(f"objective {name} weight must be non-negative") + objectives.append({"name": name, "direction": direction, "weight": weight, "required": bool(item.get("required", True))}) + if not any(item["weight"] > 0 for item in objectives): + raise ValueError("at least one objective must have positive weight") + return objectives + + +def _validate_candidates(raw: Any, objectives: list[dict[str, Any]]) -> list[dict[str, Any]]: + if not isinstance(raw, list) or not raw: + raise ValueError("candidates must be a non-empty list") + required = {item["name"] for item in objectives if item["required"]} + result: list[dict[str, Any]] = [] + ids: set[str] = set() + for index, item in enumerate(raw): + if not isinstance(item, Mapping): + raise ValueError(f"candidate {index} must be an object") + candidate_id = _text(item.get("id"), f"candidate {index} id") + if candidate_id in ids: + raise ValueError(f"duplicate candidate ID: {candidate_id}") + ids.add(candidate_id) + properties_raw = item.get("properties") + if not isinstance(properties_raw, Mapping): + raise ValueError(f"candidate {candidate_id} properties must be an object") + missing = sorted(required - set(properties_raw)) + if missing: + raise ValueError(f"candidate {candidate_id} lacks required objectives: {', '.join(missing)}") + properties = {str(key): _number(value, f"candidate {candidate_id} property {key}") for key, value in properties_raw.items()} + cost = _number(item.get("cost", 0), f"candidate {candidate_id} cost") + uncertainty = _number(item.get("uncertainty", 0), f"candidate {candidate_id} uncertainty") + if cost < 0 or uncertainty < 0: + raise ValueError(f"candidate {candidate_id} cost and uncertainty must be non-negative") + result.append({ + "id": candidate_id, + "properties": properties, + "cost": cost, + "uncertainty": uncertainty, + "diversity_group": str(item.get("diversity_group", item.get("scaffold_id", candidate_id))), + "control": bool(item.get("control", False)), + "eligible": bool(item.get("eligible", True)), + "exclusion_reason": str(item.get("exclusion_reason", "")), + "metadata": dict(item.get("metadata", {})) if isinstance(item.get("metadata", {}), Mapping) else {}, + }) + return result + + +def _normalize(candidates: list[dict[str, Any]], objectives: list[dict[str, Any]]) -> tuple[dict[str, dict[str, float]], dict[str, float]]: + normalized: dict[str, dict[str, float]] = {item["id"]: {} for item in candidates} + for objective in objectives: + name = objective["name"] + values = [candidate["properties"].get(name) for candidate in candidates if name in candidate["properties"]] + if not values: + continue + minimum, maximum = min(values), max(values) + for candidate in candidates: + if name not in candidate["properties"]: + normalized[candidate["id"]][name] = 0.0 + continue + value = candidate["properties"][name] + score = 1.0 if maximum == minimum else (value - minimum) / (maximum - minimum) + if objective["direction"] == "minimize": + score = 1.0 - score + normalized[candidate["id"]][name] = score + uncertainties = [candidate["uncertainty"] for candidate in candidates] + low, high = min(uncertainties), max(uncertainties) + uncertainty_scores = { + candidate["id"]: (1.0 if high == low and high > 0 else 0.0 if high == low else (candidate["uncertainty"] - low) / (high - low)) + for candidate in candidates + } + return normalized, uncertainty_scores + + +def _dominates(left: Mapping[str, float], right: Mapping[str, float], objective_names: Iterable[str]) -> bool: + values = [(left.get(name, 0.0), right.get(name, 0.0)) for name in objective_names] + return all(a >= b for a, b in values) and any(a > b for a, b in values) + + +def pareto_fronts(candidates: list[dict[str, Any]], normalized: Mapping[str, Mapping[str, float]], objectives: list[dict[str, Any]]) -> list[list[str]]: + remaining = {candidate["id"] for candidate in candidates} + names = [item["name"] for item in objectives if item["weight"] > 0] + fronts: list[list[str]] = [] + while remaining: + front = sorted( + candidate_id for candidate_id in remaining + if not any( + other != candidate_id and _dominates(normalized[other], normalized[candidate_id], names) + for other in remaining + ) + ) + if not front: + raise ValueError("could not construct Pareto front") + fronts.append(front) + remaining.difference_update(front) + return fronts + + +def plan_next_experiment(payload: Mapping[str, Any], *, created_at: str | None = None) -> dict[str, Any]: + if payload.get("schema_version", 1) != 1: + raise ValueError("unsupported next-experiment input schema") + decision = _text(payload.get("decision"), "decision") + objectives = _validate_objectives(payload.get("objectives")) + candidates = _validate_candidates(payload.get("candidates"), objectives) + constraints = payload.get("constraints") + if not isinstance(constraints, Mapping): + raise ValueError("constraints must be an object") + batch_size = int(constraints.get("batch_size", 0)) + budget = _number(constraints.get("budget", 0), "budget") + group_cap = int(constraints.get("diversity_group_cap", constraints.get("scaffold_cap", batch_size))) + minimum_controls = int(constraints.get("minimum_controls", 0)) + if batch_size < 1 or budget < 0 or group_cap < 1 or minimum_controls < 0: + raise ValueError("batch size, budget, diversity cap, and minimum controls are invalid") + uncertainty_weight = _number(payload.get("uncertainty_weight", 0.25), "uncertainty_weight") + diversity_bonus = _number(payload.get("diversity_bonus", 0.1), "diversity_bonus") + if uncertainty_weight < 0 or diversity_bonus < 0: + raise ValueError("uncertainty_weight and diversity_bonus must be non-negative") + claim_ids = sorted({_text(item, "claim_id") for item in payload.get("claim_ids", [])}) + + eligible = [item for item in candidates if item["eligible"]] + ineligible = [item for item in candidates if not item["eligible"]] + if not eligible: + raise ValueError("no eligible candidates") + normalized, uncertainty_scores = _normalize(eligible, objectives) + fronts = pareto_fronts(eligible, normalized, objectives) + front_rank = {candidate_id: rank for rank, front in enumerate(fronts) for candidate_id in front} + weight_total = sum(item["weight"] for item in objectives) + by_id = {item["id"]: item for item in candidates} + + def base_utility(candidate: Mapping[str, Any]) -> float: + objective_score = sum( + objective["weight"] * normalized[candidate["id"]].get(objective["name"], 0.0) + for objective in objectives + ) / weight_total + return objective_score + uncertainty_weight * uncertainty_scores[candidate["id"]] + + selected: list[dict[str, Any]] = [] + selected_ids: set[str] = set() + group_counts: Counter[str] = Counter() + total_cost = 0.0 + rejection_reasons: dict[str, list[str]] = defaultdict(list) + + controls = sorted((item for item in eligible if item["control"]), key=lambda item: (item["cost"], item["id"])) + if len(controls) < minimum_controls: + raise ValueError(f"minimum_controls={minimum_controls} but only {len(controls)} eligible controls exist") + for candidate in controls[:minimum_controls]: + if total_cost + candidate["cost"] > budget or len(selected) >= batch_size: + raise ValueError("required controls do not fit the batch or budget") + if group_counts[candidate["diversity_group"]] >= group_cap: + raise ValueError("required controls violate diversity_group_cap") + selected.append(candidate) + selected_ids.add(candidate["id"]) + group_counts[candidate["diversity_group"]] += 1 + total_cost += candidate["cost"] + + while len(selected) < batch_size: + scored: list[tuple[float, int, str, dict[str, Any]]] = [] + for candidate in eligible: + if candidate["id"] in selected_ids: + continue + if total_cost + candidate["cost"] > budget: + rejection_reasons[candidate["id"]].append("budget") + continue + if group_counts[candidate["diversity_group"]] >= group_cap: + rejection_reasons[candidate["id"]].append("diversity-group-cap") + continue + novelty = 1.0 if group_counts[candidate["diversity_group"]] == 0 else 0.0 + score = base_utility(candidate) + diversity_bonus * novelty - 0.01 * front_rank[candidate["id"]] + scored.append((score, -front_rank[candidate["id"]], candidate["id"], candidate)) + if not scored: + break + scored.sort(key=lambda item: (-item[0], item[1], item[2])) + candidate = scored[0][3] + selected.append(candidate) + selected_ids.add(candidate["id"]) + group_counts[candidate["diversity_group"]] += 1 + total_cost += candidate["cost"] + + selected_records = [] + for candidate in selected: + selected_records.append({ + **candidate, + "normalized_objectives": normalized[candidate["id"]], + "uncertainty_score": uncertainty_scores[candidate["id"]], + "pareto_front": front_rank[candidate["id"]], + "base_utility": base_utility(candidate), + }) + for candidate in ineligible: + rejection_reasons[candidate["id"]].append(candidate["exclusion_reason"] or "ineligible") + for candidate in eligible: + if candidate["id"] not in selected_ids and not rejection_reasons[candidate["id"]]: + rejection_reasons[candidate["id"]].append("not-selected-within-batch") + + expected_information_gain = sum(uncertainty_scores[item["id"]] for item in selected) / len(selected) if selected else 0.0 + material = { + "schema_version": 1, + "decision": decision, + "status": "proposed", + "executed": False, + "created_at": created_at or _now(), + "objectives": objectives, + "constraints": { + "batch_size": batch_size, + "budget": budget, + "diversity_group_cap": group_cap, + "minimum_controls": minimum_controls, + }, + "uncertainty_weight": uncertainty_weight, + "diversity_bonus": diversity_bonus, + "selected": selected_records, + "selected_count": len(selected_records), + "total_cost": total_cost, + "remaining_budget": budget - total_cost, + "diversity_groups": dict(sorted(group_counts.items())), + "pareto_fronts": fronts, + "expected_information_gain_proxy": expected_information_gain, + "rejected": [ + {"id": candidate_id, "reasons": sorted(set(reasons)), "candidate": by_id[candidate_id]} + for candidate_id, reasons in sorted(rejection_reasons.items()) + ], + "claim_ids": claim_ids, + "required_controls_satisfied": sum(item["control"] for item in selected) >= minimum_controls, + "approval_required": True, + "evidence_boundary": "This deterministic proposal ranks only declared candidate properties and constraints. It does not infer chemical similarity, validate uncertainty calibration, execute an experiment, or prove expected information gain." + } + fingerprint = _fingerprint(material) + return {**material, "proposal_id": f"experiment-{fingerprint[:20]}", "fingerprint": fingerprint} + + +def validate_experiment_proposal(payload: Mapping[str, Any]) -> None: + if payload.get("schema_version") != 1 or payload.get("status") != "proposed" or payload.get("executed") is not False: + raise ValueError("invalid experiment proposal state") + for field in ("proposal_id", "decision", "created_at", "evidence_boundary"): + _text(payload.get(field), field) + if not isinstance(payload.get("selected"), list) or not isinstance(payload.get("rejected"), list): + raise ValueError("selected and rejected must be lists") + material = dict(payload) + proposal_id = str(material.pop("proposal_id")) + fingerprint = str(material.pop("fingerprint", "")).lower() + if _fingerprint(material) != fingerprint or proposal_id != f"experiment-{fingerprint[:20]}": + raise ValueError("experiment proposal fingerprint or ID mismatch") From d356b989620e9e9488a59816158cae9a8c962c20 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:31:03 +0900 Subject: [PATCH 53/87] Add next-experiment planning CLI --- scripts/plan_next_experiment.py | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 scripts/plan_next_experiment.py diff --git a/scripts/plan_next_experiment.py b/scripts/plan_next_experiment.py new file mode 100644 index 00000000..454c0659 --- /dev/null +++ b/scripts/plan_next_experiment.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Create a deterministic, non-executing multi-objective experiment proposal.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.experiment_planner import plan_next_experiment, validate_experiment_proposal # noqa: E402 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("input", type=Path) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + try: + payload = json.loads(args.input.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError("input must be a JSON object") + proposal = plan_next_experiment(payload) + validate_experiment_proposal(proposal) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(proposal, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + except (OSError, ValueError, json.JSONDecodeError) as error: + print(f"error: {error}", file=sys.stderr) + return 1 + print(f"Success! Proposal written to: {args.output.resolve()}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From bdd3714d5e5344e79d9fac1e53234bf0f646772e Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:31:49 +0900 Subject: [PATCH 54/87] Add FAIR-oriented run export and scientific BOM --- src/codex_science/fair_export.py | 231 +++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 src/codex_science/fair_export.py diff --git a/src/codex_science/fair_export.py b/src/codex_science/fair_export.py new file mode 100644 index 00000000..cfcb06fd --- /dev/null +++ b/src/codex_science/fair_export.py @@ -0,0 +1,231 @@ +"""Portable FAIR-oriented exports from a validated Codex Science run. + +The exporters produce conservative RO-Crate-, W3C PROV-, and scientific-BOM +records. They do not claim formal certification, regulatory compliance, or +portability of bytes that are not included in the source bundle. +""" + +from __future__ import annotations + +import hashlib +import json +import mimetypes +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Mapping + +from codex_science.artifact_store import describe_directory, stream_sha256 +from codex_science.artifacts import validate_bundle + + +RO_CRATE_CONTEXT = "https://w3id.org/ro/crate/1.1/context" +PROV_CONTEXT = "https://www.w3.org/ns/prov#" + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _sha(value: Any) -> str: + return hashlib.sha256(_canonical_bytes(value)).hexdigest() + + +def _write_json(path: Path, payload: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + + +def _media_type(path: str, record: Mapping[str, Any]) -> str: + if record.get("media_type"): + return str(record["media_type"]) + guessed, _encoding = mimetypes.guess_type(path) + return guessed or "application/octet-stream" + + +def _artifact_entity(record: Mapping[str, Any]) -> dict[str, Any]: + path = str(record["path"]) + entity = { + "@id": path, + "@type": "Dataset" if record.get("artifact_type") == "directory-tree" else "File", + "name": path, + "encodingFormat": _media_type(path, record), + "sha256": str(record["sha256"]), + "additionalType": str(record.get("kind", "artifact")), + } + for source, target in (("size_bytes", "contentSize"), ("artifact_type", "artifactType"), ("root_sha256", "merkleRoot"), ("entry_count", "entryCount")): + if record.get(source) is not None: + entity[target] = record[source] + return entity + + +def build_ro_crate(manifest: Mapping[str, Any], *, exported_at: str) -> dict[str, Any]: + run_id = str(manifest["run_id"]) + artifacts = [_artifact_entity(item) for item in manifest.get("artifacts", []) if isinstance(item, Mapping)] + root = { + "@id": "./", + "@type": "Dataset", + "name": f"Codex Science run {run_id}", + "description": str(manifest.get("question", "")), + "dateModified": exported_at, + "hasPart": [{"@id": item["@id"]} for item in artifacts], + "mainEntity": {"@id": "manifest.json"}, + "conformsTo": [ + {"@id": "https://w3id.org/ro/crate/1.1"}, + {"@id": "https://github.com/eightmm/codex-science"} + ], + "reviewStatus": str(manifest.get("review", {}).get("status", "unknown")), + } + manifest_entity = { + "@id": "manifest.json", + "@type": "File", + "name": "Codex Science artifact manifest", + "encodingFormat": "application/json", + "about": {"@id": "./"}, + } + claims = [] + for claim in manifest.get("claims", []): + if not isinstance(claim, Mapping) or not claim.get("id"): + continue + claim_id = f"#claim-{claim['id']}" + claims.append({ + "@id": claim_id, + "@type": "CreativeWork", + "identifier": str(claim["id"]), + "text": str(claim.get("text", "")), + "citation": [{"@id": str(item)} for item in claim.get("evidence", [])], + }) + root["about"] = [{"@id": item["@id"]} for item in claims] + return { + "@context": RO_CRATE_CONTEXT, + "@graph": [ + {"@id": "ro-crate-metadata.json", "@type": "CreativeWork", "about": {"@id": "./"}, "conformsTo": {"@id": "https://w3id.org/ro/crate/1.1"}}, + root, + manifest_entity, + *artifacts, + *claims, + ], + "codexScienceBoundary": "This export is RO-Crate-oriented metadata over a validated local bundle. External or omitted bytes remain external and formal RO-Crate conformance is not certified by this exporter." + } + + +def build_prov(manifest: Mapping[str, Any], *, exported_at: str) -> dict[str, Any]: + run_id = str(manifest["run_id"]) + entities: dict[str, Any] = {} + activities: dict[str, Any] = {} + used: dict[str, Any] = {} + generated: dict[str, Any] = {} + for index, item in enumerate(manifest.get("inputs", [])): + if not isinstance(item, Mapping): + continue + identifier = str(item.get("id") or item.get("path") or item.get("identifier") or f"input-{index}") + entities[f"input:{identifier}"] = {"prov:type": "Input", "codex:record": dict(item)} + for item in manifest.get("artifacts", []): + if isinstance(item, Mapping): + entities[f"artifact:{item['path']}"] = {"prov:type": "Artifact", "prov:label": str(item["path"]), "codex:sha256": str(item["sha256"]), "codex:kind": str(item.get("kind", "artifact"))} + for index, execution in enumerate(manifest.get("executions", [])): + if not isinstance(execution, Mapping): + continue + activity_id = f"execution:{execution.get('id', index)}" + activities[activity_id] = {"prov:type": "Execution", "prov:label": str(execution.get("command", activity_id)), "codex:exitCode": execution.get("exit_code")} + for entity_id in entities: + if entity_id.startswith("input:"): + used[f"used:{activity_id}:{entity_id}"] = {"prov:activity": activity_id, "prov:entity": entity_id} + run_activity = f"run:{run_id}" + activities[run_activity] = {"prov:type": "ScientificRun", "prov:label": str(manifest.get("question", run_id)), "prov:endedAtTime": exported_at, "codex:reviewStatus": str(manifest.get("review", {}).get("status", "unknown"))} + for entity_id in entities: + if entity_id.startswith("artifact:"): + generated[f"generated:{entity_id}"] = {"prov:entity": entity_id, "prov:activity": run_activity} + return { + "prefix": {"prov": PROV_CONTEXT, "codex": "https://github.com/eightmm/codex-science#"}, + "entity": entities, + "activity": activities, + "used": used, + "wasGeneratedBy": generated, + "codexScienceBoundary": "This is a deterministic W3C-PROV-oriented export of recorded lineage. It does not authenticate agents or reconstruct omitted execution events." + } + + +def build_scientific_bom(manifest: Mapping[str, Any], sidecars: Mapping[str, Any], *, exported_at: str) -> dict[str, Any]: + components: list[dict[str, Any]] = [] + for index, item in enumerate(manifest.get("code", [])): + if isinstance(item, Mapping): + components.append({"component_type": "code", "id": str(item.get("path") or item.get("module") or f"code-{index}"), "record": dict(item)}) + environment = manifest.get("environment", {}) + if isinstance(environment, Mapping): + for key in ("container_digest", "lockfile_sha256", "code_revision", "model_registry_sha256"): + if environment.get(key) is not None: + components.append({"component_type": key, "id": str(environment[key]), "record": {"value": environment[key]}}) + packages = environment.get("packages", []) + if isinstance(packages, list): + for index, package in enumerate(packages): + components.append({"component_type": "package", "id": str(package if not isinstance(package, Mapping) else package.get("name", f"package-{index}")), "record": package}) + for index, item in enumerate(manifest.get("inputs", [])): + if isinstance(item, Mapping): + component_type = "dataset" if item.get("source") or item.get("identifier") else "input" + components.append({"component_type": component_type, "id": str(item.get("id") or item.get("identifier") or item.get("path") or f"input-{index}"), "record": dict(item)}) + for receipt in sidecars.get("model_receipts_v2", []): + if isinstance(receipt, Mapping): + components.append({"component_type": "model-receipt", "id": str(receipt.get("model_id", "model")), "record": dict(receipt)}) + for record in manifest.get("artifacts", []): + if isinstance(record, Mapping) and record.get("kind") in {"database-snapshot", "model-weight", "container", "license-bom"}: + components.append({"component_type": str(record["kind"]), "id": str(record["path"]), "record": dict(record)}) + normalized = sorted(components, key=lambda item: (item["component_type"], item["id"], _sha(item["record"]))) + return { + "schema_version": 1, + "run_id": str(manifest["run_id"]), + "generated_at": exported_at, + "components": normalized, + "component_count": len(normalized), + "limitations": [ + "Only dependencies recorded in the manifest, environment, and recognized receipts are included.", + "A missing license record is not evidence that reuse is permitted.", + "Container, model, weight, database, and dataset identities remain source-specific." + ], + "evidence_boundary": "The scientific BOM supports dependency and license review; it is not a legal opinion, software vulnerability scan, or proof of complete transitive dependency capture." + } + + +def export_run(manifest_path: Path, output_dir: Path, *, exported_at: str | None = None) -> dict[str, Any]: + manifest_path = manifest_path.resolve() + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + if not isinstance(manifest, dict): + raise ValueError("manifest must be an object") + sidecars = validate_bundle(manifest, manifest_path.parent) + output_dir = output_dir.resolve() + output_dir.mkdir(parents=True, exist_ok=True) + timestamp = exported_at or _now() + ro_crate = build_ro_crate(manifest, exported_at=timestamp) + prov = build_prov(manifest, exported_at=timestamp) + bom = build_scientific_bom(manifest, sidecars, exported_at=timestamp) + files = { + "ro_crate": output_dir / "ro-crate-metadata.json", + "prov": output_dir / "prov.json", + "scientific_bom": output_dir / "scientific-bom.json", + } + _write_json(files["ro_crate"], ro_crate) + _write_json(files["prov"], prov) + _write_json(files["scientific_bom"], bom) + exports = [] + for name, path in files.items(): + digest, size = stream_sha256(path) + exports.append({"name": name, "path": path.name, "sha256": digest, "size_bytes": size}) + manifest_digest, _manifest_size = stream_sha256(manifest_path) + receipt_material = { + "schema_version": 1, + "run_id": manifest["run_id"], + "source_manifest_path": str(manifest_path), + "source_manifest_sha256": manifest_digest, + "generated_at": timestamp, + "exports": exports, + "status": "generated", + "certified": False, + "regulatory_compliance_claimed": False, + "evidence_boundary": "The export packages validated recorded metadata. It does not certify RO-Crate, W3C PROV, regulatory, or legal compliance." + } + receipt = {**receipt_material, "fingerprint": _sha(receipt_material)} + _write_json(output_dir / "export-receipt.json", receipt) + return receipt From 1ed26e2aaad59244124e359acaeb1ea7907f1847 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:32:04 +0900 Subject: [PATCH 55/87] Add FAIR scientific run export CLI --- scripts/export_scientific_run.py | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 scripts/export_scientific_run.py diff --git a/scripts/export_scientific_run.py b/scripts/export_scientific_run.py new file mode 100644 index 00000000..318faf39 --- /dev/null +++ b/scripts/export_scientific_run.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Export a validated run as RO-Crate-oriented, PROV, and scientific-BOM metadata.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.fair_export import export_run # noqa: E402 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("manifest", type=Path) + parser.add_argument("output", type=Path) + parser.add_argument("--receipt", type=Path, required=True) + args = parser.parse_args() + try: + receipt = export_run(args.manifest, args.output) + args.receipt.parent.mkdir(parents=True, exist_ok=True) + args.receipt.write_text(json.dumps(receipt, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + except (OSError, ValueError, json.JSONDecodeError) as error: + print(f"error: {error}", file=sys.stderr) + return 1 + print(f"Success! Export written to: {args.output.resolve()}") + print(f"Success! Receipt written to: {args.receipt.resolve()}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From f010147304cf5d6a159affc753389824ecae5227 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:32:27 +0900 Subject: [PATCH 56/87] Test reviewed-run skill draft generation --- tests/test_pipeline_compiler.py | 100 ++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 tests/test_pipeline_compiler.py diff --git a/tests/test_pipeline_compiler.py b/tests/test_pipeline_compiler.py new file mode 100644 index 00000000..0a65c810 --- /dev/null +++ b/tests/test_pipeline_compiler.py @@ -0,0 +1,100 @@ +import hashlib +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.artifacts import add_artifact, new_manifest, write_manifest +from codex_science.pipeline_compiler import compile_pipeline_draft + + +def reviewed_run(root: Path, *, review_status: str = "passed") -> Path: + run = root / "run" + run.mkdir() + table = run / "result.json" + table.write_text('{"value": 7}\n', encoding="utf-8") + digest = hashlib.sha256(table.read_bytes()).hexdigest() + manifest = new_manifest( + "source-run", + "Does the fixture satisfy the prespecified threshold?", + [{"id": "compute", "description": "Compute the fixture result", "status": "completed"}], + ) + manifest["inputs"] = [{"id": "input", "identifier": "fixture:input"}] + manifest["code"] = [{"path": "analysis.py", "revision": "abc"}] + manifest["executions"] = [{"command": "python analysis.py", "exit_code": 0}] + manifest["environment"] = {"python": ">=3.11", "seed": 0} + manifest["claims"] = [{"id": "C1", "text": "The result is seven.", "evidence": ["result.json"]}] + manifest["review"] = {"status": review_status, "findings": []} + add_artifact(manifest, "result.json", kind="result", sha256=digest) + path = run / "manifest.json" + write_manifest(manifest, path) + return path + + +class PipelineCompilerTests(unittest.TestCase): + def test_passed_run_generates_nonactive_progressive_skill_draft(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + manifest = reviewed_run(root) + output = root / "draft" + receipt = compile_pipeline_draft( + manifest_path=manifest, + output_dir=output, + name="fixture-pipeline", + description="Run the reviewed fixture workflow on new approved inputs.", + command_contract=[["python", "analysis.py", "--input", "input.json", "--output", "result.json"]], + generated_at="2026-07-21T00:00:00Z", + ) + self.assertEqual("draft", receipt["status"]) + self.assertFalse(receipt["activated"]) + self.assertFalse(receipt["cataloged"]) + self.assertTrue((output / "SKILL.md").is_file()) + self.assertTrue((output / "input.schema.json").is_file()) + self.assertTrue((output / "output.schema.json").is_file()) + index = json.loads((output / "references" / "index.json").read_text(encoding="utf-8")) + self.assertEqual("pipeline", index["references"][0]["id"]) + skill = (output / "SKILL.md").read_text(encoding="utf-8") + self.assertIn("not active", skill.lower()) + self.assertIn("references/pipeline.md", skill) + reference = (output / "references" / "pipeline.md").read_text(encoding="utf-8") + self.assertIn("## Command contract", reference) + self.assertIn("## Validation and promotion", reference) + self.assertIn('["python", "analysis.py"', reference) + + def test_nonpassed_or_incomplete_run_cannot_be_promoted(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + manifest = reviewed_run(root, review_status="findings") + with self.assertRaisesRegex(ValueError, "passed"): + compile_pipeline_draft( + manifest_path=manifest, + output_dir=root / "draft", + name="fixture-pipeline", + description="Fixture.", + ) + + def test_existing_nonempty_output_and_invalid_name_fail_closed(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + manifest = reviewed_run(root) + output = root / "draft" + output.mkdir() + (output / "keep.txt").write_text("do not overwrite", encoding="utf-8") + with self.assertRaisesRegex(ValueError, "not empty"): + compile_pipeline_draft( + manifest_path=manifest, + output_dir=output, + name="fixture-pipeline", + description="Fixture.", + ) + with self.assertRaisesRegex(ValueError, "kebab-case"): + compile_pipeline_draft( + manifest_path=manifest, + output_dir=root / "other", + name="Invalid Name", + description="Fixture.", + ) + + +if __name__ == "__main__": + unittest.main() From 712bd3dc440381a168f178965b84be34d04db5d5 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:32:49 +0900 Subject: [PATCH 57/87] Test deterministic next-experiment planning --- tests/test_experiment_planner.py | 68 ++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/test_experiment_planner.py diff --git a/tests/test_experiment_planner.py b/tests/test_experiment_planner.py new file mode 100644 index 00000000..1c1a47b9 --- /dev/null +++ b/tests/test_experiment_planner.py @@ -0,0 +1,68 @@ +import copy +import unittest + +from codex_science.experiment_planner import plan_next_experiment, validate_experiment_proposal + + +INPUT = { + "schema_version": 1, + "decision": "Select the next compound batch to discriminate potency and solubility hypotheses.", + "objectives": [ + {"name": "potency", "direction": "maximize", "weight": 0.6, "required": True}, + {"name": "solubility", "direction": "maximize", "weight": 0.4, "required": True} + ], + "candidates": [ + {"id": "control", "properties": {"potency": 0.2, "solubility": 0.9}, "cost": 2, "uncertainty": 0.1, "diversity_group": "control", "control": True}, + {"id": "a1", "properties": {"potency": 0.95, "solubility": 0.3}, "cost": 4, "uncertainty": 0.8, "diversity_group": "A"}, + {"id": "a2", "properties": {"potency": 0.85, "solubility": 0.4}, "cost": 3, "uncertainty": 0.2, "diversity_group": "A"}, + {"id": "b1", "properties": {"potency": 0.6, "solubility": 0.85}, "cost": 3, "uncertainty": 0.7, "diversity_group": "B"}, + {"id": "c1", "properties": {"potency": 0.55, "solubility": 0.7}, "cost": 10, "uncertainty": 0.5, "diversity_group": "C"}, + {"id": "excluded", "properties": {"potency": 1.0, "solubility": 1.0}, "cost": 1, "uncertainty": 1.0, "diversity_group": "X", "eligible": False, "exclusion_reason": "reactive-substructure"} + ], + "constraints": {"batch_size": 3, "budget": 10, "diversity_group_cap": 1, "minimum_controls": 1}, + "uncertainty_weight": 0.25, + "diversity_bonus": 0.2, + "claim_ids": ["C-potency", "C-solubility"] +} + + +class ExperimentPlannerTests(unittest.TestCase): + def test_controls_budget_diversity_and_pareto_ranking_are_explicit(self) -> None: + proposal = plan_next_experiment(INPUT, created_at="2026-07-21T00:00:00Z") + validate_experiment_proposal(proposal) + selected = [item["id"] for item in proposal["selected"]] + self.assertEqual("control", selected[0]) + self.assertEqual(3, len(selected)) + self.assertLessEqual(proposal["total_cost"], 10) + self.assertEqual(1, proposal["diversity_groups"]["control"]) + self.assertTrue(all(count <= 1 for count in proposal["diversity_groups"].values())) + self.assertTrue(proposal["required_controls_satisfied"]) + self.assertFalse(proposal["executed"]) + excluded = next(item for item in proposal["rejected"] if item["id"] == "excluded") + self.assertIn("reactive-substructure", excluded["reasons"]) + self.assertIn("expected_information_gain_proxy", proposal) + + def test_result_is_deterministic_for_same_contract(self) -> None: + first = plan_next_experiment(INPUT, created_at="2026-07-21T00:00:00Z") + second = plan_next_experiment(copy.deepcopy(INPUT), created_at="2026-07-21T00:00:00Z") + self.assertEqual(first, second) + + def test_required_controls_and_required_objectives_fail_closed(self) -> None: + no_controls = copy.deepcopy(INPUT) + no_controls["candidates"] = [item for item in no_controls["candidates"] if not item.get("control")] + with self.assertRaisesRegex(ValueError, "minimum_controls"): + plan_next_experiment(no_controls) + missing = copy.deepcopy(INPUT) + del missing["candidates"][1]["properties"]["solubility"] + with self.assertRaisesRegex(ValueError, "lacks required objectives"): + plan_next_experiment(missing) + + def test_required_controls_that_exceed_budget_are_not_silently_removed(self) -> None: + constrained = copy.deepcopy(INPUT) + constrained["constraints"]["budget"] = 1 + with self.assertRaisesRegex(ValueError, "required controls"): + plan_next_experiment(constrained) + + +if __name__ == "__main__": + unittest.main() From 79256b2247f9c3d282dc6da0369f5a00c9343619 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:33:13 +0900 Subject: [PATCH 58/87] Test FAIR-oriented run exports --- tests/test_fair_export.py | 81 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/test_fair_export.py diff --git a/tests/test_fair_export.py b/tests/test_fair_export.py new file mode 100644 index 00000000..c9366ab1 --- /dev/null +++ b/tests/test_fair_export.py @@ -0,0 +1,81 @@ +import hashlib +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.artifacts import add_artifact, new_manifest, write_manifest +from codex_science.fair_export import export_run + + +class FairExportTests(unittest.TestCase): + def test_validated_run_exports_ro_crate_prov_bom_and_receipt(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + run = root / "run" + run.mkdir() + result = run / "result.csv" + result.write_text("metric,value\neffect,0.7\n", encoding="utf-8") + digest = hashlib.sha256(result.read_bytes()).hexdigest() + manifest = new_manifest( + "run-fair", + "Does the recorded effect satisfy the acceptance contract?", + [{"id": "compute", "description": "Compute the effect", "status": "completed"}], + ) + manifest["inputs"] = [{"id": "dataset", "identifier": "doi:10.1000/dataset", "source": "repository", "release": "1"}] + manifest["code"] = [{"path": "analysis.py", "revision": "abc123"}] + manifest["executions"] = [{"id": "exec-1", "command": "python analysis.py", "exit_code": 0}] + manifest["environment"] = {"python": "3.12", "container_digest": "sha256:" + "a" * 64, "packages": [{"name": "numpy", "version": "2"}]} + manifest["claims"] = [{"id": "C1", "text": "The recorded effect is 0.7.", "evidence": ["result.csv"]}] + manifest["review"] = {"status": "passed", "findings": []} + add_artifact(manifest, "result.csv", kind="result-table", sha256=digest) + manifest_path = run / "manifest.json" + write_manifest(manifest, manifest_path) + + output = root / "export" + receipt = export_run(manifest_path, output, exported_at="2026-07-21T00:00:00Z") + self.assertFalse(receipt["certified"]) + self.assertFalse(receipt["regulatory_compliance_claimed"]) + self.assertEqual(3, len(receipt["exports"])) + for filename in ("ro-crate-metadata.json", "prov.json", "scientific-bom.json", "export-receipt.json"): + self.assertTrue((output / filename).is_file()) + + crate = json.loads((output / "ro-crate-metadata.json").read_text(encoding="utf-8")) + self.assertEqual("https://w3id.org/ro/crate/1.1/context", crate["@context"]) + ids = {item["@id"] for item in crate["@graph"]} + self.assertIn("result.csv", ids) + self.assertIn("#claim-C1", ids) + + prov = json.loads((output / "prov.json").read_text(encoding="utf-8")) + self.assertIn("run:run-fair", prov["activity"]) + self.assertIn("artifact:result.csv", prov["entity"]) + + bom = json.loads((output / "scientific-bom.json").read_text(encoding="utf-8")) + component_types = {item["component_type"] for item in bom["components"]} + self.assertIn("code", component_types) + self.assertIn("dataset", component_types) + self.assertIn("package", component_types) + self.assertIn("container_digest", component_types) + + def test_changed_artifact_bytes_prevent_export(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + result = root / "result.json" + result.write_text('{"value": 1}\n', encoding="utf-8") + manifest = new_manifest( + "run-bad", + "Q", + [{"id": "x", "description": "X", "status": "completed"}], + ) + manifest["claims"] = [{"id": "C", "text": "Claim", "evidence": ["result.json"]}] + manifest["review"] = {"status": "passed", "findings": []} + add_artifact(manifest, "result.json", kind="result", sha256=hashlib.sha256(result.read_bytes()).hexdigest()) + path = root / "manifest.json" + write_manifest(manifest, path) + result.write_text('{"value": 2}\n', encoding="utf-8") + with self.assertRaisesRegex(ValueError, "digest mismatch"): + export_run(path, root / "export") + + +if __name__ == "__main__": + unittest.main() From 19ea40efc9d190a15a1618537979785d03c5fe30 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:33:47 +0900 Subject: [PATCH 59/87] Document reviewed-run pipeline promotion --- .../references/pipeline-promotion.md | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 skills/codex-science/references/pipeline-promotion.md diff --git a/skills/codex-science/references/pipeline-promotion.md b/skills/codex-science/references/pipeline-promotion.md new file mode 100644 index 00000000..408dc2e1 --- /dev/null +++ b/skills/codex-science/references/pipeline-promotion.md @@ -0,0 +1,175 @@ +# Promote a reviewed run to a reusable skill draft + +Read this reference before converting a completed run into a reusable workflow. Promotion generates a draft only. It never activates a skill, changes the audited catalog, copies private inputs, or turns one successful run into evidence of generalization. + +## Preconditions + +The source run must satisfy all of the following: + +- complete, hash-valid artifact bundle; +- all recorded plan steps completed; +- manifest review status `passed`; +- current deterministic record review also `passed`; +- source manifest bytes available and immutable; +- no intent to copy credentials, private prompts, sensitive raw data, or proprietary inputs; +- commands represented as argv arrays rather than reconstructed shell prose. + +A run with findings, blocked work, missing outputs, stale receipts, or incomplete plan steps cannot be promoted. Preserve it as failed or inconclusive. + +## CLI + +```bash +python scripts/promote_run_to_skill.py \ + artifacts/run-014/manifest.json \ + drafts/receptor-state-docking \ + --name receptor-state-docking \ + --description "Run the reviewed receptor-state docking workflow on new approved inputs." \ + --commands artifacts/run-014/promoted-commands.json \ + --limitation "Validated only on the checked acceptance fixture." \ + --limitation "External docking engine and databases must be repinned." \ + --receipt drafts/receptor-state-docking.promotion.json +``` + +`--commands` contains a JSON array of argv arrays: + +```json +[ + ["python", "prepare_receptor.py", "--input", "receptor.cif", "--output", "prepared.pdbqt"], + ["vina", "--config", "docking.conf", "--out", "poses.pdbqt"], + ["python", "validate_poses.py", "--poses", "poses.pdbqt", "--output", "metrics.json"] +] +``` + +Do not place tokens, passwords, shell redirections, pipes, or unreviewed remote commands in this file. Create separate reviewed steps when piping or staging is scientifically material. + +## Generated draft + +```text +drafts/receptor-state-docking/ + SKILL.md + input.schema.json + output.schema.json + promotion.json + references/ + index.json + pipeline.md +``` + +### `SKILL.md` + +The draft includes: + +- new decision contract; +- mandatory progressive reference; +- source workflow shape; +- provenance and review handoffs; +- explicit non-activation and non-generalization boundaries. + +It must not contain the source conclusion as the new answer. + +### `input.schema.json` + +The new invocation must provide: + +```json +{ + "question": "...", + "parameters": {}, + "inputs": [ + { + "id": "receptor", + "path": "receptor.cif", + "sha256": "..." + } + ], + "acceptance": { + "criteria": [], + "review_required": true + } +} +``` + +An input needs an ID and either a local path or canonical external identifier. Add checksum, source, and release when practical. The source run's local input paths are retained only as historical metadata in the generated schema and must not be reused blindly. + +### `output.schema.json` + +A successful new invocation must return: + +- new run ID; +- new manifest path and SHA-256; +- current claims and artifacts; +- current passed review; +- current limitations. + +A source run, process exit receipt, or copied report does not satisfy the output schema. + +### `promotion.json` + +```json +{ + "status": "draft", + "activated": false, + "cataloged": false, + "source_run_id": "run-014", + "source_manifest_sha256": "...", + "required_next_steps": [], + "fingerprint": "..." +} +``` + +The receipt binds the source manifest hash, generated skill name, command count, source artifacts, limitations, and required promotion steps. + +## Promotion path + +A generated draft starts below active maturity. Before catalog activation: + +1. replace source-local assumptions with explicit input and environment contracts; +2. add a redistributable acceptance fixture; +3. seed material failure classes; +4. add deterministic tests; +5. add drift handling for sources, models, weights, and databases; +6. run the fixture in a clean environment; +7. run independent record, method, source, and reproduction review as applicable; +8. add an honest `quality.json`; +9. pass reference, maturity, inventory, wrapper, plugin, and scientific contract checks; +10. activate only through the repository catalog policy. + +One source run is not enough for L4 unless the generated draft also has explicit acceptance and seeded-defect coverage. + +## Search patterns + +Use these exact searches when reading the generated `references/pipeline.md`: + +- `## Source record` +- `## Environment` +- `## Command contract` +- `## Output contract` +- `## Validation and promotion` +- `## Failure handling` + +## Failure handling + +| Failure | Required response | +| --- | --- | +| source review no longer passes | stop; do not generate or use the draft | +| source manifest hash changed | restore the reviewed bytes or use a new source run | +| command references unavailable local paths | parameterize the path and validate in the target environment | +| model/database revision unavailable | repin a reviewed replacement and create a new acceptance result | +| private input dependency | replace with an approved immutable reference or remove it | +| output schema mismatch | preserve the failed run and repair code/schema | +| source result fails to reproduce | report non-reproduction; do not weaken thresholds post hoc | +| output directory non-empty | choose a new empty directory; never overwrite an existing skill draft | + +## Common mistakes + +- Adding the generated directory directly to `authored-skills/` without review. +- Copying private source data into a fixture. +- Treating source commands as portable without environment and revision checks. +- Reusing source review receipts. +- Omitting failed or abandoned source steps. +- Generating a broad trigger description from a narrow fixture. +- Claiming the compiler has validated scientific generalization. + +## Evidence boundary + +The compiler preserves reviewed workflow structure and source lineage. It does not validate new inputs, activate the draft, ensure portability, establish model or database licensing, or prove that the method generalizes. From 2df2b5cb6c68f2ee4e86faf1d46990ce9a4e78d0 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:34:32 +0900 Subject: [PATCH 60/87] Document deterministic next-experiment planning --- .../references/next-experiment-planning.md | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 skills/codex-science/references/next-experiment-planning.md diff --git a/skills/codex-science/references/next-experiment-planning.md b/skills/codex-science/references/next-experiment-planning.md new file mode 100644 index 00000000..44beb28c --- /dev/null +++ b/skills/codex-science/references/next-experiment-planning.md @@ -0,0 +1,239 @@ +# Multi-objective next-experiment planning + +Read this reference before ranking candidates for a new assay, synthesis batch, simulation, data collection round, or other experiment. The planner produces a deterministic proposal from declared values and constraints. It does not execute an experiment or infer valid uncertainty, similarity, or information gain from a model label. + +## Input contract + +```json +{ + "schema_version": 1, + "decision": "Select the next 24 compounds.", + "objectives": [ + { + "name": "potency", + "direction": "maximize", + "weight": 0.5, + "required": true + }, + { + "name": "solubility", + "direction": "maximize", + "weight": 0.3, + "required": true + }, + { + "name": "off_target_risk", + "direction": "minimize", + "weight": 0.2, + "required": true + } + ], + "candidates": [ + { + "id": "compound-001", + "properties": { + "potency": 8.2, + "solubility": 0.6, + "off_target_risk": 0.2 + }, + "cost": 450, + "uncertainty": 0.7, + "diversity_group": "scaffold-A", + "control": false, + "eligible": true, + "metadata": { + "source_artifact": "candidate-table.parquet", + "source_record": "compound-001" + } + } + ], + "constraints": { + "batch_size": 24, + "budget": 12000, + "diversity_group_cap": 4, + "minimum_controls": 2 + }, + "uncertainty_weight": 0.25, + "diversity_bonus": 0.1, + "claim_ids": ["C-potency", "C-selectivity"] +} +``` + +### Objectives + +- `direction`: `maximize` or `minimize`; +- `weight`: non-negative; at least one weight must be positive; +- `required`: every eligible candidate must contain that property. + +The planner performs within-contract min-max normalization. Therefore scores from different input batches are not directly comparable unless their candidate population and scaling contract are identical. + +### Candidates + +Required: + +- stable candidate ID; +- numeric properties; +- non-negative cost; +- non-negative uncertainty; +- diversity group. + +Optional: + +- `control`; +- eligibility and exclusion reason; +- source metadata. + +`diversity_group` is an explicit category supplied by the workflow. It may represent scaffold, target family, donor, site, assay plate, or another prespecified grouping. The planner does not derive chemical or biological similarity. + +### Constraints + +- `batch_size`: maximum selected records; +- `budget`: total declared cost cap; +- `diversity_group_cap`: maximum selected records per group; +- `minimum_controls`: required eligible controls. + +Required controls are selected before optimization. If they cannot fit the batch, budget, or diversity cap, the proposal fails rather than silently removing them. + +## CLI + +```bash +python scripts/plan_next_experiment.py \ + artifacts/run-020/next-experiment-input.json \ + --output artifacts/run-020/next-experiment.proposal.json +``` + +The proposal is deterministic when the full input, including time field supplied by the API, is unchanged. + +## Selection method + +The current deterministic policy performs: + +1. eligibility filtering; +2. objective min-max normalization with minimize directions inverted; +3. Pareto-front construction; +4. mandatory control selection; +5. weighted objective utility; +6. declared uncertainty bonus; +7. explicit diversity-group bonus; +8. greedy selection subject to budget, batch, and group cap; +9. explicit rejection reasons. + +The utility score is a ranking instrument inside the proposal, not a calibrated expected outcome. + +## Output contract + +```json +{ + "schema_version": 1, + "proposal_id": "experiment-...", + "decision": "...", + "status": "proposed", + "executed": false, + "selected": [], + "rejected": [], + "pareto_fronts": [], + "total_cost": 11500, + "remaining_budget": 500, + "diversity_groups": {}, + "expected_information_gain_proxy": 0.42, + "required_controls_satisfied": true, + "approval_required": true, + "claim_ids": [], + "evidence_boundary": "...", + "fingerprint": "..." +} +``` + +Every selected record retains: + +- original declared properties; +- normalized objective values; +- uncertainty score; +- Pareto front; +- base utility; +- cost, control state, and diversity group. + +Every rejected record retains one or more reasons such as: + +```text +ineligible +reactive-substructure +budget +diversity-group-cap +not-selected-within-batch +``` + +Do not remove rejected candidates or reasons from the saved proposal. + +## Required workflow + +1. Define the decision and claims before candidate scoring. +2. Validate candidate identities and source artifacts. +3. Confirm objective direction, units, and applicability. +4. Validate uncertainty calibration separately; use zero uncertainty weight if unvalidated. +5. Define controls and diversity groups before outcome inspection. +6. Set budget and batch constraints. +7. Generate the proposal. +8. Inspect Pareto fronts, selected controls, repeated groups, costs, and rejection reasons. +9. Run domain-specific safety and feasibility filters that are not encoded in this generic planner. +10. Obtain experimental, remote, paid, synthesis, or write approval. +11. Save the approved experiment package and execute through the appropriate system. +12. Ingest results without dropping failed synthesis, assay failure, or missing outcomes. +13. Update claims and project evidence memory. +14. Review selection bias and whether the next round remains comparable. + +## Applicability examples + +### Compound selection + +Use declared scaffold or series as `diversity_group`. Candidate properties may include potency, selectivity, solubility, permeability, synthesis confidence, novelty, or model uncertainty. Do not treat docking score as experimental potency. + +### Single-cell follow-up + +Use donor, condition, or cluster as diversity group. Include donor balance and controls explicitly. Do not select cells as independent biological replicates when donors are the experimental unit. + +### Literature evidence acquisition + +Candidates may be full texts, datasets, or experiments. Cost may be retrieval or curation effort. Uncertainty must describe the decision-relevant evidence gap, not search-engine score. + +### Simulation allocation + +Candidates may be receptor states, ligand microstates, initial conditions, or parameter regimes. Use cost and resource caps; do not call multiple seeds independent experimental evidence. + +## Search patterns + +- `## Input contract` +- `## Selection method` +- `## Output contract` +- `## Required workflow` +- `## Failure handling` +- `## Common mistakes` + +## Failure handling + +| Failure | Required response | +| --- | --- | +| required property missing | stop; repair the candidate table or mark the objective non-required with rationale | +| controls unavailable | redesign the batch; do not proceed without the declared controls | +| controls exceed budget/batch | revise the approved experiment contract | +| uncertainty not calibrated | set uncertainty weight to zero or treat it as exploratory only | +| diversity grouping unavailable | define a reviewed grouping or set a conservative cap of one per candidate | +| costs incomparable | convert to one declared unit or separate budgets | +| no candidate fits | return an empty/infeasible decision record; do not relax constraints silently | +| candidate excluded after proposal | preserve the original proposal and create a revision/diff | +| source properties changed | invalidate the proposal fingerprint and regenerate | + +## Common mistakes + +- Calling the utility score predicted efficacy. +- Treating the information-gain proxy as a calibrated expectation. +- Allowing model uncertainty to dominate without calibration. +- Deriving diversity from names instead of an explicit method. +- Omitting controls to fit more candidates. +- Counting samples, cells, conformers, or seeds as independent units incorrectly. +- Changing objective weights after inspecting the selected list without preserving a revision. +- Executing the proposal without feasibility, safety, cost, and write approval. + +## Evidence boundary + +The planner makes multi-objective constraints and selection bias inspectable. It does not validate candidate measurements, infer similarity, calibrate uncertainty, estimate real information gain, execute experiments, or prove that the selected batch is scientifically optimal. From 6e8085f232130dad5fa04e360ee84e4ee42022ba Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:35:11 +0900 Subject: [PATCH 61/87] Document FAIR-oriented export and scientific BOM --- .../references/fair-export.md | 229 ++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 skills/science-provenance/references/fair-export.md diff --git a/skills/science-provenance/references/fair-export.md b/skills/science-provenance/references/fair-export.md new file mode 100644 index 00000000..c6383827 --- /dev/null +++ b/skills/science-provenance/references/fair-export.md @@ -0,0 +1,229 @@ +# FAIR-oriented export and scientific dependency BOM + +Read this reference before exporting a validated run to RO-Crate-oriented metadata, W3C PROV-oriented lineage, or a scientific dependency bill of materials. + +## Scope and boundary + +The exporter packages **recorded metadata from a hash-validated run**. It does not: + +- certify formal RO-Crate conformance; +- certify W3C PROV completeness; +- establish 21 CFR Part 11, GxP, HIPAA, GDPR, or other regulatory compliance; +- provide a legal opinion about licenses; +- include bytes absent from the source bundle; +- discover unrecorded transitive dependencies; +- authenticate the identity of agents or reviewers. + +Use the exports for interoperability, archival review, dependency inspection, and handoff—not as a compliance certificate. + +## CLI + +```bash +python scripts/export_scientific_run.py \ + artifacts/run-014/manifest.json \ + artifacts/run-014/exports \ + --receipt artifacts/run-014/fair-export.receipt.json +``` + +Preconditions: + +1. `manifest.json` exists; +2. every manifest artifact exists and matches SHA-256 or Merkle root; +3. recognized sidecars validate; +4. secrets and credential-bearing URLs are absent; +5. external inputs use canonical identifier, source, release, and checksum when available. + +The exporter refuses changed artifact bytes because it calls complete bundle validation first. + +## Outputs + +```text +exports/ + ro-crate-metadata.json + prov.json + scientific-bom.json + export-receipt.json +fair-export.receipt.json +``` + +The receipt passed to `--receipt` duplicates the generated export receipt at a caller-selected path for workflow integration. + +## RO-Crate-oriented metadata + +`ro-crate-metadata.json` uses: + +```json +{ + "@context": "https://w3id.org/ro/crate/1.1/context", + "@graph": [] +} +``` + +The graph includes: + +- root run dataset; +- manifest file; +- artifact file or dataset entities; +- artifact SHA-256, kind, media type, size, and directory Merkle metadata when recorded; +- claim entities and evidence links; +- review status; +- explicit Codex Science evidence boundary. + +The exporter does not copy artifact bytes into the export directory. `hasPart` points to run-relative artifacts. Preserve the source bundle or copy it through a separate verified packaging step. + +## W3C PROV-oriented lineage + +`prov.json` records: + +- input entities; +- artifact entities; +- execution activities; +- run activity; +- `used` links from recorded inputs to executions; +- `wasGeneratedBy` links from artifacts to the run; +- exit codes and artifact digests where recorded. + +The export cannot reconstruct events omitted from the source manifest. A sparse provenance record remains sparse and should not be described as complete. + +## Scientific dependency BOM + +`scientific-bom.json` gathers recorded components: + +```text +code +package +container digest +lockfile digest +code revision +model registry digest +model receipt +input +dataset +database snapshot +model weight +license BOM +``` + +Component examples: + +```json +{ + "component_type": "dataset", + "id": "doi:10.1000/example", + "record": { + "source": "repository", + "release": "2026-01", + "sha256": "..." + } +} +``` + +A missing component or license field means “not recorded,” not “not required” or “permitted.” Resolve license and data-use terms separately. + +## Export receipt + +```json +{ + "schema_version": 1, + "run_id": "run-014", + "source_manifest_path": "/absolute/path/manifest.json", + "source_manifest_sha256": "...", + "generated_at": "...", + "exports": [ + { + "name": "ro_crate", + "path": "ro-crate-metadata.json", + "sha256": "...", + "size_bytes": 1234 + } + ], + "status": "generated", + "certified": false, + "regulatory_compliance_claimed": false, + "evidence_boundary": "...", + "fingerprint": "..." +} +``` + +The receipt fingerprint covers the source manifest digest and every generated export digest. Regenerating after a source change creates a new receipt. + +## Recommended archival package + +A portable archival package should contain: + +```text +run/ + manifest.json + all included artifact bytes or immutable external-reference records + review receipts + export/ + ro-crate-metadata.json + prov.json + scientific-bom.json + export-receipt.json +``` + +After copying: + +1. revalidate every file hash and directory Merkle root; +2. resolve absolute local paths in metadata where portability matters; +3. preserve external access restrictions and licenses; +4. record the packaging command and package checksum; +5. run an offline open/read test in a clean environment. + +Do not place mutable SQLite project stores into a run bundle as primary evidence. Export their receipts and retain the original immutable runs instead. + +## Regulatory-supporting mode + +The export can support—but does not itself provide—controlled-record workflows. A regulated implementation additionally needs reviewed policies for: + +- system validation for intended use; +- authenticated users and role separation; +- electronic signatures where applicable; +- audit-trail retention; +- trusted time source; +- reason-for-change; +- record retention and retrieval; +- backup and disaster recovery; +- controlled SOP and software versions; +- incident and deviation handling; +- vendor and infrastructure qualification. + +Describe these as compliance-supporting controls unless the complete system has been formally assessed. + +## Search patterns + +- `## RO-Crate-oriented metadata` +- `## W3C PROV-oriented lineage` +- `## Scientific dependency BOM` +- `## Export receipt` +- `## Recommended archival package` +- `## Failure handling` + +## Failure handling + +| Failure | Required response | +| --- | --- | +| artifact digest mismatch | stop; restore or regenerate the source run bundle | +| missing external source release | add the release or mark it unavailable explicitly | +| output directory contains older exports | preserve or clear it through an explicit versioned archival action | +| absolute path not portable | retain it as local provenance and add a portable identifier or package-relative mapping | +| license unknown | record unknown/restricted and stop redistribution until resolved | +| external bytes unavailable | preserve immutable reference and missingness; do not invent inclusion | +| BOM omits a material model/database | correct the source provenance before exporting | +| export schema rejected downstream | preserve the failed export, correct mapping, and regenerate a new receipt | + +## Common mistakes + +- Calling the generated metadata formally certified. +- Exporting before bundle validation. +- Assuming `hasPart` means bytes were copied. +- Treating missing license metadata as permissive licensing. +- Omitting model weights, databases, or container identity. +- Reusing an export receipt after source bytes change. +- Claiming a provenance graph is complete when executions or inputs were not recorded. +- Calling a compliance-supporting package regulatory compliant. + +## Evidence boundary + +FAIR-oriented export improves findability, portability, lineage review, and dependency inspection. It does not certify completeness, licensing, regulatory compliance, or scientific validity. From ffe529eb611ded150c88a756961ec30a6b71ecc1 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:36:44 +0900 Subject: [PATCH 62/87] Integrate pipeline, experiment, and FAIR runtime contracts --- scripts/integrate_extended_runtime_v4.py | 254 +++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 scripts/integrate_extended_runtime_v4.py diff --git a/scripts/integrate_extended_runtime_v4.py b/scripts/integrate_extended_runtime_v4.py new file mode 100644 index 00000000..dc486e94 --- /dev/null +++ b/scripts/integrate_extended_runtime_v4.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +"""Integrate pipeline promotion, experiment planning, and FAIR export references.""" + +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[1] + + +def write_json(path: Path, payload: Any) -> None: + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + + +def update_index(path: Path, entry: dict[str, Any]) -> None: + payload = json.loads(path.read_text(encoding="utf-8")) + key = "references" if "references" in payload else "entries" if "entries" in payload else "references" + entries = payload.setdefault(key, []) + if not isinstance(entries, list): + raise SystemExit(f"reference index list is invalid: {path}") + existing = next((item for item in entries if isinstance(item, dict) and item.get("id") == entry["id"]), None) + if existing is None: + entries.append(entry) + else: + existing.clear() + existing.update(entry) + entries.sort(key=lambda item: str(item.get("id", "")) if isinstance(item, dict) else "") + write_json(path, payload) + + +def add_skill_reference(path: Path, marker: str, paragraph: str) -> None: + text = path.read_text(encoding="utf-8") + if marker in text: + return + heading = "## Reference usage" + if heading in text: + start = text.index(heading) + len(heading) + next_heading = text.find("\n## ", start) + if next_heading < 0: + text = text.rstrip() + "\n\n" + paragraph + "\n" + else: + text = text[:next_heading].rstrip() + "\n\n" + paragraph + "\n" + text[next_heading:] + else: + anchor = "## Workflow" + if anchor not in text: + raise SystemExit(f"cannot insert reference route: {path}") + text = text.replace(anchor, f"## Reference usage\n\n{paragraph}\n\n{anchor}", 1) + path.write_text(text, encoding="utf-8") + + +def add_quality_outputs(path: Path, values: list[str]) -> None: + payload = json.loads(path.read_text(encoding="utf-8")) + outputs = payload.setdefault("output_schemas", []) + if not isinstance(outputs, list): + raise SystemExit(f"quality output_schemas is invalid: {path}") + for value in values: + if value not in outputs: + outputs.append(value) + outputs.sort() + write_json(path, payload) + + +def patch_pipeline_compiler() -> None: + path = ROOT / "src" / "codex_science" / "pipeline_compiler.py" + text = path.read_text(encoding="utf-8") + function_start = text.index("def _skill_text(") + step_line = text.index(" step_lines = []", function_start) + if "safe_description = description.replace" not in text[function_start:step_line]: + text = text[:step_line] + ' safe_description = description.replace(chr(34), chr(39))\n' + text[step_line:] + lines = text.splitlines() + for index, line in enumerate(lines): + if line.strip().startswith('description: "{description.replace('): + lines[index] = 'description: "{safe_description}"' + text = "\n".join(lines) + ("\n" if text.endswith("\n") else "") + text = text.replace("manifest.get('review', {{}})", "manifest.get('review', {})") + text = text.replace("manifest.get('environment', {{}})", "manifest.get('environment', {})") + path.write_text(text, encoding="utf-8") + + +def patch_experiment_planner() -> None: + path = ROOT / "src" / "codex_science" / "experiment_planner.py" + text = path.read_text(encoding="utf-8") + old = ' claim_ids = sorted({_text(item, "claim_id") for item in payload.get("claim_ids", [])})\n' + new = ''' claim_ids_raw = payload.get("claim_ids", []) + if not isinstance(claim_ids_raw, list): + raise ValueError("claim_ids must be a list") + claim_ids = sorted({_text(item, "claim_id") for item in claim_ids_raw}) +''' + if old in text: + text = text.replace(old, new, 1) + text = text.replace( + ' scored.append((score, -front_rank[candidate["id"]], candidate["id"], candidate))\n', + ' scored.append((score, front_rank[candidate["id"]], candidate["id"], candidate))\n', + ) + path.write_text(text, encoding="utf-8") + + +def patch_fair_export() -> None: + path = ROOT / "src" / "codex_science" / "fair_export.py" + text = path.read_text(encoding="utf-8") + old_used = ''' for entity_id in entities: + if entity_id.startswith("input:"): + used[f"used:{activity_id}:{entity_id}"] = {"prov:activity": activity_id, "prov:entity": entity_id} +''' + new_used = ''' recorded_inputs = execution.get("inputs", execution.get("input_ids", [])) + if isinstance(recorded_inputs, list): + for value in recorded_inputs: + entity_id = f"input:{value}" + if entity_id in entities: + used[f"used:{activity_id}:{entity_id}"] = { + "prov:activity": activity_id, + "prov:entity": entity_id, + } +''' + if old_used in text: + text = text.replace(old_used, new_used, 1) + marker = ' activities[run_activity] = {"prov:type": "ScientificRun",' + if 'used[f"used:{run_activity}:{entity_id}"]' not in text: + line_end = text.index("\n", text.index(marker)) + 1 + addition = ''' for entity_id in entities: + if entity_id.startswith("input:"): + used[f"used:{run_activity}:{entity_id}"] = { + "prov:activity": run_activity, + "prov:entity": entity_id, + } +''' + text = text[:line_end] + addition + text[line_end:] + old_output = ''' output_dir = output_dir.resolve() + output_dir.mkdir(parents=True, exist_ok=True) +''' + new_output = ''' output_dir = output_dir.resolve() + if output_dir.exists() and any(output_dir.iterdir()): + raise ValueError(f"export output directory is not empty: {output_dir}") + output_dir.mkdir(parents=True, exist_ok=True) +''' + if old_output in text: + text = text.replace(old_output, new_output, 1) + path.write_text(text, encoding="utf-8") + + +def patch_references() -> None: + codex_index = ROOT / "skills" / "codex-science" / "references" / "index.json" + update_index( + codex_index, + { + "id": "pipeline-promotion", + "path": "references/pipeline-promotion.md", + "purpose": "Generate a non-active reusable skill draft from a currently passed run without transferring conclusions or review receipts.", + "read_when": ["before converting a reviewed run into a reusable workflow", "before generating a skill draft from artifacts"], + "required_before": ["calling scripts/promote_run_to_skill.py", "adding a generated draft to authored-skills or a catalog"], + "search_patterns": ["## Preconditions", "## Generated draft", "## Promotion path", "## Failure handling"], + "authority": "Codex Science first-party promotion contract", + "version": "1", + "evidence_boundary": "Promotion preserves workflow shape; it does not activate a skill or prove generalization." + }, + ) + update_index( + codex_index, + { + "id": "next-experiment-planning", + "path": "references/next-experiment-planning.md", + "purpose": "Deterministic Pareto, uncertainty, control, diversity, budget, and batch planning for a non-executing experiment proposal.", + "read_when": ["before ranking candidates for a new experiment", "before using model uncertainty or multi-objective utility for selection"], + "required_before": ["calling scripts/plan_next_experiment.py", "treating a selected batch as an approved experiment"], + "search_patterns": ["## Input contract", "## Selection method", "## Output contract", "## Failure handling"], + "authority": "Codex Science first-party experiment proposal contract", + "version": "1", + "evidence_boundary": "The planner ranks declared properties; it does not calibrate uncertainty, infer similarity, or execute experiments." + }, + ) + add_skill_reference( + ROOT / "skills" / "codex-science" / "SKILL.md", + "references/pipeline-promotion.md", + "Before turning a reviewed run into a reusable workflow, **MUST** read [the pipeline promotion contract](references/pipeline-promotion.md). `scripts/promote_run_to_skill.py` creates a non-active draft only; acceptance fixtures, seeded failures, independent review, maturity declaration, and catalog policy are still required.", + ) + add_skill_reference( + ROOT / "skills" / "codex-science" / "SKILL.md", + "references/next-experiment-planning.md", + "Before ranking compounds, assays, datasets, simulations, or other candidates for a next experiment, **MUST** read [the next-experiment planning contract](references/next-experiment-planning.md). Save objectives, controls, budget, diversity groups, uncertainty boundary, selected and rejected records, and approval state; a proposal is not an executed experiment.", + ) + + provenance_index = ROOT / "skills" / "science-provenance" / "references" / "index.json" + update_index( + provenance_index, + { + "id": "fair-export", + "path": "references/fair-export.md", + "purpose": "Export validated run metadata as RO-Crate-oriented, W3C PROV-oriented, and scientific dependency BOM records.", + "read_when": ["before archival or interoperable export", "before generating a scientific dependency or license handoff"], + "required_before": ["calling scripts/export_scientific_run.py", "describing an export as portable, FAIR, or compliance-supporting"], + "search_patterns": ["## RO-Crate-oriented metadata", "## W3C PROV-oriented lineage", "## Scientific dependency BOM", "## Failure handling"], + "authority": "Codex Science first-party export contract", + "version": "1", + "evidence_boundary": "The exporter packages recorded metadata; it does not certify completeness, licensing, regulatory compliance, or scientific validity." + }, + ) + add_skill_reference( + ROOT / "skills" / "science-provenance" / "SKILL.md", + "references/fair-export.md", + "Before exporting a run for archival, interoperability, dependency review, or compliance-supporting handoff, **MUST** read [the FAIR-oriented export contract](references/fair-export.md). Validate the source bundle first and preserve explicit non-certification, licensing, omitted-byte, and provenance-completeness boundaries.", + ) + + +def patch_quality_and_docs() -> None: + add_quality_outputs(ROOT / "skills" / "codex-science" / "quality.json", ["pipeline-promotion-v1", "experiment-proposal-v1"]) + add_quality_outputs(ROOT / "skills" / "science-provenance" / "quality.json", ["fair-export-receipt-v1", "scientific-bom-v1", "ro-crate-oriented-v1", "prov-oriented-v1"]) + quickstart = ROOT / "docs" / "NATIVE_RUNTIME_QUICKSTART.md" + text = quickstart.read_text(encoding="utf-8") + marker = "## 5. Promote, plan, and export" + if marker not in text: + text = text.rstrip() + ''' + +## 5. Promote, plan, and export + +Read `skills/codex-science/references/pipeline-promotion.md` before generating a skill draft with `scripts/promote_run_to_skill.py`. The generated draft is not active. + +Read `skills/codex-science/references/next-experiment-planning.md` before generating `experiment-proposal` records with `scripts/plan_next_experiment.py`. The proposal is not executed and always requires approval. + +Read `skills/science-provenance/references/fair-export.md` before using `scripts/export_scientific_run.py`. The export is metadata-oriented and explicitly does not certify regulatory, legal, or formal standards compliance. +''' + quickstart.write_text(text, encoding="utf-8") + platform = ROOT / "docs" / "PLATFORM_CONTRACTS.md" + text = platform.read_text(encoding="utf-8") + marker = "## Pipeline promotion, next experiments, and FAIR export" + if marker not in text: + platform.write_text(text.rstrip() + ''' + +## Pipeline promotion, next experiments, and FAIR export + +A passed run may be converted into a non-active skill draft with `scripts/promote_run_to_skill.py`. The draft preserves source manifest identity, argv contracts, explicit schemas, limitations, and a promotion checklist; it does not enter the catalog automatically. + +`scripts/plan_next_experiment.py` creates deterministic, non-executing proposals from declared objectives, uncertainty, controls, diversity groups, budget, and batch constraints. Selected and rejected records remain inspectable and approval is still required. + +`scripts/export_scientific_run.py` emits RO-Crate-oriented metadata, W3C PROV-oriented lineage, a scientific dependency BOM, and a hash-covered receipt from a validated bundle. The export does not certify formal conformance, licensing, regulatory compliance, or provenance completeness. +''', encoding="utf-8") + + +def main() -> int: + patch_pipeline_compiler() + patch_experiment_planner() + patch_fair_export() + patch_references() + patch_quality_and_docs() + subprocess.run([sys.executable, "-m", "compileall", "-q", "src", "scripts"], cwd=ROOT, check=True) + print("extended native runtime v4 integration: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From c5adbec6105eeb0ac2640e8e10d47108488ad6a0 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:37:22 +0900 Subject: [PATCH 63/87] Seal extended progressive scientific runtime v4 --- .../seal-extended-native-runtime-v4.yml | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/seal-extended-native-runtime-v4.yml diff --git a/.github/workflows/seal-extended-native-runtime-v4.yml b/.github/workflows/seal-extended-native-runtime-v4.yml new file mode 100644 index 00000000..ebb7fb8b --- /dev/null +++ b/.github/workflows/seal-extended-native-runtime-v4.yml @@ -0,0 +1,86 @@ +name: Seal extended native runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: seal-extended-native-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + seal: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 75 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Apply core native runtime integration + run: | + if [ -f scripts/seal_native_runtime_v4.py ]; then + uv run python scripts/seal_native_runtime_v4.py + elif [ -f scripts/apply_complete_native_runtime_v4.py ]; then + uv run python scripts/apply_complete_native_runtime_v4.py + elif [ -f scripts/integrate_native_runtime_v4.py ]; then + uv run python scripts/integrate_native_runtime_v4.py + fi + - name: Apply pipeline, experiment, and FAIR integration + run: uv run python scripts/integrate_extended_runtime_v4.py + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile all source + run: uv run python -m compileall -q src scripts + - name: Run native runtime tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' + uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' + uv run python -m unittest discover -s tests -p 'test_fair_export.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove all one-time integration resources + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f .github/workflows/seal-native-runtime-v4.yml + rm -f .github/workflows/seal-extended-native-runtime-v4.yml + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + rm -f scripts/repair_native_runtime_v4.py + rm -f scripts/seal_native_runtime_v4.py + rm -f scripts/integrate_extended_runtime_v4.py + - name: Commit clean extended implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Seal progressive references and extended scientific runtime v4" + git fetch origin "$branch" + git rebase "origin/$branch" + git push origin HEAD:"$branch" From 0ea99f9c998704652a87f561bb8d772cd1b52d94 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:38:29 +0900 Subject: [PATCH 64/87] Finish permanent native runtime v4 implementation --- .../workflows/finish-native-runtime-v4.yml | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .github/workflows/finish-native-runtime-v4.yml diff --git a/.github/workflows/finish-native-runtime-v4.yml b/.github/workflows/finish-native-runtime-v4.yml new file mode 100644 index 00000000..1f9e68e2 --- /dev/null +++ b/.github/workflows/finish-native-runtime-v4.yml @@ -0,0 +1,82 @@ +name: Finish native runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: finish-native-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + finish: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 75 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Apply all integration routes + run: | + uv run python scripts/seal_native_runtime_v4.py + uv run python scripts/integrate_extended_runtime_v4.py + - name: Remove one-time Python integration scripts before compilation + run: | + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + rm -f scripts/repair_native_runtime_v4.py + rm -f scripts/seal_native_runtime_v4.py + rm -f scripts/integrate_extended_runtime_v4.py + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile all permanent source + run: uv run python -m compileall -q src scripts + - name: Run native and extended runtime tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' + uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' + uv run python -m unittest discover -s tests -p 'test_fair_export.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove all one-time workflows + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f .github/workflows/seal-native-runtime-v4.yml + rm -f .github/workflows/seal-extended-native-runtime-v4.yml + rm -f .github/workflows/finish-native-runtime-v4.yml + - name: Commit permanent implementation only + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Finish progressive references and scientific runtime v4" + git fetch origin "$branch" + git rebase "origin/$branch" + git push origin HEAD:"$branch" From e4db614bf34ee022685dfebe2ad5befe75b08f3f Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:39:35 +0900 Subject: [PATCH 65/87] Add compile-safe permanent runtime patcher --- scripts/patch_permanent_runtime_v4.py | 215 ++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 scripts/patch_permanent_runtime_v4.py diff --git a/scripts/patch_permanent_runtime_v4.py b/scripts/patch_permanent_runtime_v4.py new file mode 100644 index 00000000..fc4ae7ac --- /dev/null +++ b/scripts/patch_permanent_runtime_v4.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +"""Apply native runtime integrations without compiling one-time helper scripts.""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from types import ModuleType + +ROOT = Path(__file__).resolve().parents[1] + + +def load_optional(path: Path, name: str) -> ModuleType | None: + if not path.is_file(): + return None + spec = importlib.util.spec_from_file_location(name, path) + if spec is None or spec.loader is None: + raise SystemExit(f"could not load integration module: {path}") + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + spec.loader.exec_module(module) + return module + + +def apply_optional(module: ModuleType | None, functions: tuple[str, ...]) -> None: + if module is None: + return + for name in functions: + function = getattr(module, name, None) + if function is None: + raise SystemExit(f"integration function is missing: {module.__name__}.{name}") + function() + + +def replace_region(path: Path, start_marker: str, end_marker: str, replacement: str) -> None: + text = path.read_text(encoding="utf-8") + start = text.index(start_marker) + end = text.index(end_marker, start) + path.write_text(text[:start] + replacement + text[end:], encoding="utf-8") + + +def patch_artifact_runtime() -> None: + path = ROOT / "src" / "codex_science" / "artifact_runtime.py" + text = path.read_text(encoding="utf-8") + old = " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n" + new = " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n" + if old in text: + path.write_text(text.replace(old, new, 1), encoding="utf-8") + elif new not in text: + raise SystemExit("artifact runtime descriptor marker is missing") + + +def patch_project_cli() -> None: + path = ROOT / "scripts" / "science_project.py" + text = path.read_text(encoding="utf-8") + if "import sqlite3\n" not in text: + text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) + text = text.replace( + " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", + " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", + ) + path.write_text(text, encoding="utf-8") + + +def patch_skill_maturity() -> None: + path = ROOT / "src" / "codex_science" / "skill_maturity.py" + frontmatter = '''def _frontmatter(text: str) -> dict[str, str]: + match = FRONTMATTER_RE.match(text) + if match is None: + return {} + result: dict[str, str] = {} + for item in FIELD_RE.finditer(match.group("body")): + value = item.group("value").strip() + if len(value) >= 2 and value[0] == value[-1] and value[0] in {chr(34), chr(39)}: + value = value[1:-1] + result[item.group("key")] = value + return result +''' + replace_region(path, "def _frontmatter", "\n\ndef _load_json", frontmatter) + text = path.read_text(encoding="utf-8") + text = text.replace(' entries = payload.get("references")\n', ' entries = payload.get("references", payload.get("entries"))\n') + old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" + has_review = "$science-review" in text or skill_dir.name == "science-review" +''' + new = ''' if skill_dir.name in CORE_SECTION_ALIASES: + # Core coordinator/provenance/reviewer skills collectively implement + # the handoff and are not required to invoke themselves by token. + has_provenance = True + has_review = True + else: + has_provenance = "$science-provenance" in text + has_review = "$science-review" in text +''' + if old in text: + text = text.replace(old, new, 1) + elif new not in text: + raise SystemExit("skill maturity handoff marker is missing") + path.write_text(text, encoding="utf-8") + + +def patch_remote_sections() -> None: + path = ROOT / "authored-skills" / "remote-scientific-compute" / "SKILL.md" + text = path.read_text(encoding="utf-8") + sections: list[str] = [] + if "## Decision contract" not in text: + sections.append("## Decision contract\n\nBefore execution, record the target, transfer boundary, argv or workflow, input hashes, runtime or container, resource and wall-time caps, cost cap, checkpoints, cancellation, outputs, and scientific acceptance criteria. Remote writes or allocation require explicit approval covering the exact job spec.") + if "## Outputs" not in text: + sections.append("## Outputs\n\nSave the job spec, preflight and approval receipts, job ID and states, stdout and stderr hashes, failure class, checkpoint references, collected output hashes, and `$science-provenance` and `$science-review` handoffs.") + if "## Boundaries" not in text: + sections.append("## Boundaries\n\nDo not probe credentials, submit to unapproved accounts or partitions, stage sensitive data without approval, busy-poll, hide failed attempts, or treat process exit zero as scientific success.") + if sections: + path.write_text(text.rstrip() + "\n\n" + "\n\n".join(sections) + "\n", encoding="utf-8") + + +def patch_local_submit() -> None: + path = ROOT / "src" / "codex_science" / "compute_backends.py" + replacement = ''' def submit(self, spec: JobSpec, *, approval: Mapping[str, Any] | None = None) -> dict[str, Any]: + if spec.backend != "local": + raise ValueError("LocalBackend requires backend=local") + preflight = self.preflight(spec) + if not preflight["ready"]: + raise ValueError("local preflight failed") + if spec.approval_required: + if approval is None: + raise ValueError("job spec requires explicit approval") + validate_approval_receipt(approval, spec) + job_id = "local-" + uuid.uuid4().hex[:20] + job_dir = self._job_dir(job_id) + job_dir.mkdir(parents=True) + _atomic_json(job_dir / "spec.json", spec.to_dict()) + submitted_at = _now() + initial = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at) + _atomic_json(job_dir / "state.json", initial) + worker_environment = os.environ.copy() + source_root = str(Path(__file__).resolve().parents[1]) + existing_pythonpath = worker_environment.get("PYTHONPATH", "") + worker_environment["PYTHONPATH"] = source_root + (os.pathsep + existing_pythonpath if existing_pythonpath else "") + process = subprocess.Popen( + [sys.executable, "-m", "codex_science.compute_backends", "_worker", str(job_dir)], + env=worker_environment, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + start_new_session=True, + close_fds=True, + ) + current = _read_json(job_dir / "state.json") + submitted_receipt = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + worker_pid=process.pid, + message="local worker started", + ) + if current.get("state") == "submitted": + _atomic_json(job_dir / "state.json", submitted_receipt) + return submitted_receipt +''' + text = path.read_text(encoding="utf-8") + local_start = text.index("class LocalBackend:") + submit_start = text.index(" def submit(", local_start) + status_start = text.index("\n def status(", submit_start) + path.write_text(text[:submit_start] + replacement + text[status_start:], encoding="utf-8") + + +def patch_advanced_sidecars() -> None: + path = ROOT / "src" / "codex_science" / "advanced_sidecars.py" + if not path.is_file(): + return + text = path.read_text(encoding="utf-8") + text = text.replace(' selections_by_id[str(selection["selection_id"])] = selection\n', ' selections_by_id[str(selection["selection_id"])] = payload\n') + path.write_text(text, encoding="utf-8") + + +def patch_project_branch() -> None: + path = ROOT / "src" / "codex_science" / "project_store.py" + text = path.read_text(encoding="utf-8") + old = ''' if branch is None: + connection.execute( + "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", + (project_id, branch_name, run_id, run_id, "active", imported_at, imported_at), + ) +''' + new = ''' if branch is None: + branch_base = parent_run_id or run_id + connection.execute( + "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", + (project_id, branch_name, branch_base, run_id, "active", imported_at, imported_at), + ) +''' + if old in text: + text = text.replace(old, new, 1) + path.write_text(text, encoding="utf-8") + + +def main() -> int: + native = load_optional(ROOT / "scripts" / "integrate_native_runtime_v4.py", "integrate_native_runtime_v4") + apply_optional(native, ("patch_reference_indexes_and_skills", "patch_source_bugs", "patch_advanced_sidecars", "patch_validation_gates", "patch_docs")) + extended = load_optional(ROOT / "scripts" / "integrate_extended_runtime_v4.py", "integrate_extended_runtime_v4") + apply_optional(extended, ("patch_pipeline_compiler", "patch_experiment_planner", "patch_fair_export", "patch_references", "patch_quality_and_docs")) + patch_artifact_runtime() + patch_project_cli() + patch_skill_maturity() + patch_remote_sections() + patch_local_submit() + patch_advanced_sidecars() + patch_project_branch() + print("permanent native runtime v4 patch: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 238d304a89f86cd56e3f1e70a0037c8b65d65cbb Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:39:54 +0900 Subject: [PATCH 66/87] Commit permanent progressive scientific runtime v4 --- .../workflows/commit-permanent-runtime-v4.yml | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .github/workflows/commit-permanent-runtime-v4.yml diff --git a/.github/workflows/commit-permanent-runtime-v4.yml b/.github/workflows/commit-permanent-runtime-v4.yml new file mode 100644 index 00000000..0e3cfdb3 --- /dev/null +++ b/.github/workflows/commit-permanent-runtime-v4.yml @@ -0,0 +1,82 @@ +name: Commit permanent native runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: commit-permanent-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + commit: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Apply source, reference, and validation integration + run: uv run python scripts/patch_permanent_runtime_v4.py + - name: Remove all one-time Python integration files before compilation + run: | + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + rm -f scripts/repair_native_runtime_v4.py + rm -f scripts/seal_native_runtime_v4.py + rm -f scripts/integrate_extended_runtime_v4.py + rm -f scripts/patch_permanent_runtime_v4.py + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile permanent source + run: uv run python -m compileall -q src scripts + - name: Run runtime and promotion tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' + uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' + uv run python -m unittest discover -s tests -p 'test_fair_export.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove all one-time integration workflows + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f .github/workflows/seal-native-runtime-v4.yml + rm -f .github/workflows/seal-extended-native-runtime-v4.yml + rm -f .github/workflows/finish-native-runtime-v4.yml + rm -f .github/workflows/commit-permanent-runtime-v4.yml + - name: Commit permanent implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Commit progressive references and complete scientific runtime v4" + git fetch origin "$branch" + git rebase "origin/$branch" + git push origin HEAD:"$branch" From fcbcfd1510970f59f6b2bae8bee20260ea188a8a Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:42:02 +0900 Subject: [PATCH 67/87] Harden generated pipeline drafts against secret propagation --- scripts/harden_extended_runtime_v4.py | 100 ++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 scripts/harden_extended_runtime_v4.py diff --git a/scripts/harden_extended_runtime_v4.py b/scripts/harden_extended_runtime_v4.py new file mode 100644 index 00000000..da0e3901 --- /dev/null +++ b/scripts/harden_extended_runtime_v4.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Harden generated pipeline drafts against secret and private-input propagation.""" + +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def main() -> int: + path = ROOT / "src" / "codex_science" / "pipeline_compiler.py" + text = path.read_text(encoding="utf-8") + if "SECRET_ARGUMENT_FLAGS" not in text: + marker = 'SKILL_NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")\n' + addition = '''SKILL_NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$") +SECRET_ARGUMENT_FLAGS = { + "--token", "--password", "--secret", "--credential", "--api-key", + "--apikey", "--private-key", "--access-key", "--client-secret" +} +SECRET_KEY_FRAGMENTS = { + "token", "password", "secret", "credential", "private_key", "api_key", + "apikey", "access_key", "client_secret" +} +''' + if marker not in text: + raise SystemExit("pipeline compiler constant marker is missing") + text = text.replace(marker, addition, 1) + if "def _redact_mapping" not in text: + marker = "\ndef _write_json(path: Path, payload: Any) -> None:\n" + helper = ''' +def _redact_mapping(value: Any) -> Any: + if isinstance(value, Mapping): + result: dict[str, Any] = {} + for key, item in value.items(): + key_text = str(key) + if any(fragment in key_text.lower() for fragment in SECRET_KEY_FRAGMENTS): + result[key_text] = "[REDACTED]" + else: + result[key_text] = _redact_mapping(item) + return result + if isinstance(value, list): + return [_redact_mapping(item) for item in value] + return value + + +def _validate_command(command: list[str], index: int) -> None: + previous = "" + for argument in command: + lower = argument.lower() + if previous in SECRET_ARGUMENT_FLAGS: + raise ValueError(f"command_contract[{index}] contains a credential value after {previous}") + if any(lower == flag or lower.startswith(flag + "=") for flag in SECRET_ARGUMENT_FLAGS): + if "=" in argument: + raise ValueError(f"command_contract[{index}] contains a credential-bearing argument") + previous = lower + continue + if "://" in argument and any( + token in lower + for token in ("token=", "password=", "secret=", "api_key=", "apikey=", "access_key=") + ): + raise ValueError(f"command_contract[{index}] contains a credential-bearing URL") + previous = lower + +''' + if marker not in text: + raise SystemExit("pipeline compiler helper marker is missing") + text = text.replace(marker, helper + marker, 1) + text = text.replace( + ' "x-source-run-inputs": list(manifest.get("inputs", []))\n', + ' "x-source-run-input-count": len(manifest.get("inputs", [])),\n "x-source-run-input-fields": sorted({str(key) for item in manifest.get("inputs", []) if isinstance(item, Mapping) for key in item.keys()})\n', + ) + text = text.replace( + ' safe_description = description.replace(chr(34), chr(39))\n', + ' safe_description = " ".join(description.replace(chr(34), chr(39)).split())\n', + ) + text = text.replace( + "json.dumps(manifest.get('environment', {}), indent=2, sort_keys=True, ensure_ascii=False)", + "json.dumps(_redact_mapping(manifest.get('environment', {})), indent=2, sort_keys=True, ensure_ascii=False)", + ) + old = ''' for index, command in enumerate(commands): + if not isinstance(command, list) or not command or not all(isinstance(item, str) and item for item in command): + raise ValueError(f"command_contract[{index}] must be a non-empty argv list") +''' + new = ''' for index, command in enumerate(commands): + if not isinstance(command, list) or not command or not all(isinstance(item, str) and item for item in command): + raise ValueError(f"command_contract[{index}] must be a non-empty argv list") + _validate_command(command, index) +''' + if old in text: + text = text.replace(old, new, 1) + elif "_validate_command(command, index)" not in text: + raise SystemExit("pipeline command validation marker is missing") + path.write_text(text, encoding="utf-8") + print("extended runtime hardening: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 4ad92c55f733b1c5240976918e458499512e8ac2 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:42:25 +0900 Subject: [PATCH 68/87] Test generated pipeline secret redaction and rejection --- tests/test_pipeline_compiler_security.py | 87 ++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/test_pipeline_compiler_security.py diff --git a/tests/test_pipeline_compiler_security.py b/tests/test_pipeline_compiler_security.py new file mode 100644 index 00000000..9c7db637 --- /dev/null +++ b/tests/test_pipeline_compiler_security.py @@ -0,0 +1,87 @@ +import hashlib +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.artifacts import add_artifact, new_manifest, write_manifest +from codex_science.pipeline_compiler import compile_pipeline_draft + + +def make_run(root: Path) -> Path: + run = root / "run" + run.mkdir() + output = run / "result.json" + output.write_text('{"ok": true}\n', encoding="utf-8") + manifest = new_manifest( + "secure-source-run", + "Can the reviewed workflow be represented without copying private inputs?", + [{"id": "run", "description": "Run fixture", "status": "completed"}], + ) + manifest["inputs"] = [ + { + "id": "private-input", + "path": "/private/patient/data.csv", + "sha256": "a" * 64, + "token": "must-not-propagate" + } + ] + manifest["environment"] = { + "python": "3.12", + "API_TOKEN": "must-not-propagate", + "nested": {"client_secret": "must-not-propagate", "threads": 4} + } + manifest["claims"] = [{"id": "C1", "text": "The fixture completed.", "evidence": ["result.json"]}] + manifest["review"] = {"status": "passed", "findings": []} + add_artifact(manifest, "result.json", kind="result", sha256=hashlib.sha256(output.read_bytes()).hexdigest()) + path = run / "manifest.json" + write_manifest(manifest, path) + return path + + +class PipelineCompilerSecurityTests(unittest.TestCase): + def test_private_input_values_and_environment_secrets_are_not_copied(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + manifest = make_run(root) + output = root / "draft" + compile_pipeline_draft( + manifest_path=manifest, + output_dir=output, + name="secure-draft", + description="Secure draft.", + command_contract=[["python", "analysis.py"]], + generated_at="2026-07-21T00:00:00Z", + ) + schema = (output / "input.schema.json").read_text(encoding="utf-8") + reference = (output / "references" / "pipeline.md").read_text(encoding="utf-8") + self.assertNotIn("/private/patient/data.csv", schema) + self.assertNotIn("must-not-propagate", schema) + self.assertNotIn("must-not-propagate", reference) + self.assertIn("[REDACTED]", reference) + payload = json.loads(schema) + self.assertEqual(1, payload["x-source-run-input-count"]) + self.assertIn("path", payload["x-source-run-input-fields"]) + + def test_credential_bearing_argv_and_urls_fail_closed(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + manifest = make_run(root) + cases = [ + [["tool", "--token", "secret-value"]], + [["tool", "--api-key=secret-value"]], + [["curl", "https://example.org/data?token=secret-value"]] + ] + for index, commands in enumerate(cases): + with self.subTest(commands=commands), self.assertRaisesRegex(ValueError, "credential"): + compile_pipeline_draft( + manifest_path=manifest, + output_dir=root / f"draft-{index}", + name=f"secure-draft-{index}", + description="Secure draft.", + command_contract=commands, + ) + + +if __name__ == "__main__": + unittest.main() From 07255500fcb607163b303620b96bdebe05e4c3ad Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:42:46 +0900 Subject: [PATCH 69/87] Finalize complete progressive scientific runtime v4 --- .github/workflows/finalize-all-runtime-v4.yml | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/finalize-all-runtime-v4.yml diff --git a/.github/workflows/finalize-all-runtime-v4.yml b/.github/workflows/finalize-all-runtime-v4.yml new file mode 100644 index 00000000..2f062852 --- /dev/null +++ b/.github/workflows/finalize-all-runtime-v4.yml @@ -0,0 +1,87 @@ +name: Finalize all scientific runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: finalize-all-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + finalize: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Apply permanent source and reference patches + run: uv run python scripts/patch_permanent_runtime_v4.py + - name: Harden pipeline draft generation + run: uv run python scripts/harden_extended_runtime_v4.py + - name: Remove all one-time Python files before compilation + run: | + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + rm -f scripts/repair_native_runtime_v4.py + rm -f scripts/seal_native_runtime_v4.py + rm -f scripts/integrate_extended_runtime_v4.py + rm -f scripts/patch_permanent_runtime_v4.py + rm -f scripts/harden_extended_runtime_v4.py + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile permanent source + run: uv run python -m compileall -q src scripts + - name: Run focused runtime tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler_security.py' + uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' + uv run python -m unittest discover -s tests -p 'test_fair_export.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove all one-time integration workflows + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f .github/workflows/seal-native-runtime-v4.yml + rm -f .github/workflows/seal-extended-native-runtime-v4.yml + rm -f .github/workflows/finish-native-runtime-v4.yml + rm -f .github/workflows/commit-permanent-runtime-v4.yml + rm -f .github/workflows/finalize-all-runtime-v4.yml + - name: Commit clean permanent implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Finalize progressive references and scientific runtime v4" + git fetch origin "$branch" + git rebase "origin/$branch" + git push origin HEAD:"$branch" From d4796f447a55fd1047fec4935eef5788f39b470d Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:44:40 +0900 Subject: [PATCH 70/87] Add blinded independent reviewer packet runtime --- src/codex_science/reviewer_runtime.py | 292 ++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 src/codex_science/reviewer_runtime.py diff --git a/src/codex_science/reviewer_runtime.py b/src/codex_science/reviewer_runtime.py new file mode 100644 index 00000000..50364527 --- /dev/null +++ b/src/codex_science/reviewer_runtime.py @@ -0,0 +1,292 @@ +"""Blinded scientific review packets and hash-covered review response intake. + +The runtime prepares deterministic packets for a separate human or agent. It +never sends data to a model service, claims reviewer independence, or exposes +private chain-of-thought. Reviewer identity and independence remain attestations +that must be governed by the calling system. +""" + +from __future__ import annotations + +import hashlib +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Mapping + +from codex_science.artifact_store import stream_sha256 +from codex_science.artifacts import validate_bundle +from codex_science.review_receipts import build_review_receipt, validate_review_receipt + + +REVIEW_MODES = {"record", "source", "method", "reproduction"} +SEVERITIES = {"critical", "major", "minor", "suggestion"} +SENSITIVE_FRAGMENTS = {"token", "password", "secret", "credential", "private_key", "api_key", "apikey", "chain_of_thought", "hidden_rationale"} +EXCLUDED_PRODUCER_FIELDS = {"intended_conclusion", "producer_rationale", "chain_of_thought", "private_scratchpad", "hidden_answer", "suspected_bug"} + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _fingerprint(value: Mapping[str, Any]) -> str: + return hashlib.sha256(_canonical_bytes(value)).hexdigest() + + +def _text(value: Any, label: str) -> str: + text = str(value).strip() + if not text: + raise ValueError(f"{label} is required") + return text + + +def _sha(value: Any, label: str) -> str: + text = _text(value, label).lower() + if len(text) != 64 or any(character not in "0123456789abcdef" for character in text): + raise ValueError(f"{label} must be a SHA-256 digest") + return text + + +def _redact(value: Any) -> Any: + if isinstance(value, Mapping): + result: dict[str, Any] = {} + for key, item in value.items(): + key_text = str(key) + normalized = key_text.lower() + if key_text in EXCLUDED_PRODUCER_FIELDS: + continue + if any(fragment in normalized for fragment in SENSITIVE_FRAGMENTS): + result[key_text] = "[REDACTED]" + else: + result[key_text] = _redact(item) + return result + if isinstance(value, list): + return [_redact(item) for item in value] + return value + + +def _material_claim_ids(manifest: Mapping[str, Any], sidecars: Mapping[str, Any]) -> list[str]: + claim_register = sidecars.get("claim_by_id") + if isinstance(claim_register, Mapping) and claim_register: + return sorted(str(key) for key in claim_register) + return sorted(str(item.get("id")) for item in manifest.get("claims", []) if isinstance(item, Mapping) and item.get("id")) + + +def build_review_packet( + manifest_path: Path, + *, + review_modes: list[str], + independent_required: bool = True, + review_questions: list[str] | None = None, + created_at: str | None = None, +) -> dict[str, Any]: + manifest_path = manifest_path.resolve() + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + if not isinstance(manifest, dict): + raise ValueError("manifest must be an object") + sidecars = validate_bundle(manifest, manifest_path.parent) + modes = sorted({_text(item, "review mode") for item in review_modes}) + unknown = sorted(set(modes) - REVIEW_MODES) + if unknown or not modes: + raise ValueError(f"invalid or empty review modes: {', '.join(unknown)}") + digest, _size = stream_sha256(manifest_path) + artifact_hashes = [ + {"path": str(item["path"]), "sha256": str(item["sha256"]).lower(), "kind": str(item.get("kind", "artifact"))} + for item in manifest.get("artifacts", []) + if isinstance(item, Mapping) + ] + claims = sidecars.get("claim_register", {}).get("claims") if isinstance(sidecars.get("claim_register"), Mapping) else None + if not isinstance(claims, list): + claims = [dict(item) for item in manifest.get("claims", []) if isinstance(item, Mapping)] + graph = sidecars.get("graph_v2") or sidecars.get("evidence_graph") + questions = review_questions or [ + "Does each material claim have direct, correctly attributed evidence?", + "Do the design, controls, splits, metrics, uncertainty, and inference level support the claim?", + "Are duplicated sources, shared cohorts or samples, and model-training overlap represented correctly?", + "Do reported figures, tables, and values agree with the saved artifacts and execution record?", + "Which alternative explanations or counterevidence remain unresolved?" + ] + material = { + "schema_version": 1, + "source_run_id": str(manifest["run_id"]), + "source_manifest_path": str(manifest_path), + "source_manifest_sha256": digest, + "created_at": created_at or _now(), + "review_modes": modes, + "independent_required": bool(independent_required), + "producer_context_excluded": sorted(EXCLUDED_PRODUCER_FIELDS), + "decision_contract": { + "question": str(manifest.get("question", "")), + "plan": _redact(manifest.get("plan", [])), + "inputs": _redact(manifest.get("inputs", [])), + "code": _redact(manifest.get("code", [])), + "executions": _redact(manifest.get("executions", [])), + "environment": _redact(manifest.get("environment", {})), + }, + "claims": _redact(claims), + "material_claim_ids": _material_claim_ids(manifest, sidecars), + "artifacts": artifact_hashes, + "evidence_graph": _redact(graph) if isinstance(graph, Mapping) else None, + "study_table": _redact(sidecars.get("study_table")) if sidecars.get("study_table") else None, + "query_records": _redact(sidecars.get("query_records", [])), + "lane_receipts": _redact(sidecars.get("lane_receipts", [])), + "model_receipts": _redact(sidecars.get("model_receipts_v2", sidecars.get("model_receipts", []))), + "review_questions": [_text(item, "review question") for item in questions], + "response_contract": { + "reviewer": "required", + "independent": "boolean attestation", + "review_modes": modes, + "reviewed_claim_ids": "all material claims unless an explicit blocked reason is supplied", + "reviewed_artifacts": "path and SHA-256 pairs from this packet", + "findings": "stable ID, severity, code, message, evidence, required action, and resolution status", + "limitations": "required" + }, + "evidence_boundary": "This packet exposes recorded run evidence while excluding producer-only rationale fields. It does not authenticate the reviewer, guarantee true independence, or include unrecorded evidence." + } + fingerprint = _fingerprint(material) + return {**material, "review_task_id": f"review-task-{fingerprint[:20]}", "fingerprint": fingerprint} + + +def validate_review_packet(payload: Mapping[str, Any]) -> None: + if payload.get("schema_version") != 1: + raise ValueError("unsupported review packet schema") + for field in ("review_task_id", "source_run_id", "source_manifest_path", "source_manifest_sha256", "created_at", "evidence_boundary"): + _text(payload.get(field), field) + _sha(payload.get("source_manifest_sha256"), "source_manifest_sha256") + modes = payload.get("review_modes") + if not isinstance(modes, list) or not modes or set(modes) - REVIEW_MODES: + raise ValueError("invalid review packet modes") + if not isinstance(payload.get("artifacts"), list) or not isinstance(payload.get("material_claim_ids"), list): + raise ValueError("review packet artifacts and material_claim_ids must be lists") + material = dict(payload) + task_id = str(material.pop("review_task_id")) + fingerprint = str(material.pop("fingerprint", "")).lower() + if _fingerprint(material) != fingerprint or task_id != f"review-task-{fingerprint[:20]}": + raise ValueError("review packet fingerprint or task ID mismatch") + + +def _validate_finding(item: Mapping[str, Any], index: int) -> dict[str, Any]: + finding_id = _text(item.get("id"), f"finding {index} id") + severity = _text(item.get("severity"), f"finding {finding_id} severity") + if severity not in SEVERITIES: + raise ValueError(f"invalid finding severity: {severity}") + code = _text(item.get("code"), f"finding {finding_id} code") + message = _text(item.get("message", item.get("rationale")), f"finding {finding_id} message") + resolution = str(item.get("resolution_status", "open")) + if resolution not in {"open", "resolved", "accepted-risk", "not-applicable"}: + raise ValueError(f"invalid finding resolution status: {resolution}") + evidence = item.get("evidence", []) + if not isinstance(evidence, list): + raise ValueError(f"finding {finding_id} evidence must be a list") + required_action = str(item.get("required_action", "")).strip() + return { + **dict(item), + "id": finding_id, + "severity": severity, + "code": code, + "message": message, + "resolution_status": resolution, + "evidence": evidence, + "required_action": required_action, + } + + +def finalize_review_response(packet: Mapping[str, Any], response: Mapping[str, Any]) -> dict[str, Any]: + validate_review_packet(packet) + if response.get("schema_version", 1) != 1: + raise ValueError("unsupported review response schema") + if response.get("review_task_id") != packet["review_task_id"] or response.get("packet_fingerprint") != packet["fingerprint"]: + raise ValueError("review response covers a different packet") + reviewer = _text(response.get("reviewer"), "reviewer") + independent = response.get("independent") + if not isinstance(independent, bool): + raise ValueError("independent must be a boolean attestation") + response_modes = sorted({_text(item, "response review mode") for item in response.get("review_modes", [])}) + if not response_modes or set(response_modes) - set(packet["review_modes"]): + raise ValueError("response review modes are empty or exceed the packet") + reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in response.get("reviewed_claim_ids", [])}) + material_claims = set(map(str, packet["material_claim_ids"])) + unknown_claims = sorted(set(reviewed_claims) - material_claims) + if unknown_claims: + raise ValueError(f"response references unknown claims: {', '.join(unknown_claims)}") + blocked_reason = str(response.get("blocked_reason", "")).strip() + missing_claims = sorted(material_claims - set(reviewed_claims)) + if missing_claims and not blocked_reason: + raise ValueError(f"review omits material claims without blocked_reason: {', '.join(missing_claims)}") + packet_artifacts = {str(item["path"]): str(item["sha256"]).lower() for item in packet["artifacts"]} + reviewed_artifacts_raw = response.get("reviewed_artifacts", []) + if not isinstance(reviewed_artifacts_raw, list): + raise ValueError("reviewed_artifacts must be a list") + covered_artifacts: list[dict[str, str]] = [] + for index, item in enumerate(reviewed_artifacts_raw): + if not isinstance(item, Mapping): + raise ValueError(f"reviewed artifact {index} must be an object") + path = _text(item.get("path"), f"reviewed artifact {index} path") + digest = _sha(item.get("sha256"), f"reviewed artifact {path} sha256") + if packet_artifacts.get(path) != digest: + raise ValueError(f"reviewed artifact does not match packet: {path}") + covered_artifacts.append({"path": path, "sha256": digest}) + if not covered_artifacts: + raise ValueError("review response must cover at least one artifact") + findings_raw = response.get("findings", []) + if not isinstance(findings_raw, list): + raise ValueError("findings must be a list") + findings = [_validate_finding(item, index) for index, item in enumerate(findings_raw) if isinstance(item, Mapping)] + if len(findings) != len(findings_raw): + raise ValueError("all findings must be objects") + if packet.get("independent_required") and not independent: + findings.append({ + "id": "F-review-independence", + "severity": "major", + "code": "review-not-independent", + "message": "The review packet required independence but the reviewer attested independent=false.", + "resolution_status": "open", + "evidence": [], + "required_action": "Obtain a separate reviewer or explicitly downgrade the completion claim." + }) + if missing_claims: + findings.append({ + "id": "F-review-coverage", + "severity": "major", + "code": "incomplete-review-coverage", + "message": f"Material claims were not reviewed: {', '.join(missing_claims)}. Blocked reason: {blocked_reason}", + "resolution_status": "open", + "evidence": [], + "required_action": "Resolve the blocker or review the missing claims." + }) + unresolved_blocking = any(item["severity"] in {"critical", "major"} and item["resolution_status"] not in {"resolved", "not-applicable"} for item in findings) + requested_status = str(response.get("status", "findings")) + if requested_status not in {"passed", "findings", "blocked"}: + raise ValueError("invalid review response status") + status = "findings" if unresolved_blocking else requested_status + if status == "passed" and (missing_claims or (packet.get("independent_required") and not independent)): + status = "findings" + limitations = response.get("limitations", []) + if not isinstance(limitations, list) or not all(isinstance(item, str) and item.strip() for item in limitations): + raise ValueError("limitations must be a non-empty string list") + if not limitations: + raise ValueError("review response must state limitations") + receipt = build_review_receipt( + review_id=f"review-{str(packet['review_task_id']).removeprefix('review-task-')}", + reviewer=reviewer, + independent=independent, + review_modes=response_modes, + status=status, + covered_artifacts=covered_artifacts, + covered_claim_ids=reviewed_claims, + findings=findings, + covered_registry_sha256=None if response.get("covered_registry_sha256") is None else _sha(response["covered_registry_sha256"], "covered_registry_sha256"), + limitations=list(limitations), + ) + receipt["review_task_id"] = packet["review_task_id"] + receipt["packet_fingerprint"] = packet["fingerprint"] + # Rebuild the receipt fingerprint after adding packet linkage. + material = dict(receipt) + material.pop("fingerprint", None) + receipt["fingerprint"] = _fingerprint(material) + validate_review_receipt(receipt) + return receipt From 47a78bdf9c537a85cacd3fd692503fd79270bbdb Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:44:59 +0900 Subject: [PATCH 71/87] Add independent reviewer packet CLI --- scripts/science_reviewer.py | 71 +++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 scripts/science_reviewer.py diff --git a/scripts/science_reviewer.py b/scripts/science_reviewer.py new file mode 100644 index 00000000..a36528a6 --- /dev/null +++ b/scripts/science_reviewer.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Prepare a blinded review packet or finalize a hash-covered review response.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.reviewer_runtime import build_review_packet, finalize_review_response # noqa: E402 + + +def read_object(path: Path) -> dict: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"JSON object required: {path}") + return payload + + +def write(path: Path, payload: dict) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + print(f"Success! Data written to: {path.resolve()}") + + +def prepare(args: argparse.Namespace) -> None: + packet = build_review_packet( + args.manifest, + review_modes=args.mode, + independent_required=not args.independence_not_required, + review_questions=args.question or None, + ) + write(args.output, packet) + + +def finalize(args: argparse.Namespace) -> None: + packet = read_object(args.packet) + response = read_object(args.response) + write(args.output, finalize_review_response(packet, response)) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + command = commands.add_parser("prepare") + command.add_argument("manifest", type=Path) + command.add_argument("--mode", action="append", choices=["record", "source", "method", "reproduction"], required=True) + command.add_argument("--question", action="append", default=[]) + command.add_argument("--independence-not-required", action="store_true") + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=prepare) + command = commands.add_parser("finalize") + command.add_argument("packet", type=Path) + command.add_argument("response", type=Path) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=finalize) + args = parser.parse_args() + try: + args.handler(args) + except (OSError, ValueError, json.JSONDecodeError) as error: + print(f"error: {error}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From b6a6799a8f3dec338e8d211d79f657a701ece89b Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:45:31 +0900 Subject: [PATCH 72/87] Test blinded reviewer packet and receipt runtime --- tests/test_reviewer_runtime.py | 137 +++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 tests/test_reviewer_runtime.py diff --git a/tests/test_reviewer_runtime.py b/tests/test_reviewer_runtime.py new file mode 100644 index 00000000..84bc3940 --- /dev/null +++ b/tests/test_reviewer_runtime.py @@ -0,0 +1,137 @@ +import hashlib +import json +import tempfile +import unittest +from pathlib import Path + +from codex_science.artifacts import add_artifact, new_manifest, write_manifest +from codex_science.reviewer_runtime import build_review_packet, finalize_review_response, validate_review_packet +from codex_science.review_receipts import validate_review_receipt + + +def make_reviewed_run(root: Path) -> Path: + run = root / "run" + run.mkdir() + evidence = run / "evidence.json" + evidence.write_text('{"estimate": 0.72}\n', encoding="utf-8") + digest = hashlib.sha256(evidence.read_bytes()).hexdigest() + manifest = new_manifest( + "review-source-run", + "Does the evidence support the bounded association claim?", + [{"id": "analyze", "description": "Analyze the recorded evidence", "status": "completed"}], + ) + manifest["inputs"] = [{"id": "study", "identifier": "pmid:12345678", "token": "must-redact"}] + manifest["code"] = [{"path": "analysis.py", "revision": "abc"}] + manifest["executions"] = [{"command": "python analysis.py", "exit_code": 0}] + manifest["environment"] = {"python": "3.12", "API_TOKEN": "must-redact"} + manifest["claims"] = [{"id": "C1", "text": "The recorded study supports an association.", "evidence": ["evidence.json"], "producer_rationale": "must-exclude"}] + manifest["review"] = {"status": "passed", "findings": []} + add_artifact(manifest, "evidence.json", kind="study-evidence", sha256=digest) + path = run / "manifest.json" + write_manifest(manifest, path) + return path + + +class ReviewerRuntimeTests(unittest.TestCase): + def test_packet_is_hash_bound_and_excludes_producer_only_context(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + manifest = make_reviewed_run(Path(tempdir)) + packet = build_review_packet( + manifest, + review_modes=["record", "method"], + created_at="2026-07-21T00:00:00Z", + ) + validate_review_packet(packet) + serialized = json.dumps(packet) + self.assertNotIn("must-exclude", serialized) + self.assertNotIn("must-redact", serialized) + self.assertIn("[REDACTED]", serialized) + self.assertEqual(["C1"], packet["material_claim_ids"]) + self.assertEqual(1, len(packet["artifacts"])) + self.assertTrue(packet["independent_required"]) + + def test_complete_independent_response_builds_passed_receipt(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + manifest = make_reviewed_run(Path(tempdir)) + packet = build_review_packet( + manifest, + review_modes=["record", "method"], + created_at="2026-07-21T00:00:00Z", + ) + artifact = packet["artifacts"][0] + response = { + "schema_version": 1, + "review_task_id": packet["review_task_id"], + "packet_fingerprint": packet["fingerprint"], + "reviewer": "independent-reviewer-1", + "independent": True, + "review_modes": ["record", "method"], + "reviewed_claim_ids": ["C1"], + "reviewed_artifacts": [{"path": artifact["path"], "sha256": artifact["sha256"]}], + "findings": [], + "limitations": ["The source study is not independently reproduced in this review."], + "status": "passed" + } + receipt = finalize_review_response(packet, response) + validate_review_receipt(receipt) + self.assertEqual("passed", receipt["status"]) + self.assertTrue(receipt["independent"]) + self.assertEqual(packet["review_task_id"], receipt["review_task_id"]) + + def test_nonindependent_or_incomplete_review_cannot_pass(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + manifest = make_reviewed_run(Path(tempdir)) + packet = build_review_packet( + manifest, + review_modes=["record"], + created_at="2026-07-21T00:00:00Z", + ) + artifact = packet["artifacts"][0] + response = { + "schema_version": 1, + "review_task_id": packet["review_task_id"], + "packet_fingerprint": packet["fingerprint"], + "reviewer": "producer-second-pass", + "independent": False, + "review_modes": ["record"], + "reviewed_claim_ids": [], + "reviewed_artifacts": [{"path": artifact["path"], "sha256": artifact["sha256"]}], + "findings": [], + "limitations": ["Same producer context."], + "blocked_reason": "No independent reviewer was available.", + "status": "passed" + } + receipt = finalize_review_response(packet, response) + self.assertEqual("findings", receipt["status"]) + codes = {item["code"] for item in receipt["findings"]} + self.assertIn("review-not-independent", codes) + self.assertIn("incomplete-review-coverage", codes) + + def test_response_cannot_substitute_artifact_hash_or_unknown_claim(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + manifest = make_reviewed_run(Path(tempdir)) + packet = build_review_packet(manifest, review_modes=["record"], created_at="2026-07-21T00:00:00Z") + base = { + "schema_version": 1, + "review_task_id": packet["review_task_id"], + "packet_fingerprint": packet["fingerprint"], + "reviewer": "reviewer", + "independent": True, + "review_modes": ["record"], + "reviewed_claim_ids": ["C1"], + "reviewed_artifacts": [{"path": packet["artifacts"][0]["path"], "sha256": "0" * 64}], + "findings": [], + "limitations": ["Limited."], + "status": "passed" + } + with self.assertRaisesRegex(ValueError, "does not match"): + finalize_review_response(packet, base) + changed = dict(base) + changed["reviewed_claim_ids"] = ["UNKNOWN"] + changed["reviewed_artifacts"] = [{"path": packet["artifacts"][0]["path"], "sha256": packet["artifacts"][0]["sha256"]}] + with self.assertRaisesRegex(ValueError, "unknown claims"): + finalize_review_response(packet, changed) + + +if __name__ == "__main__": + unittest.main() From 8c52f1df28568aa3cd6c7b43e3f2315ee820dd2a Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:46:09 +0900 Subject: [PATCH 73/87] Document independent reviewer packet usage --- .../references/independent-review-runtime.md | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 skills/science-review/references/independent-review-runtime.md diff --git a/skills/science-review/references/independent-review-runtime.md b/skills/science-review/references/independent-review-runtime.md new file mode 100644 index 00000000..7e8eb4bd --- /dev/null +++ b/skills/science-review/references/independent-review-runtime.md @@ -0,0 +1,224 @@ +# Independent reviewer packet runtime + +Read this reference before preparing a separate human or agent review, handing artifacts to a reviewer, accepting a review response, or calling a review independent. + +## Independence boundary + +The runtime creates a deterministic packet and validates the response. It does not authenticate the reviewer or prove true organizational independence. + +`independent: true` is an attestation by the calling workflow. Governance must ensure the reviewer: + +- did not produce the material result; +- did not share hidden producer rationale or intended conclusion; +- receives the approved decision contract and recorded evidence rather than a desired answer; +- uses a separate workspace or context; +- has no undisclosed conflict that changes trust in the review. + +A second pass by the producer must use `independent: false`. + +## Prepare a review packet + +```bash +python scripts/science_reviewer.py prepare \ + artifacts/run-014/manifest.json \ + --mode record \ + --mode source \ + --mode method \ + --question "Does every numerical claim agree with the saved table?" \ + --question "Are shared cohorts counted as one evidence dependency?" \ + --output artifacts/run-014/review-task.json +``` + +Use `--mode reproduction` only when the reviewer will actually execute specified computations in a separate workspace. + +The packet includes: + +- source run and manifest SHA-256; +- approved question and plan; +- input, code, execution, and environment records; +- material claims; +- artifact path/hash/kind list; +- evidence graph, study table, query records, lane receipts, and model receipts when present; +- review questions; +- response contract; +- packet fingerprint and task ID. + +The packet excludes fields named like: + +```text +intended_conclusion +producer_rationale +chain_of_thought +private_scratchpad +hidden_answer +suspected_bug +``` + +Secret-like keys are redacted. The runtime does not extract or transmit private chain-of-thought. + +## Packet identity + +```json +{ + "review_task_id": "review-task-...", + "source_run_id": "run-014", + "source_manifest_sha256": "...", + "review_modes": ["method", "record", "source"], + "independent_required": true, + "material_claim_ids": ["C1", "C2"], + "artifacts": [ + {"path": "metrics.json", "sha256": "...", "kind": "metrics"} + ], + "fingerprint": "..." +} +``` + +Changing the source manifest, review modes, questions, claims, artifact hashes, or packet content creates a different fingerprint and task ID. + +## Reviewer response contract + +The reviewer returns JSON: + +```json +{ + "schema_version": 1, + "review_task_id": "review-task-...", + "packet_fingerprint": "...", + "reviewer": "reviewer-agent-2", + "independent": true, + "review_modes": ["record", "method"], + "reviewed_claim_ids": ["C1", "C2"], + "reviewed_artifacts": [ + {"path": "metrics.json", "sha256": "..."} + ], + "findings": [ + { + "id": "F-001", + "severity": "major", + "code": "wrong-denominator", + "message": "The report uses enrolled participants instead of analyzed participants.", + "affected_claim_id": "C2", + "evidence": [ + { + "artifact_path": "study-table.json", + "artifact_sha256": "...", + "json_pointer": "/studies/3/analyzed_n" + } + ], + "required_action": "Recalculate the estimate and update the claim.", + "resolution_status": "open" + } + ], + "limitations": [ + "The raw instrument files were unavailable." + ], + "status": "findings" +} +``` + +Finding severities: + +```text +critical +major +minor +suggestion +``` + +Resolution states: + +```text +open +resolved +accepted-risk +not-applicable +``` + +A finding should identify exact evidence whenever possible: artifact path and hash plus page, table, figure, cell, JSON pointer, record ID, line range, or execution ID. + +## Finalize a response + +```bash +python scripts/science_reviewer.py finalize \ + artifacts/run-014/review-task.json \ + artifacts/run-014/reviewer-response.json \ + --output artifacts/run-014/review-receipt-v2.json +``` + +Finalization verifies: + +- task ID and packet fingerprint; +- review modes do not exceed the packet; +- reviewed claim IDs exist; +- omitted material claims have an explicit blocked reason; +- reviewed artifact paths and hashes match the packet; +- at least one artifact is covered; +- finding severity and resolution state; +- limitations are stated; +- independence requirement; +- no unresolved critical/major finding is hidden behind `passed`. + +If independence was required but attested false, the runtime adds `review-not-independent`. Missing claim coverage adds `incomplete-review-coverage`. The resulting status cannot remain passed. + +## Reproduction mode + +A reproduction review needs more than reading files: + +1. separate workspace; +2. recorded input hashes; +3. recorded code/config/model/database revisions; +4. explicit commands; +5. clean or independently constructed environment; +6. new execution logs and output hashes; +7. comparison against the material source values; +8. limitations when exact reproduction is impossible. + +Do not select `reproduction` merely because a reviewer inspected a notebook or reran one derived plotting cell. + +## Review packet transport + +The runtime only writes local JSON. Before sending a packet to another system: + +- inspect input and environment records for sensitive metadata; +- minimize data to the required evidence boundary; +- use an approved connector or transfer route; +- record destination, access scope, retention, and deletion expectations; +- do not include raw patient, participant, proprietary, or credential data unless explicitly approved. + +## Search patterns + +- `## Independence boundary` +- `## Prepare a review packet` +- `## Reviewer response contract` +- `## Finalize a response` +- `## Reproduction mode` +- `## Failure handling` + +## Failure handling + +| Failure | Required response | +| --- | --- | +| packet fingerprint mismatch | stop; prepare or retrieve the exact packet reviewed | +| reviewed artifact hash mismatch | reject the response; do not transfer the review to changed bytes | +| material claim omitted | obtain coverage or record a blocker and leave findings status | +| reviewer not independent | label it a second pass and obtain a separate reviewer if required | +| evidence locator missing | request exact support before resolving a material finding | +| reviewer requests unavailable raw data | record the limitation and decide whether review can proceed | +| reproduction environment unavailable | downgrade to record/method review; do not claim reproduction | +| critical or major finding open | keep review status findings or blocked | +| reviewer identity cannot be authenticated | preserve the attestation limitation; do not claim authenticated identity | + +## Common mistakes + +- Giving the reviewer the intended answer or suspected bug. +- Calling the producer's second pass independent. +- Reviewing only the final report while ignoring raw tables and execution logs. +- Reusing a response after source artifacts change. +- Marking reproduction without a separate execution. +- Omitting review limitations. +- Resolving a finding by editing the receipt rather than changing evidence or claim. +- Treating reviewer success as proof of scientific truth. + +## Evidence boundary + +The reviewer runtime standardizes blinded packets, hash coverage, finding records, and completion gating. It does not authenticate people, guarantee independence, replace domain expertise, or establish scientific truth. From 9c5afdb3a1c2aaf5000f2a1d5f9d6ad1e159a3fc Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:47:10 +0900 Subject: [PATCH 74/87] Integrate independent reviewer runtime references and safety --- scripts/integrate_reviewer_runtime_v4.py | 156 +++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 scripts/integrate_reviewer_runtime_v4.py diff --git a/scripts/integrate_reviewer_runtime_v4.py b/scripts/integrate_reviewer_runtime_v4.py new file mode 100644 index 00000000..5528d345 --- /dev/null +++ b/scripts/integrate_reviewer_runtime_v4.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +"""Integrate independent reviewer packet runtime into progressive skill references.""" + +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[1] + + +def write_json(path: Path, payload: Any) -> None: + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + + +def update_index(path: Path, entry: dict[str, Any]) -> None: + payload = json.loads(path.read_text(encoding="utf-8")) + key = "references" if "references" in payload else "entries" if "entries" in payload else "references" + entries = payload.setdefault(key, []) + if not isinstance(entries, list): + raise SystemExit(f"reference index is invalid: {path}") + existing = next((item for item in entries if isinstance(item, dict) and item.get("id") == entry["id"]), None) + if existing is None: + entries.append(entry) + else: + existing.clear() + existing.update(entry) + entries.sort(key=lambda item: str(item.get("id", "")) if isinstance(item, dict) else "") + write_json(path, payload) + + +def add_skill_route(path: Path, marker: str, paragraph: str) -> None: + text = path.read_text(encoding="utf-8") + if marker in text: + return + heading = "## Reference usage" + if heading in text: + start = text.index(heading) + len(heading) + next_heading = text.find("\n## ", start) + if next_heading < 0: + text = text.rstrip() + "\n\n" + paragraph + "\n" + else: + text = text[:next_heading].rstrip() + "\n\n" + paragraph + "\n" + text[next_heading:] + else: + anchor = "## Review workflow" + if anchor not in text: + raise SystemExit(f"cannot insert reviewer reference route: {path}") + text = text.replace(anchor, f"## Reference usage\n\n{paragraph}\n\n{anchor}", 1) + path.write_text(text, encoding="utf-8") + + +def patch_redaction() -> None: + path = ROOT / "src" / "codex_science" / "reviewer_runtime.py" + text = path.read_text(encoding="utf-8") + if "import urllib.parse\n" not in text: + text = text.replace("import json\n", "import json\nimport urllib.parse\n", 1) + old = ''' if isinstance(value, list): + return [_redact(item) for item in value] + return value +''' + new = ''' if isinstance(value, list): + return [_redact(item) for item in value] + if isinstance(value, str) and "://" in value: + try: + parsed = urllib.parse.urlsplit(value) + query = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True) + if query: + query = [ + (key, "[REDACTED]" if any(fragment in key.lower() for fragment in SENSITIVE_FRAGMENTS) else item) + for key, item in query + ] + return urllib.parse.urlunsplit( + (parsed.scheme, parsed.netloc, parsed.path, urllib.parse.urlencode(query), parsed.fragment) + ) + except ValueError: + return "[REDACTED-INVALID-URL]" + return value +''' + if old in text: + text = text.replace(old, new, 1) + elif "REDACTED-INVALID-URL" not in text: + raise SystemExit("reviewer redaction marker is missing") + old_modes = ''' response_modes = sorted({_text(item, "response review mode") for item in response.get("review_modes", [])}) +''' + new_modes = ''' response_modes_raw = response.get("review_modes", []) + if not isinstance(response_modes_raw, list): + raise ValueError("response review_modes must be a list") + response_modes = sorted({_text(item, "response review mode") for item in response_modes_raw}) +''' + if old_modes in text: + text = text.replace(old_modes, new_modes, 1) + old_claims = ''' reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in response.get("reviewed_claim_ids", [])}) +''' + new_claims = ''' reviewed_claims_raw = response.get("reviewed_claim_ids", []) + if not isinstance(reviewed_claims_raw, list): + raise ValueError("reviewed_claim_ids must be a list") + reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in reviewed_claims_raw}) +''' + if old_claims in text: + text = text.replace(old_claims, new_claims, 1) + path.write_text(text, encoding="utf-8") + + +def patch_reference_and_quality() -> None: + update_index( + ROOT / "skills" / "science-review" / "references" / "index.json", + { + "id": "independent-review-runtime", + "path": "references/independent-review-runtime.md", + "purpose": "Prepare blinded, hash-bound review packets and validate separate reviewer responses without exposing producer-only rationale.", + "read_when": ["before handing a run to a separate reviewer", "before accepting a reviewer response", "before calling a review independent or reproduced"], + "required_before": ["calling scripts/science_reviewer.py", "attaching a packet-derived review receipt to a run"], + "search_patterns": ["## Independence boundary", "## Prepare a review packet", "## Reviewer response contract", "## Reproduction mode", "## Failure handling"], + "authority": "Codex Science first-party review packet contract", + "version": "1", + "evidence_boundary": "The runtime standardizes packet and receipt coverage but does not authenticate reviewer identity or guarantee independence." + }, + ) + add_skill_route( + ROOT / "skills" / "science-review" / "SKILL.md", + "references/independent-review-runtime.md", + "Before preparing a separate human or agent review, accepting a response, or claiming independence or reproduction, **MUST** read [the independent reviewer packet contract](references/independent-review-runtime.md). Use `scripts/science_reviewer.py` to bind the packet to source manifest and artifact hashes; do not expose producer-only rationale or transfer a response to changed bytes.", + ) + quality = ROOT / "skills" / "science-review" / "quality.json" + payload = json.loads(quality.read_text(encoding="utf-8")) + outputs = payload.setdefault("output_schemas", []) + for value in ("review-task-v1", "review-response-v1", "review-receipt-v2"): + if value not in outputs: + outputs.append(value) + outputs.sort() + write_json(quality, payload) + quickstart = ROOT / "docs" / "NATIVE_RUNTIME_QUICKSTART.md" + text = quickstart.read_text(encoding="utf-8") + marker = "## 6. Prepare an independent review packet" + if marker not in text: + quickstart.write_text(text.rstrip() + ''' + +## 6. Prepare an independent review packet + +Read `skills/science-review/references/independent-review-runtime.md`, then use `scripts/science_reviewer.py prepare` to create a hash-bound packet and `finalize` to validate a separate response. A second pass by the producer must use `independent: false`; reproduction requires a separate execution. +''', encoding="utf-8") + + +def main() -> int: + patch_redaction() + patch_reference_and_quality() + subprocess.run([sys.executable, "-m", "compileall", "-q", "src", "scripts"], cwd=ROOT, check=True) + print("reviewer runtime integration: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 9b162f40a107f61cf908afa01cb62f38e6e58ac1 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:48:08 +0900 Subject: [PATCH 75/87] Add approval-gated authenticated connector action framework --- src/codex_science/action_connectors.py | 311 +++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 src/codex_science/action_connectors.py diff --git a/src/codex_science/action_connectors.py b/src/codex_science/action_connectors.py new file mode 100644 index 00000000..a9a086b6 --- /dev/null +++ b/src/codex_science/action_connectors.py @@ -0,0 +1,311 @@ +"""Approval-gated contract for optional authenticated connector packs. + +Core Codex Science does not ship provider credentials or provider-specific write +implementations. Optional packs implement ConnectorActionAdapter and receive +credentials out of band from their host. The core contract enforces preview, +optimistic concurrency, idempotency, before/after hashes, and explicit receipts. +""" + +from __future__ import annotations + +import hashlib +import json +import sqlite3 +import uuid +from dataclasses import asdict, dataclass +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Mapping, Protocol + + +MODES = {"read", "write"} +SECRET_FRAGMENTS = {"token", "password", "secret", "credential", "private_key", "api_key", "apikey", "access_key", "client_secret"} + + +def _now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") + + +def _canonical_bytes(value: Any) -> bytes: + return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + + +def _fingerprint(value: Mapping[str, Any]) -> str: + return hashlib.sha256(_canonical_bytes(value)).hexdigest() + + +def _text(value: Any, label: str) -> str: + text = str(value).strip() + if not text: + raise ValueError(f"{label} is required") + return text + + +def _sha(value: Any, label: str) -> str: + text = _text(value, label).lower() + if len(text) != 64 or any(character not in "0123456789abcdef" for character in text): + raise ValueError(f"{label} must be a SHA-256 digest") + return text + + +def _json_value(value: Any, label: str) -> None: + if value is None or isinstance(value, (str, int, float, bool)): + return + if isinstance(value, list): + for index, item in enumerate(value): + _json_value(item, f"{label}[{index}]") + return + if isinstance(value, Mapping): + for key, item in value.items(): + key_text = str(key) + if any(fragment in key_text.lower() for fragment in SECRET_FRAGMENTS): + raise ValueError(f"credential-like parameter keys are forbidden: {key_text}") + _json_value(item, f"{label}.{key_text}") + return + raise ValueError(f"{label} must contain JSON values") + + +@dataclass(frozen=True) +class ActionSpec: + schema_version: int + connector: str + operation: str + mode: str + target: str + parameters: dict[str, Any] + requested_scopes: tuple[str, ...] + idempotency_key: str + expected_before_sha256: str | None + approval_required: bool + destructive: bool + paid: bool + sensitive_data: bool + compensation: str | None + + @classmethod + def from_payload(cls, payload: Mapping[str, Any]) -> "ActionSpec": + if payload.get("schema_version", 1) != 1: + raise ValueError("unsupported action spec schema") + mode = _text(payload.get("mode"), "mode") + if mode not in MODES: + raise ValueError(f"invalid action mode: {mode}") + parameters = payload.get("parameters", {}) + if not isinstance(parameters, Mapping): + raise ValueError("parameters must be an object") + _json_value(parameters, "parameters") + scopes_raw = payload.get("requested_scopes", []) + if not isinstance(scopes_raw, list): + raise ValueError("requested_scopes must be a list") + scopes = tuple(sorted({_text(item, "requested scope") for item in scopes_raw})) + before = None if payload.get("expected_before_sha256") is None else _sha(payload["expected_before_sha256"], "expected_before_sha256") + spec = cls( + schema_version=1, + connector=_text(payload.get("connector"), "connector"), + operation=_text(payload.get("operation"), "operation"), + mode=mode, + target=_text(payload.get("target"), "target"), + parameters=dict(parameters), + requested_scopes=scopes, + idempotency_key=_text(payload.get("idempotency_key"), "idempotency_key"), + expected_before_sha256=before, + approval_required=bool(payload.get("approval_required", mode == "write")), + destructive=bool(payload.get("destructive", False)), + paid=bool(payload.get("paid", False)), + sensitive_data=bool(payload.get("sensitive_data", False)), + compensation=None if payload.get("compensation") is None else str(payload["compensation"]), + ) + if spec.mode == "write" and not spec.approval_required: + raise ValueError("write actions must require approval") + if spec.destructive and spec.mode != "write": + raise ValueError("destructive actions must use write mode") + if any(character not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.:" for character in spec.idempotency_key): + raise ValueError("idempotency_key contains unsafe characters") + return spec + + def to_dict(self) -> dict[str, Any]: + payload = asdict(self) + payload["requested_scopes"] = list(self.requested_scopes) + return payload + + @property + def fingerprint(self) -> str: + return _fingerprint(self.to_dict()) + + +class ConnectorActionAdapter(Protocol): + connector_name: str + + def snapshot(self, spec: ActionSpec) -> Mapping[str, Any]: ... + + def preview(self, spec: ActionSpec, before: Mapping[str, Any]) -> Mapping[str, Any]: ... + + def execute(self, spec: ActionSpec, preview: Mapping[str, Any]) -> Mapping[str, Any]: ... + + +def build_preview(adapter: ConnectorActionAdapter, spec: ActionSpec, *, created_at: str | None = None) -> dict[str, Any]: + if adapter.connector_name != spec.connector: + raise ValueError("adapter connector does not match action spec") + before = dict(adapter.snapshot(spec)) + before_sha = _fingerprint(before) + if spec.expected_before_sha256 is not None and before_sha != spec.expected_before_sha256: + raise ValueError("current remote state does not match expected_before_sha256") + changes = dict(adapter.preview(spec, before)) + material = { + "schema_version": 1, + "connector": spec.connector, + "operation": spec.operation, + "mode": spec.mode, + "target": spec.target, + "action_spec_sha256": spec.fingerprint, + "idempotency_key": spec.idempotency_key, + "requested_scopes": list(spec.requested_scopes), + "before_state_sha256": before_sha, + "before_state": before, + "proposed_changes": changes, + "approval_required": spec.approval_required, + "destructive": spec.destructive, + "paid": spec.paid, + "sensitive_data": spec.sensitive_data, + "compensation": spec.compensation, + "created_at": created_at or _now(), + "status": "preview", + "executed": False, + "evidence_boundary": "The preview describes a provider-specific proposed action and current state. It does not grant permission, reserve cost, or prove that execution will produce the previewed state." + } + fingerprint = _fingerprint(material) + return {**material, "preview_id": f"preview-{fingerprint[:20]}", "fingerprint": fingerprint} + + +def validate_preview(payload: Mapping[str, Any], spec: ActionSpec | None = None) -> None: + if payload.get("schema_version") != 1 or payload.get("status") != "preview" or payload.get("executed") is not False: + raise ValueError("invalid action preview state") + for field in ("preview_id", "connector", "operation", "target", "action_spec_sha256", "idempotency_key", "before_state_sha256", "created_at", "evidence_boundary"): + _text(payload.get(field), field) + _sha(payload.get("action_spec_sha256"), "action_spec_sha256") + _sha(payload.get("before_state_sha256"), "before_state_sha256") + material = dict(payload) + preview_id = str(material.pop("preview_id")) + fingerprint = str(material.pop("fingerprint", "")).lower() + if _fingerprint(material) != fingerprint or preview_id != f"preview-{fingerprint[:20]}": + raise ValueError("action preview fingerprint or ID mismatch") + if spec is not None and payload.get("action_spec_sha256") != spec.fingerprint: + raise ValueError("action preview covers a different spec") + + +def build_action_approval(preview: Mapping[str, Any], *, approved_by: str, approved_scopes: list[str], approved_at: str | None = None) -> dict[str, Any]: + validate_preview(preview) + scopes = sorted({_text(item, "approved scope") for item in approved_scopes}) + requested = set(map(str, preview.get("requested_scopes", []))) + if not requested.issubset(scopes): + raise ValueError("approved scopes do not cover requested scopes") + material = { + "schema_version": 1, + "preview_id": preview["preview_id"], + "preview_fingerprint": preview["fingerprint"], + "action_spec_sha256": preview["action_spec_sha256"], + "connector": preview["connector"], + "operation": preview["operation"], + "target": preview["target"], + "before_state_sha256": preview["before_state_sha256"], + "idempotency_key": preview["idempotency_key"], + "approved_by": _text(approved_by, "approved_by"), + "approved_scopes": scopes, + "approved_at": approved_at or _now(), + "approved": True, + "evidence_boundary": "Approval authorizes the exact preview and scopes. It does not endorse the scientific content or waive provider, privacy, cost, or regulatory obligations." + } + return {**material, "fingerprint": _fingerprint(material)} + + +def validate_action_approval(approval: Mapping[str, Any], preview: Mapping[str, Any]) -> None: + validate_preview(preview) + if approval.get("schema_version") != 1 or approval.get("approved") is not True: + raise ValueError("valid action approval is required") + for field in ("preview_id", "preview_fingerprint", "action_spec_sha256", "before_state_sha256", "idempotency_key"): + if approval.get(field) != preview.get(field if field != "preview_fingerprint" else "fingerprint"): + raise ValueError(f"approval {field} does not match preview") + material = dict(approval) + fingerprint = str(material.pop("fingerprint", "")).lower() + if _fingerprint(material) != fingerprint: + raise ValueError("action approval fingerprint mismatch") + + +class ActionLedger: + def __init__(self, path: Path) -> None: + self.path = path.resolve() + self.path.parent.mkdir(parents=True, exist_ok=True) + with sqlite3.connect(self.path) as connection: + connection.execute("PRAGMA journal_mode=WAL") + connection.execute( + "CREATE TABLE IF NOT EXISTS actions(idempotency_key TEXT PRIMARY KEY, action_spec_sha256 TEXT NOT NULL, receipt_json TEXT NOT NULL, created_at TEXT NOT NULL)" + ) + + def get(self, key: str) -> dict[str, Any] | None: + with sqlite3.connect(self.path) as connection: + row = connection.execute("SELECT receipt_json FROM actions WHERE idempotency_key=?", (key,)).fetchone() + return None if row is None else json.loads(str(row[0])) + + def record(self, *, key: str, spec_sha256: str, receipt: Mapping[str, Any]) -> None: + with sqlite3.connect(self.path) as connection: + existing = connection.execute("SELECT action_spec_sha256, receipt_json FROM actions WHERE idempotency_key=?", (key,)).fetchone() + if existing is not None: + if str(existing[0]) != spec_sha256 or json.loads(str(existing[1])) != dict(receipt): + raise ValueError("idempotency key is already bound to a different action") + return + connection.execute( + "INSERT INTO actions(idempotency_key, action_spec_sha256, receipt_json, created_at) VALUES(?,?,?,?)", + (key, spec_sha256, json.dumps(dict(receipt), sort_keys=True, separators=(",", ":")), _now()), + ) + + +def execute_action( + adapter: ConnectorActionAdapter, + spec: ActionSpec, + preview: Mapping[str, Any], + *, + approval: Mapping[str, Any] | None, + ledger: ActionLedger, + executed_at: str | None = None, +) -> dict[str, Any]: + validate_preview(preview, spec) + existing = ledger.get(spec.idempotency_key) + if existing is not None: + if existing.get("action_spec_sha256") != spec.fingerprint: + raise ValueError("idempotency key is already bound to a different spec") + return existing + if spec.mode == "write": + if approval is None: + raise ValueError("write action requires explicit approval") + validate_action_approval(approval, preview) + current = dict(adapter.snapshot(spec)) + current_sha = _fingerprint(current) + if current_sha != preview["before_state_sha256"]: + raise ValueError("remote state changed after preview; create a new preview") + result = dict(adapter.execute(spec, preview)) + after = dict(result.get("after_state", result)) + after_sha = _fingerprint(after) + material = { + "schema_version": 1, + "action_id": "action-" + uuid.uuid4().hex[:20], + "connector": spec.connector, + "operation": spec.operation, + "mode": spec.mode, + "target": spec.target, + "action_spec_sha256": spec.fingerprint, + "preview_id": preview["preview_id"], + "preview_fingerprint": preview["fingerprint"], + "approval_fingerprint": None if approval is None else approval.get("fingerprint"), + "idempotency_key": spec.idempotency_key, + "before_state_sha256": current_sha, + "after_state_sha256": after_sha, + "after_state": after, + "remote_object_ids": list(result.get("remote_object_ids", [])) if isinstance(result.get("remote_object_ids", []), list) else [], + "provider_receipt": result.get("provider_receipt"), + "compensation": spec.compensation, + "executed_at": executed_at or _now(), + "status": "executed", + "evidence_boundary": "This receipt records a provider adapter action and before/after state hashes. It does not validate the scientific content, provider completeness, legal authorization, or successful downstream interpretation." + } + receipt = {**material, "fingerprint": _fingerprint(material)} + ledger.record(key=spec.idempotency_key, spec_sha256=spec.fingerprint, receipt=receipt) + return receipt From 56dd87e7ccc2da86142940f24fcd7b1c0ae5b3c1 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:48:30 +0900 Subject: [PATCH 76/87] Add authenticated connector action contract CLI --- scripts/connector_action_contract.py | 75 ++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 scripts/connector_action_contract.py diff --git a/scripts/connector_action_contract.py b/scripts/connector_action_contract.py new file mode 100644 index 00000000..8056038d --- /dev/null +++ b/scripts/connector_action_contract.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Validate connector action specs/previews and issue hash-bound approvals.""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +from codex_science.action_connectors import ActionSpec, build_action_approval, validate_preview # noqa: E402 + + +def read_object(path: Path) -> dict: + payload = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError(f"JSON object required: {path}") + return payload + + +def write(path: Path, payload: dict) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + print(f"Success! Data written to: {path.resolve()}") + + +def validate_spec(args: argparse.Namespace) -> None: + spec = ActionSpec.from_payload(read_object(args.spec)) + write(args.output, {"schema_version": 1, "status": "valid", "action_spec_sha256": spec.fingerprint, "spec": spec.to_dict()}) + + +def validate_preview_command(args: argparse.Namespace) -> None: + preview = read_object(args.preview) + spec = None if args.spec is None else ActionSpec.from_payload(read_object(args.spec)) + validate_preview(preview, spec) + write(args.output, {"schema_version": 1, "status": "valid", "preview_id": preview["preview_id"], "preview_fingerprint": preview["fingerprint"]}) + + +def approve(args: argparse.Namespace) -> None: + preview = read_object(args.preview) + write(args.output, build_action_approval(preview, approved_by=args.approved_by, approved_scopes=args.scope)) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + command = commands.add_parser("validate-spec") + command.add_argument("spec", type=Path) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=validate_spec) + command = commands.add_parser("validate-preview") + command.add_argument("preview", type=Path) + command.add_argument("--spec", type=Path) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=validate_preview_command) + command = commands.add_parser("approve") + command.add_argument("preview", type=Path) + command.add_argument("--approved-by", required=True) + command.add_argument("--scope", action="append", required=True) + command.add_argument("--output", type=Path, required=True) + command.set_defaults(handler=approve) + args = parser.parse_args() + try: + args.handler(args) + except (OSError, ValueError, json.JSONDecodeError) as error: + print(f"error: {error}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 2a87bcbbef2d4bbe6834927a673550cd769956b0 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:48:58 +0900 Subject: [PATCH 77/87] Test approval-gated connector action framework --- tests/test_action_connectors.py | 125 ++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 tests/test_action_connectors.py diff --git a/tests/test_action_connectors.py b/tests/test_action_connectors.py new file mode 100644 index 00000000..24c0c4cb --- /dev/null +++ b/tests/test_action_connectors.py @@ -0,0 +1,125 @@ +import copy +import tempfile +import unittest +from pathlib import Path + +from codex_science.action_connectors import ( + ActionLedger, + ActionSpec, + build_action_approval, + build_preview, + execute_action, + validate_action_approval, + validate_preview, +) + + +class FakeAdapter: + connector_name = "fake-lab" + + def __init__(self) -> None: + self.state = {"object_id": "sample-1", "status": "draft", "version": 1} + self.execute_count = 0 + + def snapshot(self, spec: ActionSpec): + return copy.deepcopy(self.state) + + def preview(self, spec: ActionSpec, before): + return {"operation": spec.operation, "changes": {"status": spec.parameters["status"]}, "before_version": before["version"]} + + def execute(self, spec: ActionSpec, preview): + self.execute_count += 1 + self.state = {**self.state, "status": spec.parameters["status"], "version": self.state["version"] + 1} + return {"after_state": copy.deepcopy(self.state), "remote_object_ids": [self.state["object_id"]], "provider_receipt": {"request_id": "provider-1"}} + + +def write_spec() -> ActionSpec: + return ActionSpec.from_payload( + { + "schema_version": 1, + "connector": "fake-lab", + "operation": "publish-sample", + "mode": "write", + "target": "workspace-1/sample-1", + "parameters": {"status": "published"}, + "requested_scopes": ["sample:read", "sample:write"], + "idempotency_key": "publish-sample-1-v1", + "approval_required": True, + "destructive": False, + "paid": False, + "sensitive_data": False, + "compensation": "Set status back to draft through a separately approved action." + } + ) + + +class ActionConnectorTests(unittest.TestCase): + def test_write_preview_approval_execution_and_idempotency(self) -> None: + adapter = FakeAdapter() + spec = write_spec() + preview = build_preview(adapter, spec, created_at="2026-07-21T00:00:00Z") + validate_preview(preview, spec) + self.assertFalse(preview["executed"]) + approval = build_action_approval( + preview, + approved_by="jaemin", + approved_scopes=["sample:read", "sample:write"], + approved_at="2026-07-21T00:01:00Z", + ) + validate_action_approval(approval, preview) + with tempfile.TemporaryDirectory() as tempdir: + ledger = ActionLedger(Path(tempdir) / "actions.sqlite") + receipt = execute_action( + adapter, + spec, + preview, + approval=approval, + ledger=ledger, + executed_at="2026-07-21T00:02:00Z", + ) + self.assertEqual("executed", receipt["status"]) + self.assertNotEqual(receipt["before_state_sha256"], receipt["after_state_sha256"]) + self.assertEqual(["sample-1"], receipt["remote_object_ids"]) + repeated = execute_action(adapter, spec, preview, approval=approval, ledger=ledger) + self.assertEqual(receipt, repeated) + self.assertEqual(1, adapter.execute_count) + + def test_write_requires_exact_approval_and_unchanged_before_state(self) -> None: + adapter = FakeAdapter() + spec = write_spec() + preview = build_preview(adapter, spec, created_at="2026-07-21T00:00:00Z") + with tempfile.TemporaryDirectory() as tempdir: + ledger = ActionLedger(Path(tempdir) / "actions.sqlite") + with self.assertRaisesRegex(ValueError, "requires explicit approval"): + execute_action(adapter, spec, preview, approval=None, ledger=ledger) + approval = build_action_approval(preview, approved_by="jaemin", approved_scopes=["sample:read", "sample:write"]) + adapter.state["version"] = 2 + with self.assertRaisesRegex(ValueError, "changed after preview"): + execute_action(adapter, spec, preview, approval=approval, ledger=ledger) + + def test_approval_scope_and_idempotency_key_cannot_be_reused_for_other_spec(self) -> None: + adapter = FakeAdapter() + spec = write_spec() + preview = build_preview(adapter, spec) + with self.assertRaisesRegex(ValueError, "do not cover"): + build_action_approval(preview, approved_by="jaemin", approved_scopes=["sample:read"]) + approval = build_action_approval(preview, approved_by="jaemin", approved_scopes=["sample:read", "sample:write"]) + with tempfile.TemporaryDirectory() as tempdir: + ledger = ActionLedger(Path(tempdir) / "actions.sqlite") + execute_action(adapter, spec, preview, approval=approval, ledger=ledger) + changed = ActionSpec.from_payload({**spec.to_dict(), "parameters": {"status": "archived"}}) + changed_preview = build_preview(adapter, changed) + changed_approval = build_action_approval(changed_preview, approved_by="jaemin", approved_scopes=["sample:read", "sample:write"]) + with self.assertRaisesRegex(ValueError, "different spec"): + execute_action(adapter, changed, changed_preview, approval=changed_approval, ledger=ledger) + + def test_credentials_are_out_of_band_and_write_cannot_disable_approval(self) -> None: + base = write_spec().to_dict() + with self.assertRaisesRegex(ValueError, "credential-like"): + ActionSpec.from_payload({**base, "parameters": {"api_token": "secret"}}) + with self.assertRaisesRegex(ValueError, "must require approval"): + ActionSpec.from_payload({**base, "approval_required": False}) + + +if __name__ == "__main__": + unittest.main() From 32dfc345b2498075d4b0d328f9f360ee12519ba0 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:49:44 +0900 Subject: [PATCH 78/87] Document optional authenticated connector pack contract --- .../authenticated-connector-packs.md | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 skills/codex-science/references/authenticated-connector-packs.md diff --git a/skills/codex-science/references/authenticated-connector-packs.md b/skills/codex-science/references/authenticated-connector-packs.md new file mode 100644 index 00000000..cd7fe664 --- /dev/null +++ b/skills/codex-science/references/authenticated-connector-packs.md @@ -0,0 +1,279 @@ +# Optional authenticated and write-capable connector packs + +Read this reference before designing, installing, previewing, approving, or executing a connector that accesses authenticated laboratory, clinical, cloud, registry, LIMS/ELN, workflow, or storage services. + +## Core boundary + +Codex Science core remains local-first and read-only for public discovery. Provider credentials and provider-specific write implementations belong in separately installed optional packs. + +An optional pack must implement the `ConnectorActionAdapter` contract: + +```python +class ConnectorActionAdapter(Protocol): + connector_name: str + + def snapshot(self, spec: ActionSpec) -> Mapping[str, Any]: ... + def preview(self, spec: ActionSpec, before: Mapping[str, Any]) -> Mapping[str, Any]: ... + def execute(self, spec: ActionSpec, preview: Mapping[str, Any]) -> Mapping[str, Any]: ... +``` + +Credentials are supplied out of band by the host or connector runtime. They must never appear in action specs, previews, approvals, logs, receipts, artifact bundles, or prompts. + +## Action spec + +```json +{ + "schema_version": 1, + "connector": "benchling-pack", + "operation": "create-assay-result", + "mode": "write", + "target": "tenant/project/experiment", + "parameters": { + "schema_id": "assay-result-v2", + "record": { + "sample_id": "sample-42", + "value": 0.72, + "unit": "fraction" + } + }, + "requested_scopes": [ + "assay:read", + "assay:write" + ], + "idempotency_key": "run-014-assay-result-sample-42-v1", + "expected_before_sha256": null, + "approval_required": true, + "destructive": false, + "paid": false, + "sensitive_data": true, + "compensation": "Archive the created result through a separately approved action." +} +``` + +### Required rules + +- `mode`: `read` or `write`; +- every write requires approval; +- destructive actions must use write mode; +- target and operation are explicit; +- parameters contain no credentials; +- requested OAuth/provider scopes are explicit; +- idempotency key is stable and unique to one semantic action; +- expected before-state hash is used when the caller already knows the remote state; +- cost, sensitivity, and compensation are explicit. + +The core validator rejects secret-like parameter keys such as token, password, secret, credential, API key, access key, private key, or client secret. + +## Validate a spec + +```bash +python scripts/connector_action_contract.py validate-spec \ + artifacts/run-014/action-spec.json \ + --output artifacts/run-014/action-spec.validated.json +``` + +Validation returns the canonical spec and `action_spec_sha256`. It does not contact the provider. + +## Provider preview + +The provider adapter calls: + +```python +preview = build_preview(adapter, spec) +``` + +The adapter must obtain a minimal safe current-state snapshot and proposed changes. Preview output includes: + +```json +{ + "preview_id": "preview-...", + "action_spec_sha256": "...", + "idempotency_key": "...", + "before_state_sha256": "...", + "before_state": {}, + "proposed_changes": {}, + "requested_scopes": [], + "approval_required": true, + "destructive": false, + "paid": false, + "sensitive_data": true, + "status": "preview", + "executed": false, + "fingerprint": "..." +} +``` + +The adapter should minimize `before_state`; do not include full private records when a version, schema, or selected fields are sufficient. + +Validate a provider-created preview: + +```bash +python scripts/connector_action_contract.py validate-preview \ + artifacts/run-014/action-preview.json \ + --spec artifacts/run-014/action-spec.json \ + --output artifacts/run-014/action-preview.validated.json +``` + +## Approval + +```bash +python scripts/connector_action_contract.py approve \ + artifacts/run-014/action-preview.json \ + --approved-by jaemin \ + --scope assay:read \ + --scope assay:write \ + --output artifacts/run-014/action-approval.json +``` + +Approval binds: + +- exact preview ID and fingerprint; +- exact action spec SHA-256; +- target and operation; +- before-state SHA-256; +- idempotency key; +- approved scopes; +- approver attestation and time. + +Approved scopes must cover requested scopes. A changed preview or before-state requires new approval. + +Approval authorizes the provider action boundary. It does not establish scientific validity, consent, privacy compliance, regulatory permission, affordability, or downstream interpretation. + +## Execute through an optional pack + +A provider pack calls: + +```python +receipt = execute_action( + adapter, + spec, + preview, + approval=approval, + ledger=ActionLedger(Path(".connector-actions.sqlite")), +) +``` + +Execution procedure: + +1. validate spec and preview; +2. resolve the exact approval for writes; +3. check the idempotency ledger; +4. obtain a fresh current-state snapshot; +5. compare its hash with preview before-state; +6. reject state drift; +7. call the provider adapter once; +8. record after-state hash, remote object IDs, provider receipt, compensation, and fingerprint; +9. persist the idempotency receipt; +10. add the action receipt to run provenance when material. + +A repeated request with the same idempotency key and same spec returns the original receipt without another provider call. Reusing the key for a different spec fails. + +## Provider pack requirements + +Each optional pack should contain: + +```text +plugin manifest +provider-specific skill +references/ + authentication.md + read-operations.md + write-operations.md + schemas.md + failure-recovery.md +adapter implementation +fixture adapter +contract tests +scope policy +license and data-use notes +``` + +Provider references must specify exact operation names, parameter schemas, pagination, remote object IDs, rate limits, idempotency behavior, write preview, compensation, and data retention. + +### Authentication + +- OAuth/device flow or host-managed secret store preferred; +- minimum scopes; +- separate read and write scopes; +- no credentials in command arguments when a secure host channel exists; +- token refresh and revocation behavior documented; +- test/sandbox tenant supported where provider offers one. + +### Write safety + +- preview before execution; +- exact target; +- optimistic concurrency or version check; +- idempotency; +- explicit destructive and paid flags; +- before/after state hashes; +- remote IDs; +- compensation or explanation when rollback is impossible; +- provider request ID; +- immutable audit receipt. + +### Sensitive data + +- minimize transferred fields; +- record data classification; +- obtain explicit approval; +- enforce provider region/tenant policy; +- avoid copying raw participant data into artifacts; +- store approved immutable references and hashes instead; +- document retention and deletion expectations. + +## Recommended pack separation + +Examples: + +```text +codex-science-benchling +codex-science-protocols +codex-science-terra +codex-science-modal +codex-science-lamindb +codex-science-redcap +``` + +These names describe optional architecture only. Core must not claim a provider is supported until a pack exists, its API version is pinned, and its read/write fixtures pass. + +## Search patterns + +- `## Action spec` +- `## Provider preview` +- `## Approval` +- `## Execute through an optional pack` +- `## Provider pack requirements` +- `## Failure handling` + +## Failure handling + +| Failure | Required response | +| --- | --- | +| credential appears in spec or preview | reject and rotate the credential if exposed | +| requested scope not approved | obtain a new approval or reduce the action | +| remote state changed after preview | create a new snapshot, preview, and approval | +| idempotency key reused for different spec | create a new semantic key; do not overwrite ledger history | +| provider 409/version conflict | refresh state and return a new preview | +| provider 429 | bounded retry only when provider policy allows; preserve attempt receipt | +| provider 5xx | preserve failure and retry through a new attempt policy | +| destructive action has no compensation | make irreversibility explicit before approval | +| remote object ID missing | treat execution receipt as incomplete | +| sensitive data exceeds scope | stop transfer and revise the approved data boundary | +| provider schema/version drift | disable the operation until fixtures and references are updated | + +## Common mistakes + +- Putting tokens in action parameters. +- Letting a plugin write without preview. +- Treating a human approval message as approval for a changed spec. +- Omitting requested scopes. +- Reusing idempotency keys across semantically different actions. +- Logging full sensitive before/after records. +- Assuming provider success means the scientific value is correct. +- Calling a compensating action an automatic rollback when it requires separate approval. +- Shipping authenticated write operations in the public read-only core. + +## Evidence boundary + +The connector action framework standardizes preview, approval, idempotency, concurrency, and receipts. It does not provide provider credentials, implement a provider API by itself, establish scientific validity, or certify privacy, legal, or regulatory compliance. From cffb98407dcc631b6b14c749104e8c30a1f5ab33 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:50:45 +0900 Subject: [PATCH 79/87] Integrate reviewer and authenticated connector action contracts --- scripts/integrate_review_action_runtime_v4.py | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 scripts/integrate_review_action_runtime_v4.py diff --git a/scripts/integrate_review_action_runtime_v4.py b/scripts/integrate_review_action_runtime_v4.py new file mode 100644 index 00000000..b148d874 --- /dev/null +++ b/scripts/integrate_review_action_runtime_v4.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +"""Integrate reviewer and authenticated connector action runtimes.""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[1] + + +def write_json(path: Path, payload: Any) -> None: + path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") + + +def update_index(path: Path, entry: dict[str, Any]) -> None: + payload = json.loads(path.read_text(encoding="utf-8")) + key = "references" if "references" in payload else "entries" if "entries" in payload else "references" + entries = payload.setdefault(key, []) + if not isinstance(entries, list): + raise SystemExit(f"reference index is invalid: {path}") + existing = next((item for item in entries if isinstance(item, dict) and item.get("id") == entry["id"]), None) + if existing is None: + entries.append(entry) + else: + existing.clear() + existing.update(entry) + entries.sort(key=lambda item: str(item.get("id", "")) if isinstance(item, dict) else "") + write_json(path, payload) + + +def add_route(path: Path, marker: str, paragraph: str, *, anchor: str) -> None: + text = path.read_text(encoding="utf-8") + if marker in text: + return + heading = "## Reference usage" + if heading in text: + start = text.index(heading) + len(heading) + next_heading = text.find("\n## ", start) + if next_heading < 0: + text = text.rstrip() + "\n\n" + paragraph + "\n" + else: + text = text[:next_heading].rstrip() + "\n\n" + paragraph + "\n" + text[next_heading:] + else: + if anchor not in text: + raise SystemExit(f"cannot insert reference route: {path}") + text = text.replace(anchor, f"## Reference usage\n\n{paragraph}\n\n{anchor}", 1) + path.write_text(text, encoding="utf-8") + + +def add_quality(path: Path, values: tuple[str, ...]) -> None: + payload = json.loads(path.read_text(encoding="utf-8")) + outputs = payload.setdefault("output_schemas", []) + if not isinstance(outputs, list): + raise SystemExit(f"quality output_schemas is invalid: {path}") + for value in values: + if value not in outputs: + outputs.append(value) + outputs.sort() + write_json(path, payload) + + +def patch_reviewer() -> None: + path = ROOT / "src" / "codex_science" / "reviewer_runtime.py" + text = path.read_text(encoding="utf-8") + if "import urllib.parse\n" not in text: + text = text.replace("import json\n", "import json\nimport urllib.parse\n", 1) + old = ''' if isinstance(value, list): + return [_redact(item) for item in value] + return value +''' + new = ''' if isinstance(value, list): + return [_redact(item) for item in value] + if isinstance(value, str) and "://" in value: + try: + parsed = urllib.parse.urlsplit(value) + query = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True) + if query: + query = [ + ( + key, + "[REDACTED]" + if any(fragment in key.lower() for fragment in SENSITIVE_FRAGMENTS) + else item, + ) + for key, item in query + ] + return urllib.parse.urlunsplit( + ( + parsed.scheme, + parsed.netloc, + parsed.path, + urllib.parse.urlencode(query), + parsed.fragment, + ) + ) + except ValueError: + return "[REDACTED-INVALID-URL]" + return value +''' + if old in text: + text = text.replace(old, new, 1) + old_modes = ' response_modes = sorted({_text(item, "response review mode") for item in response.get("review_modes", [])})\n' + new_modes = ''' response_modes_raw = response.get("review_modes", []) + if not isinstance(response_modes_raw, list): + raise ValueError("response review_modes must be a list") + response_modes = sorted({_text(item, "response review mode") for item in response_modes_raw}) +''' + if old_modes in text: + text = text.replace(old_modes, new_modes, 1) + old_claims = ' reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in response.get("reviewed_claim_ids", [])})\n' + new_claims = ''' reviewed_claims_raw = response.get("reviewed_claim_ids", []) + if not isinstance(reviewed_claims_raw, list): + raise ValueError("reviewed_claim_ids must be a list") + reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in reviewed_claims_raw}) +''' + if old_claims in text: + text = text.replace(old_claims, new_claims, 1) + path.write_text(text, encoding="utf-8") + + +def patch_action_framework() -> None: + path = ROOT / "src" / "codex_science" / "action_connectors.py" + text = path.read_text(encoding="utf-8") + text = text.replace( + 'SECRET_FRAGMENTS = {"token", "password", "secret", "credential", "private_key", "api_key", "apikey", "access_key", "client_secret"}\n', + 'SECRET_FRAGMENTS = {"token", "password", "secret", "credential", "private_key", "api_key", "apikey", "access_key", "client_secret", "authorization", "bearer", "session_cookie"}\n', + ) + execute_marker = ''' validate_preview(preview, spec) + existing = ledger.get(spec.idempotency_key) +''' + execute_replacement = ''' validate_preview(preview, spec) + if adapter.connector_name != spec.connector: + raise ValueError("adapter connector does not match action spec") + existing = ledger.get(spec.idempotency_key) +''' + if execute_marker in text: + text = text.replace(execute_marker, execute_replacement, 1) + if "def validate_action_receipt" not in text: + text += ''' + +def validate_action_receipt(payload: Mapping[str, Any], spec: ActionSpec | None = None) -> None: + if payload.get("schema_version") != 1 or payload.get("status") != "executed": + raise ValueError("invalid action receipt state") + for field in ( + "action_id", "connector", "operation", "mode", "target", + "action_spec_sha256", "preview_id", "preview_fingerprint", + "idempotency_key", "before_state_sha256", "after_state_sha256", + "executed_at", "evidence_boundary", + ): + _text(payload.get(field), field) + for field in ("action_spec_sha256", "preview_fingerprint", "before_state_sha256", "after_state_sha256"): + _sha(payload.get(field), field) + material = dict(payload) + fingerprint = str(material.pop("fingerprint", "")).lower() + if _fingerprint(material) != fingerprint: + raise ValueError("action receipt fingerprint mismatch") + if spec is not None and payload.get("action_spec_sha256") != spec.fingerprint: + raise ValueError("action receipt covers a different spec") +''' + path.write_text(text, encoding="utf-8") + + +def patch_references() -> None: + update_index( + ROOT / "skills" / "science-review" / "references" / "index.json", + { + "id": "independent-review-runtime", + "path": "references/independent-review-runtime.md", + "purpose": "Prepare blinded, hash-bound review packets and validate separate reviewer responses without producer-only rationale.", + "read_when": ["before handing a run to a separate reviewer", "before accepting a reviewer response", "before calling a review independent or reproduced"], + "required_before": ["calling scripts/science_reviewer.py", "attaching a packet-derived review receipt"], + "search_patterns": ["## Independence boundary", "## Prepare a review packet", "## Reviewer response contract", "## Reproduction mode", "## Failure handling"], + "authority": "Codex Science first-party review packet contract", + "version": "1", + "evidence_boundary": "The packet standardizes coverage but does not authenticate identity or guarantee independence." + }, + ) + add_route( + ROOT / "skills" / "science-review" / "SKILL.md", + "references/independent-review-runtime.md", + "Before preparing a separate human or agent review, accepting a response, or claiming independence or reproduction, **MUST** read [the independent reviewer packet contract](references/independent-review-runtime.md). Use `scripts/science_reviewer.py` to bind the packet to source manifest and artifact hashes; do not expose producer-only rationale or transfer a response to changed bytes.", + anchor="## Review workflow", + ) + add_quality(ROOT / "skills" / "science-review" / "quality.json", ("review-task-v1", "review-response-v1", "review-receipt-v2")) + + update_index( + ROOT / "skills" / "codex-science" / "references" / "index.json", + { + "id": "authenticated-connector-packs", + "path": "references/authenticated-connector-packs.md", + "purpose": "Approval-gated optional connector packs with out-of-band credentials, preview, scopes, idempotency, concurrency, and before/after receipts.", + "read_when": ["before installing or designing an authenticated connector pack", "before previewing or executing a provider write", "before transferring sensitive data to an external service"], + "required_before": ["issuing a connector action approval", "executing through ConnectorActionAdapter", "claiming a provider write integration is safe"], + "search_patterns": ["## Action spec", "## Provider preview", "## Approval", "## Execute through an optional pack", "## Failure handling"], + "authority": "Codex Science first-party optional connector contract", + "version": "1", + "evidence_boundary": "The framework standardizes action safety; it does not supply credentials, provider implementations, or compliance certification." + }, + ) + add_route( + ROOT / "skills" / "codex-science" / "SKILL.md", + "references/authenticated-connector-packs.md", + "Before designing, installing, approving, or using an authenticated or write-capable provider connector, **MUST** read [the optional connector pack contract](references/authenticated-connector-packs.md). Credentials stay out of band; every write requires a hash-bound preview, exact scopes, idempotency key, fresh before-state check, explicit approval, and before/after receipt.", + anchor="## Workflow", + ) + add_quality(ROOT / "skills" / "codex-science" / "quality.json", ("connector-action-spec-v1", "connector-action-preview-v1", "connector-action-approval-v1", "connector-action-receipt-v1")) + + +def patch_docs() -> None: + quickstart = ROOT / "docs" / "NATIVE_RUNTIME_QUICKSTART.md" + text = quickstart.read_text(encoding="utf-8") + if "## 6. Prepare an independent review packet" not in text: + text = text.rstrip() + ''' + +## 6. Prepare an independent review packet + +Read `skills/science-review/references/independent-review-runtime.md`, then use `scripts/science_reviewer.py prepare` and `finalize`. A producer second pass is not independent, and reproduction requires a separate execution. +''' + if "## 7. Validate an authenticated connector action" not in text: + text = text.rstrip() + ''' + +## 7. Validate an authenticated connector action + +Read `skills/codex-science/references/authenticated-connector-packs.md`. Use `scripts/connector_action_contract.py` for spec, preview, and approval validation. Provider execution belongs to an optional adapter pack; credentials never enter the action JSON. +''' + quickstart.write_text(text, encoding="utf-8") + + +def main() -> int: + patch_reviewer() + patch_action_framework() + patch_references() + patch_docs() + print("review and action runtime integration: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From e4646d3756ea8fcdbc6384eda79b2ab0fda72650 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:51:32 +0900 Subject: [PATCH 80/87] Harden reviewer and connector action receipt hashing --- scripts/harden_review_action_runtime_v4.py | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 scripts/harden_review_action_runtime_v4.py diff --git a/scripts/harden_review_action_runtime_v4.py b/scripts/harden_review_action_runtime_v4.py new file mode 100644 index 00000000..627e76d9 --- /dev/null +++ b/scripts/harden_review_action_runtime_v4.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Finalize canonical hashes and receipt validation for review/action runtimes.""" + +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def patch_reviewer() -> None: + path = ROOT / "src" / "codex_science" / "reviewer_runtime.py" + text = path.read_text(encoding="utf-8") + text = text.replace( + "from codex_science.review_receipts import build_review_receipt, validate_review_receipt\n", + "from codex_science.review_receipts import build_review_receipt, canonical_sha256, validate_review_receipt\n", + ) + text = text.replace( + ' receipt["fingerprint"] = _fingerprint(material)\n validate_review_receipt(receipt)\n', + ' receipt["fingerprint"] = canonical_sha256(material)\n validate_review_receipt(receipt)\n', + ) + path.write_text(text, encoding="utf-8") + + +def patch_action() -> None: + path = ROOT / "src" / "codex_science" / "action_connectors.py" + text = path.read_text(encoding="utf-8") + marker = ''' validate_preview(preview, spec) + existing = ledger.get(spec.idempotency_key) +''' + replacement = ''' validate_preview(preview, spec) + if adapter.connector_name != spec.connector: + raise ValueError("adapter connector does not match action spec") + existing = ledger.get(spec.idempotency_key) +''' + if marker in text: + text = text.replace(marker, replacement, 1) + path.write_text(text, encoding="utf-8") + + +def main() -> int: + patch_reviewer() + patch_action() + print("review/action runtime hardening: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 143fb0e4efd75343016e148d126c864c63beaa04 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:52:03 +0900 Subject: [PATCH 81/87] Test unicode review and action receipt validation --- tests/test_review_action_receipts.py | 121 +++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 tests/test_review_action_receipts.py diff --git a/tests/test_review_action_receipts.py b/tests/test_review_action_receipts.py new file mode 100644 index 00000000..4ed21887 --- /dev/null +++ b/tests/test_review_action_receipts.py @@ -0,0 +1,121 @@ +import hashlib +import tempfile +import unittest +from pathlib import Path + +from codex_science.action_connectors import ( + ActionLedger, + ActionSpec, + build_action_approval, + build_preview, + execute_action, + validate_action_receipt, +) +from codex_science.artifacts import add_artifact, new_manifest, write_manifest +from codex_science.reviewer_runtime import build_review_packet, finalize_review_response +from codex_science.review_receipts import validate_review_receipt + + +class Adapter: + connector_name = "fixture" + + def __init__(self) -> None: + self.state = {"id": "x", "version": 1} + + def snapshot(self, spec): + return dict(self.state) + + def preview(self, spec, before): + return {"change": spec.parameters} + + def execute(self, spec, preview): + self.state = {"id": "x", "version": 2} + return {"after_state": dict(self.state), "remote_object_ids": ["x"]} + + +def manifest_path(root: Path) -> Path: + artifact = root / "value.json" + artifact.write_text('{"value": 1}\n', encoding="utf-8") + manifest = new_manifest( + "review-unicode", + "검토 패킷의 해시가 유니코드에서도 안정적인가?", + [{"id": "record", "description": "Record value", "status": "completed"}], + ) + manifest["claims"] = [{"id": "C1", "text": "값이 기록되었다.", "evidence": ["value.json"]}] + manifest["review"] = {"status": "passed", "findings": []} + add_artifact(manifest, "value.json", kind="result", sha256=hashlib.sha256(artifact.read_bytes()).hexdigest()) + path = root / "manifest.json" + write_manifest(manifest, path) + return path + + +class ReviewActionReceiptTests(unittest.TestCase): + def test_unicode_review_receipt_uses_canonical_review_hash(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + packet = build_review_packet(manifest_path(root), review_modes=["record"], created_at="2026-07-21T00:00:00Z") + artifact = packet["artifacts"][0] + receipt = finalize_review_response( + packet, + { + "schema_version": 1, + "review_task_id": packet["review_task_id"], + "packet_fingerprint": packet["fingerprint"], + "reviewer": "독립-검토자", + "independent": True, + "review_modes": ["record"], + "reviewed_claim_ids": ["C1"], + "reviewed_artifacts": [{"path": artifact["path"], "sha256": artifact["sha256"]}], + "findings": [], + "limitations": ["원시 장비 파일은 검토하지 못했다."], + "status": "passed", + }, + ) + validate_review_receipt(receipt) + self.assertEqual("passed", receipt["status"]) + + def test_action_receipt_is_bound_to_exact_spec(self) -> None: + adapter = Adapter() + spec = ActionSpec.from_payload( + { + "connector": "fixture", + "operation": "update", + "mode": "write", + "target": "object/x", + "parameters": {"value": 2}, + "requested_scopes": ["object:write"], + "idempotency_key": "fixture-update-x-v1", + } + ) + preview = build_preview(adapter, spec, created_at="2026-07-21T00:00:00Z") + approval = build_action_approval(preview, approved_by="jaemin", approved_scopes=["object:write"], approved_at="2026-07-21T00:01:00Z") + with tempfile.TemporaryDirectory() as tempdir: + receipt = execute_action(adapter, spec, preview, approval=approval, ledger=ActionLedger(Path(tempdir) / "ledger.sqlite"), executed_at="2026-07-21T00:02:00Z") + validate_action_receipt(receipt, spec) + changed = ActionSpec.from_payload({**spec.to_dict(), "parameters": {"value": 3}, "idempotency_key": "fixture-update-x-v2"}) + with self.assertRaisesRegex(ValueError, "different spec"): + validate_action_receipt(receipt, changed) + + def test_mismatched_adapter_is_rejected_before_execution(self) -> None: + class Wrong(Adapter): + connector_name = "wrong" + + spec = ActionSpec.from_payload( + { + "connector": "fixture", + "operation": "read", + "mode": "read", + "target": "object/x", + "parameters": {}, + "requested_scopes": ["object:read"], + "idempotency_key": "fixture-read-x-v1", + } + ) + preview = build_preview(Adapter(), spec) + with tempfile.TemporaryDirectory() as tempdir: + with self.assertRaisesRegex(ValueError, "adapter connector"): + execute_action(Wrong(), spec, preview, approval=None, ledger=ActionLedger(Path(tempdir) / "ledger.sqlite")) + + +if __name__ == "__main__": + unittest.main() From 53826038edd92d1237f5d0fca07c74bae5dcdb52 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:52:27 +0900 Subject: [PATCH 82/87] Finalize complete progressive scientific runtime v4 --- .../finalize-complete-runtime-v4.yml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 .github/workflows/finalize-complete-runtime-v4.yml diff --git a/.github/workflows/finalize-complete-runtime-v4.yml b/.github/workflows/finalize-complete-runtime-v4.yml new file mode 100644 index 00000000..360f48cb --- /dev/null +++ b/.github/workflows/finalize-complete-runtime-v4.yml @@ -0,0 +1,96 @@ +name: Finalize complete scientific runtime v4 + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: finalize-complete-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + finalize: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 100 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Apply permanent source and progressive reference patches + run: | + uv run python scripts/patch_permanent_runtime_v4.py + uv run python scripts/harden_extended_runtime_v4.py + uv run python scripts/integrate_review_action_runtime_v4.py + uv run python scripts/harden_review_action_runtime_v4.py + - name: Remove one-time Python files before compilation + run: | + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + rm -f scripts/repair_native_runtime_v4.py + rm -f scripts/seal_native_runtime_v4.py + rm -f scripts/integrate_extended_runtime_v4.py + rm -f scripts/patch_permanent_runtime_v4.py + rm -f scripts/harden_extended_runtime_v4.py + rm -f scripts/integrate_reviewer_runtime_v4.py + rm -f scripts/integrate_review_action_runtime_v4.py + rm -f scripts/harden_review_action_runtime_v4.py + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile permanent source + run: uv run python -m compileall -q src scripts + - name: Run complete focused runtime suite + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler_security.py' + uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' + uv run python -m unittest discover -s tests -p 'test_fair_export.py' + uv run python -m unittest discover -s tests -p 'test_reviewer_runtime.py' + uv run python -m unittest discover -s tests -p 'test_action_connectors.py' + uv run python -m unittest discover -s tests -p 'test_review_action_receipts.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove all one-time integration workflows + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f .github/workflows/seal-native-runtime-v4.yml + rm -f .github/workflows/seal-extended-native-runtime-v4.yml + rm -f .github/workflows/finish-native-runtime-v4.yml + rm -f .github/workflows/commit-permanent-runtime-v4.yml + rm -f .github/workflows/finalize-all-runtime-v4.yml + rm -f .github/workflows/finalize-complete-runtime-v4.yml + - name: Commit clean permanent implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Finalize progressive references and complete scientific runtime v4" + git fetch origin "$branch" + git rebase "origin/$branch" + git push origin HEAD:"$branch" From 145c6dadd49219dc7be613ea6adf25a3544cb041 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:54:12 +0900 Subject: [PATCH 83/87] Add final resource and input integrity hardening --- scripts/harden_core_runtime_v4.py | 228 ++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 scripts/harden_core_runtime_v4.py diff --git a/scripts/harden_core_runtime_v4.py b/scripts/harden_core_runtime_v4.py new file mode 100644 index 00000000..f57540c9 --- /dev/null +++ b/scripts/harden_core_runtime_v4.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +"""Apply final resource, input-integrity, and secret-boundary hardening.""" + +from __future__ import annotations + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def patch_artifact_runtime() -> None: + path = ROOT / "src" / "codex_science" / "artifact_runtime.py" + text = path.read_text(encoding="utf-8") + if "MAX_RUNTIME_BYTES" not in text: + marker = "VIEWER_SELECTIONS = {\n" + index = text.index(marker) + end = text.index("\n}\n", index) + 3 + text = text[:end] + "\nMAX_RUNTIME_BYTES = 16 * 1024 * 1024\nMAX_RUNTIME_RECORDS = 10_000\nMAX_SELECTION_BYTES = 64 * 1024\nMAX_PROPOSAL_PARAMETER_BYTES = 64 * 1024\nMAX_REASON_CHARACTERS = 4_000\n" + text[end:] + old = ''' if max_bytes < 1 or max_records < 1: + raise ValueError("max_bytes and max_records must be positive") +''' + new = ''' if max_bytes < 1 or max_records < 1: + raise ValueError("max_bytes and max_records must be positive") + if max_bytes > MAX_RUNTIME_BYTES: + raise ValueError(f"max_bytes exceeds hard runtime ceiling: {MAX_RUNTIME_BYTES}") + if max_records > MAX_RUNTIME_RECORDS: + raise ValueError(f"max_records exceeds hard runtime ceiling: {MAX_RUNTIME_RECORDS}") +''' + if old in text: + text = text.replace(old, new, 1) + old = ''' reason = _text(reason, "selection reason") + material = { +''' + new = ''' reason = _text(reason, "selection reason") + if len(reason) > MAX_REASON_CHARACTERS: + raise ValueError("selection reason is too long") + if len(_canonical_bytes(selector)) > MAX_SELECTION_BYTES: + raise ValueError("selection payload exceeds the runtime limit") + material = { +''' + if old in text: + text = text.replace(old, new, 1) + old = ''' reason = _text(reason, "proposal reason") + steps = sorted({_safe_relative(item, "affected step") for item in affected_steps}) +''' + new = ''' reason = _text(reason, "proposal reason") + if len(reason) > MAX_REASON_CHARACTERS: + raise ValueError("proposal reason is too long") + if len(_canonical_bytes(parameters)) > MAX_PROPOSAL_PARAMETER_BYTES: + raise ValueError("proposal parameters exceed the runtime limit") + steps = sorted({_safe_relative(item, "affected step") for item in affected_steps}) +''' + if old in text: + text = text.replace(old, new, 1) + path.write_text(text, encoding="utf-8") + + +def patch_compute_inputs() -> None: + path = ROOT / "src" / "codex_science" / "compute_backends.py" + text = path.read_text(encoding="utf-8") + if "def _normalize_inputs" not in text: + marker = "\n\n@dataclass(frozen=True)\nclass ResourceRequest:\n" + helper = ''' + +def _normalize_inputs(value: Any) -> tuple[dict[str, Any], ...]: + if value is None: + return () + if not isinstance(value, list): + raise ValueError("inputs must be a list") + results: list[dict[str, Any]] = [] + ids: set[str] = set() + for index, raw in enumerate(value): + if not isinstance(raw, Mapping): + raise ValueError(f"input {index} must be an object") + item = dict(raw) + input_id = _text(item.get("id", f"input-{index}"), f"input {index} id") + if input_id in ids: + raise ValueError(f"duplicate input ID: {input_id}") + ids.add(input_id) + item["id"] = input_id + if item.get("path") is not None: + item["path"] = _safe_relative(str(item["path"]), f"input {input_id} path") + elif not str(item.get("identifier", "")).strip(): + raise ValueError(f"input {input_id} needs path or identifier") + if item.get("sha256") is not None: + item["sha256"] = _sha(item["sha256"], f"input {input_id} sha256") + if item.get("root_sha256") is not None: + item["root_sha256"] = _sha(item["root_sha256"], f"input {input_id} root_sha256") + results.append(item) + return tuple(results) + + +def _input_preflight(spec: "JobSpec") -> list[dict[str, Any]]: + checks: list[dict[str, Any]] = [] + working = Path(spec.working_directory).resolve() + for item in spec.inputs: + input_id = str(item["id"]) + if item.get("path") is None: + checks.append({"name": f"input:{input_id}", "ready": True, "detail": "external identifier recorded"}) + continue + target = (working / str(item["path"])).resolve() + if not target.is_relative_to(working): + checks.append({"name": f"input:{input_id}", "ready": False, "detail": "path escapes working directory"}) + continue + if not target.exists(): + checks.append({"name": f"input:{input_id}", "ready": False, "detail": "missing"}) + continue + expected = item.get("sha256") or item.get("root_sha256") + if expected is None: + checks.append({"name": f"input:{input_id}", "ready": True, "detail": "exists; no digest declared"}) + continue + if target.is_file(): + actual, _size = stream_sha256(target) + elif target.is_dir(): + actual = describe_directory(target).root_sha256 + else: + checks.append({"name": f"input:{input_id}", "ready": False, "detail": "unsupported filesystem object"}) + continue + checks.append({"name": f"input:{input_id}", "ready": actual == expected, "detail": "digest matches" if actual == expected else f"digest mismatch: {actual}"}) + return checks +''' + if marker not in text: + raise SystemExit("compute input helper marker is missing") + text = text.replace(marker, helper + marker, 1) + text = text.replace( + ' inputs=tuple(dict(item) for item in payload.get("inputs", [])),\n', + ' inputs=_normalize_inputs(payload.get("inputs", [])),\n', + ) + local_marker = ''' checks = [ + {"name": "working-directory", "ready": Path(spec.working_directory).is_dir(), "detail": spec.working_directory}, + {"name": "executable", "ready": executable is not None, "detail": executable or "not found"}, + {"name": "resource-request-recorded", "ready": True, "detail": spec.resources.to_dict()}, + ] +''' + local_replacement = local_marker + " checks.extend(_input_preflight(spec))\n" + if local_marker in text and "checks.extend(_input_preflight(spec))" not in text[text.index(local_marker):text.index(local_marker) + len(local_marker) + 100]: + text = text.replace(local_marker, local_replacement, 1) + slurm_marker = ''' checks = [ + {"name": "working-directory", "ready": Path(spec.working_directory).is_dir(), "detail": spec.working_directory}, + {"name": "sbatch", "ready": sbatch is not None, "detail": sbatch or "not found"}, + {"name": "scancel", "ready": scancel is not None, "detail": scancel or "not found"}, + {"name": "status-command", "ready": sacct is not None or squeue is not None, "detail": sacct or squeue or "not found"}, + ] +''' + slurm_replacement = slurm_marker + " checks.extend(_input_preflight(spec))\n" + if slurm_marker in text: + text = text.replace(slurm_marker, slurm_replacement, 1) + path.write_text(text, encoding="utf-8") + + +def patch_pipeline_command_validation() -> None: + path = ROOT / "src" / "codex_science" / "pipeline_compiler.py" + text = path.read_text(encoding="utf-8") + if "def _validate_command" in text: + start = text.index("def _validate_command") + end = text.index("\n\ndef _write_json", start) + replacement = '''def _validate_command(command: list[str], index: int) -> None: + for argument in command: + lower = argument.lower() + if any(lower == flag or lower.startswith(flag + "=") for flag in SECRET_ARGUMENT_FLAGS): + raise ValueError(f"command_contract[{index}] contains a credential-bearing argument") + if "://" in argument and any( + token in lower + for token in ( + "token=", "password=", "secret=", "api_key=", "apikey=", + "access_key=", "client_secret=", "authorization=" + ) + ): + raise ValueError(f"command_contract[{index}] contains a credential-bearing URL") + +''' + text = text[:start] + replacement + text[end + 2:] + path.write_text(text, encoding="utf-8") + + +def patch_action_provider_records() -> None: + path = ROOT / "src" / "codex_science" / "action_connectors.py" + text = path.read_text(encoding="utf-8") + text = text.replace( + ' before = dict(adapter.snapshot(spec))\n before_sha = _fingerprint(before)\n', + ' before = dict(adapter.snapshot(spec))\n _json_value(before, "provider before state")\n before_sha = _fingerprint(before)\n', + ) + text = text.replace( + ' changes = dict(adapter.preview(spec, before))\n material = {\n', + ' changes = dict(adapter.preview(spec, before))\n _json_value(changes, "provider proposed changes")\n material = {\n', + ) + text = text.replace( + ' result = dict(adapter.execute(spec, preview))\n after = dict(result.get("after_state", result))\n', + ' result = dict(adapter.execute(spec, preview))\n _json_value(result, "provider execution result")\n after = dict(result.get("after_state", result))\n', + ) + path.write_text(text, encoding="utf-8") + + +def patch_experiment_integers() -> None: + path = ROOT / "src" / "codex_science" / "experiment_planner.py" + text = path.read_text(encoding="utf-8") + old = ''' batch_size = int(constraints.get("batch_size", 0)) + budget = _number(constraints.get("budget", 0), "budget") + group_cap = int(constraints.get("diversity_group_cap", constraints.get("scaffold_cap", batch_size))) + minimum_controls = int(constraints.get("minimum_controls", 0)) +''' + new = ''' batch_raw = constraints.get("batch_size", 0) + group_raw = constraints.get("diversity_group_cap", constraints.get("scaffold_cap", batch_raw)) + controls_raw = constraints.get("minimum_controls", 0) + if any(isinstance(item, bool) for item in (batch_raw, group_raw, controls_raw)): + raise ValueError("batch size, diversity cap, and minimum controls must be integers") + batch_size = int(batch_raw) + budget = _number(constraints.get("budget", 0), "budget") + group_cap = int(group_raw) + minimum_controls = int(controls_raw) +''' + if old in text: + text = text.replace(old, new, 1) + path.write_text(text, encoding="utf-8") + + +def main() -> int: + patch_artifact_runtime() + patch_compute_inputs() + patch_pipeline_command_validation() + patch_action_provider_records() + patch_experiment_integers() + print("core runtime v4 hardening: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From efdeb131c29fbe1bb4ec870a0cd74fb94053ebc8 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:54:43 +0900 Subject: [PATCH 84/87] Test final runtime hardening boundaries --- tests/test_runtime_hardening.py | 107 ++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 tests/test_runtime_hardening.py diff --git a/tests/test_runtime_hardening.py b/tests/test_runtime_hardening.py new file mode 100644 index 00000000..9e93c07f --- /dev/null +++ b/tests/test_runtime_hardening.py @@ -0,0 +1,107 @@ +import hashlib +import tempfile +import unittest +from pathlib import Path + +from codex_science.action_connectors import ActionSpec, build_preview +from codex_science.artifact_runtime import describe_runtime +from codex_science.compute_backends import JobSpec, LocalBackend +from codex_science.experiment_planner import plan_next_experiment +from codex_science.pipeline_compiler import _validate_command + + +class SecretAdapter: + connector_name = "secret-provider" + + def snapshot(self, spec): + return {"object": "x", "api_token": "must-not-record"} + + def preview(self, spec, before): + return {"change": "none"} + + def execute(self, spec, preview): + return {"after_state": {"object": "x"}} + + +class RuntimeHardeningTests(unittest.TestCase): + def test_artifact_runtime_has_absolute_view_limits(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + path = Path(tempdir) / "data.txt" + path.write_text("hello\n", encoding="utf-8") + with self.assertRaisesRegex(ValueError, "hard runtime ceiling"): + describe_runtime( + path, + artifact_path="data.txt", + kind="text", + max_bytes=16 * 1024 * 1024 + 1, + max_records=10, + ) + with self.assertRaisesRegex(ValueError, "hard runtime ceiling"): + describe_runtime( + path, + artifact_path="data.txt", + kind="text", + max_bytes=100, + max_records=10_001, + ) + + def test_local_preflight_verifies_declared_input_hash(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + root = Path(tempdir) + work = root / "work" + work.mkdir() + data = work / "input.json" + data.write_text('{"value": 1}\n', encoding="utf-8") + good = hashlib.sha256(data.read_bytes()).hexdigest() + spec = JobSpec.from_payload( + { + "backend": "local", + "name": "input-check", + "command": ["python", "-c", "print('ok')"], + "working_directory": str(work), + "inputs": [{"id": "input", "path": "input.json", "sha256": good}], + "outputs": [], + } + ) + checks = LocalBackend(root / "state").preflight(spec)["checks"] + input_check = next(item for item in checks if item["name"] == "input:input") + self.assertTrue(input_check["ready"]) + stale = JobSpec.from_payload({**spec.to_dict(), "inputs": [{"id": "input", "path": "input.json", "sha256": "0" * 64}]}) + result = LocalBackend(root / "other-state").preflight(stale) + input_check = next(item for item in result["checks"] if item["name"] == "input:input") + self.assertFalse(input_check["ready"]) + self.assertFalse(result["ready"]) + + def test_provider_state_with_secret_like_keys_is_rejected(self) -> None: + spec = ActionSpec.from_payload( + { + "connector": "secret-provider", + "operation": "read", + "mode": "read", + "target": "object/x", + "parameters": {}, + "requested_scopes": ["object:read"], + "idempotency_key": "secret-provider-read-x-v1", + } + ) + with self.assertRaisesRegex(ValueError, "credential-like"): + build_preview(SecretAdapter(), spec) + + def test_boolean_integer_constraints_are_rejected(self) -> None: + payload = { + "decision": "Select candidates.", + "objectives": [{"name": "score", "direction": "maximize", "weight": 1}], + "candidates": [{"id": "x", "properties": {"score": 1}, "cost": 0, "uncertainty": 0}], + "constraints": {"batch_size": True, "budget": 1, "diversity_group_cap": 1, "minimum_controls": 0}, + } + with self.assertRaisesRegex(ValueError, "must be integers"): + plan_next_experiment(payload) + + def test_any_credential_argument_flag_is_rejected(self) -> None: + for command in (["tool", "--token"], ["tool", "--password=value"], ["curl", "https://example.test/?authorization=value"]): + with self.subTest(command=command), self.assertRaisesRegex(ValueError, "credential"): + _validate_command(command, 0) + + +if __name__ == "__main__": + unittest.main() From daf43ab971461903eaa7209b87c87e82c0148cbd Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:55:23 +0900 Subject: [PATCH 85/87] Run ultimate native runtime v4 validation --- .../workflows/ultimate-native-runtime-v4.yml | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 .github/workflows/ultimate-native-runtime-v4.yml diff --git a/.github/workflows/ultimate-native-runtime-v4.yml b/.github/workflows/ultimate-native-runtime-v4.yml new file mode 100644 index 00000000..59c46861 --- /dev/null +++ b/.github/workflows/ultimate-native-runtime-v4.yml @@ -0,0 +1,102 @@ +name: Ultimate native scientific runtime v4 validation + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: ultimate-native-runtime-v4-${{ github.ref }} + cancel-in-progress: false + +jobs: + validate: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 120 + steps: + - name: Let earlier one-time jobs settle + run: sleep 180 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Apply any remaining idempotent integrations + run: | + [ ! -f scripts/patch_permanent_runtime_v4.py ] || uv run python scripts/patch_permanent_runtime_v4.py + [ ! -f scripts/harden_extended_runtime_v4.py ] || uv run python scripts/harden_extended_runtime_v4.py + [ ! -f scripts/integrate_review_action_runtime_v4.py ] || uv run python scripts/integrate_review_action_runtime_v4.py + [ ! -f scripts/harden_review_action_runtime_v4.py ] || uv run python scripts/harden_review_action_runtime_v4.py + [ ! -f scripts/harden_core_runtime_v4.py ] || uv run python scripts/harden_core_runtime_v4.py + - name: Remove all one-time Python files before compilation + run: | + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + rm -f scripts/repair_native_runtime_v4.py + rm -f scripts/seal_native_runtime_v4.py + rm -f scripts/integrate_extended_runtime_v4.py + rm -f scripts/patch_permanent_runtime_v4.py + rm -f scripts/harden_extended_runtime_v4.py + rm -f scripts/integrate_reviewer_runtime_v4.py + rm -f scripts/integrate_review_action_runtime_v4.py + rm -f scripts/harden_review_action_runtime_v4.py + rm -f scripts/harden_core_runtime_v4.py + - name: Regenerate audited inventory and wrappers + run: | + uv run python scripts/audit_skills.py + uv run python scripts/generate_wrappers.py + - name: Compile permanent source + run: uv run python -m compileall -q src scripts + - name: Run all new runtime tests + run: | + uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' + uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' + uv run python -m unittest discover -s tests -p 'test_project_store.py' + uv run python -m unittest discover -s tests -p 'test_compute_backends.py' + uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' + uv run python -m unittest discover -s tests -p 'test_pipeline_compiler_security.py' + uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' + uv run python -m unittest discover -s tests -p 'test_fair_export.py' + uv run python -m unittest discover -s tests -p 'test_reviewer_runtime.py' + uv run python -m unittest discover -s tests -p 'test_action_connectors.py' + uv run python -m unittest discover -s tests -p 'test_review_action_receipts.py' + uv run python -m unittest discover -s tests -p 'test_runtime_hardening.py' + - name: Run complete repository validation + run: ./scripts/check.sh fast + - name: Remove all one-time workflows + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f .github/workflows/seal-native-runtime-v4.yml + rm -f .github/workflows/seal-extended-native-runtime-v4.yml + rm -f .github/workflows/finish-native-runtime-v4.yml + rm -f .github/workflows/commit-permanent-runtime-v4.yml + rm -f .github/workflows/finalize-all-runtime-v4.yml + rm -f .github/workflows/finalize-complete-runtime-v4.yml + rm -f .github/workflows/ultimate-native-runtime-v4.yml + - name: Commit clean validated implementation + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Validate and finalize progressive scientific runtime v4" + git fetch origin "$branch" + git rebase "origin/$branch" + git push origin HEAD:"$branch" From c91cc4f968e5d345d8704b76cbb8675a89ce8cb9 Mon Sep 17 00:00:00 2001 From: Jaemin Sim <78335868+eightmm@users.noreply.github.com> Date: Tue, 21 Jul 2026 13:56:41 +0900 Subject: [PATCH 86/87] Validate runtime v4 on Python 3.11 and 3.12 --- .../validate-native-runtime-v4-receipt.yml | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 .github/workflows/validate-native-runtime-v4-receipt.yml diff --git a/.github/workflows/validate-native-runtime-v4-receipt.yml b/.github/workflows/validate-native-runtime-v4-receipt.yml new file mode 100644 index 00000000..fa9dab24 --- /dev/null +++ b/.github/workflows/validate-native-runtime-v4-receipt.yml @@ -0,0 +1,137 @@ +name: Validate native runtime v4 and record receipt + +on: + push: + branches: [agent/reference-runtime-v3] + pull_request: + +permissions: + contents: write + +concurrency: + group: validate-native-runtime-v4-receipt-${{ github.ref }} + cancel-in-progress: false + +jobs: + validate: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + timeout-minutes: 140 + steps: + - name: Let integration workflows finish first + run: sleep 300 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.head_ref || github.ref_name }} + submodules: recursive + fetch-depth: 0 + - name: Install uv and Python runtimes + uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + with: + python-version: "3.11" + - name: Ensure Python 3.12 is available + run: uv python install 3.12 + - name: Remove any remaining one-time Python files + run: | + rm -f scripts/integrate_native_runtime_v4.py + rm -f scripts/finalize_native_runtime_v4.py + rm -f scripts/apply_complete_native_runtime_v4.py + rm -f scripts/repair_native_runtime_v4.py + rm -f scripts/seal_native_runtime_v4.py + rm -f scripts/integrate_extended_runtime_v4.py + rm -f scripts/patch_permanent_runtime_v4.py + rm -f scripts/harden_extended_runtime_v4.py + rm -f scripts/integrate_reviewer_runtime_v4.py + rm -f scripts/integrate_review_action_runtime_v4.py + rm -f scripts/harden_review_action_runtime_v4.py + rm -f scripts/harden_core_runtime_v4.py + - name: Regenerate and validate generated state + run: | + uv run --python 3.11 python scripts/audit_skills.py + uv run --python 3.11 python scripts/generate_wrappers.py + ./scripts/check.sh inventory + ./scripts/check.sh wrappers + - name: Run complete Python 3.11 validation + run: ./scripts/check.sh fast + - name: Run complete Python 3.12 unit suite + run: uv run --python 3.12 python -m unittest discover -s tests + - name: Write deterministic validation receipt + run: | + mkdir -p reports + python - <<'PY' + from __future__ import annotations + import hashlib + import json + import subprocess + from datetime import datetime, timezone + from pathlib import Path + + def digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + root = Path('.') + payload = { + "schema_version": 1, + "status": "passed", + "validated_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace('+00:00', 'Z'), + "source_head": subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip(), + "python": ["3.11", "3.12"], + "checks": [ + "compile", + "complete unit suite on Python 3.11", + "complete unit suite on Python 3.12", + "inventory determinism", + "wrapper determinism", + "release and connector contracts", + "artifact and sidecar contracts", + "native skill reference audit", + "native skill maturity audit", + "reviewer benchmark", + "model registry contracts", + "deterministic SBDD acceptance", + "plugin and skill validation when system validators are available" + ], + "inventory_sha256": digest(root / 'catalog' / 'inventory.json'), + "model_registry_v2_sha256": digest(root / 'models' / 'registry-v2.json'), + "plugin_manifest_sha256": digest(root / '.codex-plugin' / 'plugin.json'), + "limitations": [ + "CodeQL and hosted public-source drift are separate workflows.", + "External live services, remote clusters, and provider write adapters are not exercised by this local deterministic receipt.", + "A passed software contract does not establish scientific truth or empirical model performance." + ] + } + material = json.dumps(payload, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode() + payload['fingerprint'] = hashlib.sha256(material).hexdigest() + (root / 'reports' / 'native-runtime-v4-validation.json').write_text( + json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + '\n', + encoding='utf-8' + ) + PY + - name: Remove every one-time workflow + run: | + rm -f .github/workflows/integrate-native-runtime-v4.yml + rm -f .github/workflows/finalize-native-runtime-v4.yml + rm -f .github/workflows/complete-native-runtime-v4.yml + rm -f .github/workflows/apply-complete-native-runtime-v4.yml + rm -f .github/workflows/seal-native-runtime-v4.yml + rm -f .github/workflows/seal-extended-native-runtime-v4.yml + rm -f .github/workflows/finish-native-runtime-v4.yml + rm -f .github/workflows/commit-permanent-runtime-v4.yml + rm -f .github/workflows/finalize-all-runtime-v4.yml + rm -f .github/workflows/finalize-complete-runtime-v4.yml + rm -f .github/workflows/ultimate-native-runtime-v4.yml + rm -f .github/workflows/validate-native-runtime-v4-receipt.yml + - name: Commit clean validated runtime and receipt + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "Validate progressive references and scientific runtime v4" + git fetch origin "$branch" + git rebase "origin/$branch" + git push origin HEAD:"$branch" From 5d9536bc4eb3da0bd7d91a86deef227225b10c25 Mon Sep 17 00:00:00 2001 From: eightmm Date: Tue, 21 Jul 2026 19:36:32 +0900 Subject: [PATCH 87/87] Fix runtime v4 validation --- .github/runtime-source-payload/manifest.json | 16 - .github/runtime-source-payload/part-00 | 1 - .github/runtime-source-payload/part-01 | 1 - .../apply-complete-native-runtime-v4.yml | 67 --- .../apply-project-artifact-runtime.yml | 185 -------- .../workflows/commit-permanent-runtime-v4.yml | 82 ---- .../workflows/complete-native-runtime-v4.yml | 118 ----- .github/workflows/finalize-all-runtime-v4.yml | 87 ---- .../finalize-complete-runtime-v4.yml | 96 ---- .../workflows/finalize-native-runtime-v4.yml | 59 --- .../workflows/finish-native-runtime-v4.yml | 82 ---- .../workflows/integrate-native-runtime-v4.yml | 54 --- .../seal-extended-native-runtime-v4.yml | 86 ---- .github/workflows/seal-native-runtime-v4.yml | 72 --- .../workflows/ultimate-native-runtime-v4.yml | 102 ----- .../validate-native-runtime-v4-receipt.yml | 137 ------ .../remote-scientific-compute/SKILL.md | 14 + .../references/index.json | 11 + catalog/inventory.json | 2 +- docs/NATIVE_RUNTIME_QUICKSTART.md | 8 + scripts/apply_complete_native_runtime_v4.py | 105 ----- scripts/finalize_native_runtime_v4.py | 103 ----- scripts/harden_core_runtime_v4.py | 228 ---------- scripts/harden_extended_runtime_v4.py | 100 ---- scripts/harden_review_action_runtime_v4.py | 49 -- scripts/integrate_extended_runtime_v4.py | 254 ----------- scripts/integrate_native_runtime_v4.py | 430 ------------------ scripts/integrate_review_action_runtime_v4.py | 241 ---------- scripts/integrate_reviewer_runtime_v4.py | 156 ------- scripts/patch_permanent_runtime_v4.py | 215 --------- scripts/repair_native_runtime_v4.py | 196 -------- scripts/seal_native_runtime_v4.py | 227 --------- skills/codex-science/SKILL.md | 2 + skills/codex-science/quality.json | 8 +- skills/codex-science/references/index.json | 44 ++ skills/science-provenance/SKILL.md | 2 + skills/science-provenance/quality.json | 6 +- .../science-provenance/references/index.json | 22 + skills/science-review/SKILL.md | 2 + skills/science-review/quality.json | 4 +- skills/science-review/references/index.json | 11 + src/codex_science/action_connectors.py | 55 ++- src/codex_science/advanced_sidecars.py | 64 +++ src/codex_science/artifact_runtime.py | 27 +- src/codex_science/compute_backends.py | 142 +++++- src/codex_science/experiment_planner.py | 11 +- src/codex_science/pipeline_compiler.py | 75 ++- src/codex_science/project_store.py | 3 +- src/codex_science/review_receipts.py | 9 +- src/codex_science/reviewer_runtime.py | 40 +- src/codex_science/skill_maturity.py | 24 +- 51 files changed, 554 insertions(+), 3581 deletions(-) delete mode 100644 .github/runtime-source-payload/manifest.json delete mode 100644 .github/runtime-source-payload/part-00 delete mode 100644 .github/runtime-source-payload/part-01 delete mode 100644 .github/workflows/apply-complete-native-runtime-v4.yml delete mode 100644 .github/workflows/apply-project-artifact-runtime.yml delete mode 100644 .github/workflows/commit-permanent-runtime-v4.yml delete mode 100644 .github/workflows/complete-native-runtime-v4.yml delete mode 100644 .github/workflows/finalize-all-runtime-v4.yml delete mode 100644 .github/workflows/finalize-complete-runtime-v4.yml delete mode 100644 .github/workflows/finalize-native-runtime-v4.yml delete mode 100644 .github/workflows/finish-native-runtime-v4.yml delete mode 100644 .github/workflows/integrate-native-runtime-v4.yml delete mode 100644 .github/workflows/seal-extended-native-runtime-v4.yml delete mode 100644 .github/workflows/seal-native-runtime-v4.yml delete mode 100644 .github/workflows/ultimate-native-runtime-v4.yml delete mode 100644 .github/workflows/validate-native-runtime-v4-receipt.yml delete mode 100644 scripts/apply_complete_native_runtime_v4.py delete mode 100644 scripts/finalize_native_runtime_v4.py delete mode 100644 scripts/harden_core_runtime_v4.py delete mode 100644 scripts/harden_extended_runtime_v4.py delete mode 100644 scripts/harden_review_action_runtime_v4.py delete mode 100644 scripts/integrate_extended_runtime_v4.py delete mode 100644 scripts/integrate_native_runtime_v4.py delete mode 100644 scripts/integrate_review_action_runtime_v4.py delete mode 100644 scripts/integrate_reviewer_runtime_v4.py delete mode 100644 scripts/patch_permanent_runtime_v4.py delete mode 100644 scripts/repair_native_runtime_v4.py delete mode 100644 scripts/seal_native_runtime_v4.py diff --git a/.github/runtime-source-payload/manifest.json b/.github/runtime-source-payload/manifest.json deleted file mode 100644 index e9bad0f7..00000000 --- a/.github/runtime-source-payload/manifest.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "base64_bytes": 47340, - "base64_sha256": "3c4180324bd1d4517f064acb5f83f09af3f36ceb33a4fab9dbfe0a66d9f20a7d", - "chunks": { - "part-00": "5db18cd1e958f139a932419f101326e969f811f6758b6d2638046bb93d7c6548", - "part-01": "ef249b8dc4cb25f7681ef76647d012460e8887d9edd5ea9615d4d1b8a7f112c2", - "part-02": "0c222e9abaf2d917181df35adf06a9b3f7f2f4f298a5ece01cc26e94bf3e3133", - "part-03": "e8e645129cf4152f9b665e2365a4177e546ca69eaa920c8f1b9b5bbb3f6aefcb", - "part-04": "717f56bc12a1297974cf6f4541923d22903463a88cf1c045ab240fd14bd953b9", - "part-05": "e9eecf989da3995cc1ed1354aea9cf152ab1df36e30136ca4fc66f0e7ff55d28", - "part-06": "25d01cfc209f4ad2dad886d20a8e9190e3f5b0679893394887b84d2bbe805468" - }, - "schema_version": 1, - "tar_bytes": 35505, - "tar_sha256": "5668332d63b158c630170ef80635636a7ae7a00b9761a7c3aa098f742853e25c" -} diff --git a/.github/runtime-source-payload/part-00 b/.github/runtime-source-payload/part-00 deleted file mode 100644 index 2915541d..00000000 --- a/.github/runtime-source-payload/part-00 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/runtime-source-payload/part-01 b/.github/runtime-source-payload/part-01 deleted file mode 100644 index 49c38e5a..00000000 --- a/.github/runtime-source-payload/part-01 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/.github/workflows/apply-complete-native-runtime-v4.yml b/.github/workflows/apply-complete-native-runtime-v4.yml deleted file mode 100644 index 0dc6e89f..00000000 --- a/.github/workflows/apply-complete-native-runtime-v4.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Apply complete native runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: apply-complete-native-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - apply: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Apply source and reference integration - run: uv run python scripts/apply_complete_native_runtime_v4.py - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile runtime - run: uv run python -m compileall -q src scripts - - name: Run focused runtime tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove all one-time runtime resources - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - - name: Commit clean implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Complete native artifact, project, compute, and skill maturity runtimes" - git push origin HEAD:"$branch" diff --git a/.github/workflows/apply-project-artifact-runtime.yml b/.github/workflows/apply-project-artifact-runtime.yml deleted file mode 100644 index f5f691b0..00000000 --- a/.github/workflows/apply-project-artifact-runtime.yml +++ /dev/null @@ -1,185 +0,0 @@ -name: Apply project and artifact runtime - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -jobs: - apply: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Verify and expand runtime payload - run: | - set -euo pipefail - archive=.github/project-artifact-runtime/payload.tgz - test -f "$archive" - file "$archive" - gzip -t "$archive" - echo "archive sha256=$(sha256sum "$archive" | cut -d' ' -f1)" - python - <<'PY' - import pathlib - import tarfile - - archive = pathlib.Path('.github/project-artifact-runtime/payload.tgz') - destination = pathlib.Path('.').resolve() - with tarfile.open(archive, mode='r:gz') as handle: - members = handle.getmembers() - if not members: - raise SystemExit('runtime archive is empty') - for member in members: - name = member.name.removeprefix('./') - if not name: - continue - target = (destination / name).resolve() - if not target.is_relative_to(destination): - raise SystemExit(f'archive member escapes repository: {member.name}') - if member.issym() or member.islnk(): - raise SystemExit(f'archive links are forbidden: {member.name}') - print(f'validated {len(members)} archive members') - PY - tar --extract --gzip --file "$archive" --directory . --no-same-owner --no-same-permissions - - name: Extend progressive reference maps - run: | - python - <<'PY' - import json - from pathlib import Path - - def add_reference(path: str, entry: dict) -> None: - target = Path(path) - payload = json.loads(target.read_text(encoding='utf-8')) - if not any(item.get('id') == entry['id'] for item in payload['references']): - payload['references'].append(entry) - target.write_text(json.dumps(payload, indent=2, sort_keys=True) + '\n', encoding='utf-8') - - add_reference( - 'skills/science-provenance/references/index.json', - { - 'id': 'artifact-runtime', - 'path': 'references/artifact-runtime.md', - 'purpose': 'Create typed offline artifact views, hash-bound selections, and non-executing transform proposals.', - 'read_when': [ - 'rendering a scientific artifact', - 'creating a typed artifact selection', - 'proposing an artifact edit', - 'planning a selective rerun from an annotation', - ], - 'required_before': [ - 'artifact-runtime-render', - 'artifact-selection', - 'transform-proposal', - ], - 'search_patterns': [ - '## Viewer routing', - '## Typed selections', - '## Transform proposal', - '## Rerun handoff', - ], - 'authority': 'repository-contract', - 'version': '1', - 'evidence_boundary': 'Derived views, selections, and proposals are not evidence that a transform executed or a scientific claim is valid.', - }, - ) - add_reference( - 'skills/codex-science/references/index.json', - { - 'id': 'project-evidence-store', - 'path': 'references/project-evidence-store.md', - 'purpose': 'Manage project-level run lineage, hypothesis forks, quantitative assertions, comparisons, and merge plans.', - 'read_when': [ - 'initializing a science project', - 'importing multiple reviewed runs', - 'forking a hypothesis or method branch', - 'adding a quantitative evidence assertion', - 'preparing a scientific merge plan', - ], - 'required_before': [ - 'project-run-import', - 'hypothesis-fork', - 'evidence-assertion', - 'project-merge-plan', - ], - 'search_patterns': [ - '## Project layout', - '## Fork a hypothesis', - '## Evidence assertion', - '## Compare and merge-plan', - ], - 'authority': 'repository-contract', - 'version': '1', - 'evidence_boundary': 'The project store indexes validated runs; it does not replace manifests, execute experiments, or prove claims.', - }, - ) - - provenance = Path('skills/science-provenance/SKILL.md') - text = provenance.read_text(encoding='utf-8') - marker = 'Read [the artifact contract](references/artifact-contract.md) before authoring a manifest, sidecar, or bundle-version change. Read [the large-artifact protocol](references/large-artifacts.md) before registering a directory, a file too large for whole-memory loading, a chunked dataset, model weights, or an immutable external object. Do not guess sidecar fields, Merkle rules, or invalidation semantics.' - replacement = marker + ' Read [the artifact runtime contract](references/artifact-runtime.md) before rendering a native artifact view, saving a typed selection, proposing an edit, or issuing a selective-rerun handoff.' - if 'references/artifact-runtime.md' not in text: - if marker not in text: - raise SystemExit('science-provenance reference marker is missing') - text = text.replace(marker, replacement, 1) - provenance.write_text(text, encoding='utf-8') - - coordinator = Path('skills/codex-science/SKILL.md') - text = coordinator.read_text(encoding='utf-8') - marker = 'Read [the catalog policy](references/catalog-policy.md) before selecting or inspecting an inactive catalog skill. Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run.' - replacement = 'Read [the catalog policy](references/catalog-policy.md) before selecting or inspecting an inactive catalog skill. Read [the project evidence-store contract](references/project-evidence-store.md) before importing multiple runs, forking a hypothesis, adding a quantitative assertion, or preparing a merge plan. Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run.' - if 'references/project-evidence-store.md' not in text: - if marker not in text: - raise SystemExit('codex-science reference marker is missing') - text = text.replace(marker, replacement, 1) - coordinator.write_text(text, encoding='utf-8') - - platform = Path('docs/PLATFORM_CONTRACTS.md') - text = platform.read_text(encoding='utf-8') - heading = '## Native artifact runtime and project evidence store' - if heading not in text: - text += '''\n\n## Native artifact runtime and project evidence store\n\nUse `scripts/render_artifact_runtime.py` to build a validated, offline runtime descriptor and HTML view for structures, molecules, genome records, AnnData containers, tables, figures, evidence graphs, trajectories, directories, and text. A viewer remains bounded: it reads a preview or metadata rather than loading a large scientific object wholesale.\n\nSelections are typed, artifact-hash-bound records. A transform proposal records the requested operation, structured parameters, affected steps, expected outputs, approval boundary, and deterministic fingerprint. A proposal does not mutate source bytes or claim that execution occurred. Save material selections and proposals as `artifact-selection` and `transform-proposal` sidecars.\n\nUse `scripts/science_project.py` to initialize a local SQLite project evidence store, import hash-validated runs, fork hypotheses, add quantitative evidence assertions with exact locators and independence groups, compare runs, and prepare non-executing merge plans. The project store is an index over immutable run bundles; manifests and artifact hashes remain authoritative.\n''' - platform.write_text(text, encoding='utf-8') - - status = Path('docs/IMPLEMENTATION_STATUS.md') - text = status.read_text(encoding='utf-8') - heading = '### Native artifact runtime and project memory' - if heading not in text: - insertion = '''\n### Native artifact runtime and project memory\n\n- Typed, bounded previews for structures, molecules, genome records, tables, figures, evidence graphs, trajectories, directories, and text.\n- Hash-bound selections and deterministic transform proposals that feed selective rerun planning without silently mutating artifacts.\n- Local SQLite project evidence store with run lineage, branches, quantitative assertions, comparisons, and merge plans.\n''' - marker = '\n## Deliberate boundaries\n' - if marker in text: - text = text.replace(marker, insertion + marker, 1) - else: - text += insertion - status.write_text(text, encoding='utf-8') - PY - - name: Regenerate deterministic catalog and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Remove one-time integration resources - run: | - rm -rf .github/project-artifact-runtime - rm -rf .github/runtime-source-payload - rm -f .github/workflows/apply-project-artifact-runtime.yml - - name: Commit integrated runtime source - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git commit -m "Add native artifact runtime and project evidence store" - git push origin HEAD:"$branch" - - name: Run complete verification - run: ./scripts/check.sh fast diff --git a/.github/workflows/commit-permanent-runtime-v4.yml b/.github/workflows/commit-permanent-runtime-v4.yml deleted file mode 100644 index 0e3cfdb3..00000000 --- a/.github/workflows/commit-permanent-runtime-v4.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Commit permanent native runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: commit-permanent-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - commit: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 90 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Apply source, reference, and validation integration - run: uv run python scripts/patch_permanent_runtime_v4.py - - name: Remove all one-time Python integration files before compilation - run: | - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - rm -f scripts/repair_native_runtime_v4.py - rm -f scripts/seal_native_runtime_v4.py - rm -f scripts/integrate_extended_runtime_v4.py - rm -f scripts/patch_permanent_runtime_v4.py - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile permanent source - run: uv run python -m compileall -q src scripts - - name: Run runtime and promotion tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' - uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' - uv run python -m unittest discover -s tests -p 'test_fair_export.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove all one-time integration workflows - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f .github/workflows/seal-native-runtime-v4.yml - rm -f .github/workflows/seal-extended-native-runtime-v4.yml - rm -f .github/workflows/finish-native-runtime-v4.yml - rm -f .github/workflows/commit-permanent-runtime-v4.yml - - name: Commit permanent implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Commit progressive references and complete scientific runtime v4" - git fetch origin "$branch" - git rebase "origin/$branch" - git push origin HEAD:"$branch" diff --git a/.github/workflows/complete-native-runtime-v4.yml b/.github/workflows/complete-native-runtime-v4.yml deleted file mode 100644 index 4b1583bf..00000000 --- a/.github/workflows/complete-native-runtime-v4.yml +++ /dev/null @@ -1,118 +0,0 @@ -name: Complete native runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: complete-native-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - complete: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Integrate source and progressive references - run: | - if [ -f scripts/integrate_native_runtime_v4.py ]; then - uv run python scripts/integrate_native_runtime_v4.py - fi - if [ -f scripts/finalize_native_runtime_v4.py ]; then - uv run python scripts/finalize_native_runtime_v4.py - fi - - name: Make local submission receipt deterministic - run: | - python - <<'PY' - from pathlib import Path - path = Path("src/codex_science/compute_backends.py") - text = path.read_text(encoding="utf-8") - old = ''' current = _read_json(job_dir / "state.json") - # A short command may already have written running or terminal state. - # Add the PID only while the original submitted state is still current; - # never overwrite a newer worker receipt. - if current.get("state") == "submitted": - state = _state_material( - spec, - job_id=job_id, - state="submitted", - submitted_at=submitted_at, - worker_pid=process.pid, - message="local worker started", - ) - _atomic_json(job_dir / "state.json", state) - else: - state = current - return state - ''' - old = '\n'.join(line[10:] if line.startswith(' ') else line for line in old.splitlines()) + '\n' - new = ''' current = _read_json(job_dir / "state.json") - # A short command may already have written running or terminal state. - # Persist the PID only while the original submitted state is current, - # but always return the historical submission receipt to the caller. - submitted_receipt = _state_material( - spec, - job_id=job_id, - state="submitted", - submitted_at=submitted_at, - worker_pid=process.pid, - message="local worker started", - ) - if current.get("state") == "submitted": - _atomic_json(job_dir / "state.json", submitted_receipt) - return submitted_receipt - ''' - new = '\n'.join(line[10:] if line.startswith(' ') else line for line in new.splitlines()) + '\n' - if new not in text: - if old not in text: - raise SystemExit("local submission receipt marker is missing") - path.write_text(text.replace(old, new, 1), encoding="utf-8") - PY - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile runtime - run: uv run python -m compileall -q src scripts - - name: Run focused runtime tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove one-time resources - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - - name: Commit completed implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No completion changes" - exit 0 - fi - git commit -m "Complete native artifact, project, compute, and skill maturity runtimes" - git push origin HEAD:"$branch" diff --git a/.github/workflows/finalize-all-runtime-v4.yml b/.github/workflows/finalize-all-runtime-v4.yml deleted file mode 100644 index 2f062852..00000000 --- a/.github/workflows/finalize-all-runtime-v4.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Finalize all scientific runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: finalize-all-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - finalize: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 90 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Apply permanent source and reference patches - run: uv run python scripts/patch_permanent_runtime_v4.py - - name: Harden pipeline draft generation - run: uv run python scripts/harden_extended_runtime_v4.py - - name: Remove all one-time Python files before compilation - run: | - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - rm -f scripts/repair_native_runtime_v4.py - rm -f scripts/seal_native_runtime_v4.py - rm -f scripts/integrate_extended_runtime_v4.py - rm -f scripts/patch_permanent_runtime_v4.py - rm -f scripts/harden_extended_runtime_v4.py - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile permanent source - run: uv run python -m compileall -q src scripts - - name: Run focused runtime tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler_security.py' - uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' - uv run python -m unittest discover -s tests -p 'test_fair_export.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove all one-time integration workflows - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f .github/workflows/seal-native-runtime-v4.yml - rm -f .github/workflows/seal-extended-native-runtime-v4.yml - rm -f .github/workflows/finish-native-runtime-v4.yml - rm -f .github/workflows/commit-permanent-runtime-v4.yml - rm -f .github/workflows/finalize-all-runtime-v4.yml - - name: Commit clean permanent implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Finalize progressive references and scientific runtime v4" - git fetch origin "$branch" - git rebase "origin/$branch" - git push origin HEAD:"$branch" diff --git a/.github/workflows/finalize-complete-runtime-v4.yml b/.github/workflows/finalize-complete-runtime-v4.yml deleted file mode 100644 index 360f48cb..00000000 --- a/.github/workflows/finalize-complete-runtime-v4.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Finalize complete scientific runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: finalize-complete-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - finalize: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 100 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Apply permanent source and progressive reference patches - run: | - uv run python scripts/patch_permanent_runtime_v4.py - uv run python scripts/harden_extended_runtime_v4.py - uv run python scripts/integrate_review_action_runtime_v4.py - uv run python scripts/harden_review_action_runtime_v4.py - - name: Remove one-time Python files before compilation - run: | - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - rm -f scripts/repair_native_runtime_v4.py - rm -f scripts/seal_native_runtime_v4.py - rm -f scripts/integrate_extended_runtime_v4.py - rm -f scripts/patch_permanent_runtime_v4.py - rm -f scripts/harden_extended_runtime_v4.py - rm -f scripts/integrate_reviewer_runtime_v4.py - rm -f scripts/integrate_review_action_runtime_v4.py - rm -f scripts/harden_review_action_runtime_v4.py - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile permanent source - run: uv run python -m compileall -q src scripts - - name: Run complete focused runtime suite - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler_security.py' - uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' - uv run python -m unittest discover -s tests -p 'test_fair_export.py' - uv run python -m unittest discover -s tests -p 'test_reviewer_runtime.py' - uv run python -m unittest discover -s tests -p 'test_action_connectors.py' - uv run python -m unittest discover -s tests -p 'test_review_action_receipts.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove all one-time integration workflows - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f .github/workflows/seal-native-runtime-v4.yml - rm -f .github/workflows/seal-extended-native-runtime-v4.yml - rm -f .github/workflows/finish-native-runtime-v4.yml - rm -f .github/workflows/commit-permanent-runtime-v4.yml - rm -f .github/workflows/finalize-all-runtime-v4.yml - rm -f .github/workflows/finalize-complete-runtime-v4.yml - - name: Commit clean permanent implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Finalize progressive references and complete scientific runtime v4" - git fetch origin "$branch" - git rebase "origin/$branch" - git push origin HEAD:"$branch" diff --git a/.github/workflows/finalize-native-runtime-v4.yml b/.github/workflows/finalize-native-runtime-v4.yml deleted file mode 100644 index a78ff18d..00000000 --- a/.github/workflows/finalize-native-runtime-v4.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Finalize native runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: native-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - finalize: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Finalize source and progressive references - run: uv run python scripts/finalize_native_runtime_v4.py - - name: Compile runtime - run: uv run python -m compileall -q src scripts - - name: Run focused runtime tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove one-time integration resources - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - - name: Commit finalized runtime - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No finalization changes" - exit 0 - fi - git commit -m "Finalize native artifact, project, compute, and skill maturity runtimes" - git push origin HEAD:"$branch" diff --git a/.github/workflows/finish-native-runtime-v4.yml b/.github/workflows/finish-native-runtime-v4.yml deleted file mode 100644 index 1f9e68e2..00000000 --- a/.github/workflows/finish-native-runtime-v4.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Finish native runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: finish-native-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - finish: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 75 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Apply all integration routes - run: | - uv run python scripts/seal_native_runtime_v4.py - uv run python scripts/integrate_extended_runtime_v4.py - - name: Remove one-time Python integration scripts before compilation - run: | - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - rm -f scripts/repair_native_runtime_v4.py - rm -f scripts/seal_native_runtime_v4.py - rm -f scripts/integrate_extended_runtime_v4.py - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile all permanent source - run: uv run python -m compileall -q src scripts - - name: Run native and extended runtime tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' - uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' - uv run python -m unittest discover -s tests -p 'test_fair_export.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove all one-time workflows - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f .github/workflows/seal-native-runtime-v4.yml - rm -f .github/workflows/seal-extended-native-runtime-v4.yml - rm -f .github/workflows/finish-native-runtime-v4.yml - - name: Commit permanent implementation only - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Finish progressive references and scientific runtime v4" - git fetch origin "$branch" - git rebase "origin/$branch" - git push origin HEAD:"$branch" diff --git a/.github/workflows/integrate-native-runtime-v4.yml b/.github/workflows/integrate-native-runtime-v4.yml deleted file mode 100644 index 4cae6051..00000000 --- a/.github/workflows/integrate-native-runtime-v4.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Integrate native runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -jobs: - integrate: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 45 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Integrate references and validation routes - run: uv run python scripts/integrate_native_runtime_v4.py - - name: Compile new runtime modules - run: uv run python -m compileall -q src scripts - - name: Run focused runtime tests - run: | - uv run python -m unittest \ - tests.test_artifact_runtime \ - tests.test_project_store \ - tests.test_compute_backends \ - tests.test_skill_maturity - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove one-time integration resources - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f scripts/integrate_native_runtime_v4.py - - name: Commit integrated implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No integration changes" - exit 0 - fi - git commit -m "Integrate native artifact, project, compute, and skill maturity runtimes" - git push origin HEAD:"$branch" diff --git a/.github/workflows/seal-extended-native-runtime-v4.yml b/.github/workflows/seal-extended-native-runtime-v4.yml deleted file mode 100644 index ebb7fb8b..00000000 --- a/.github/workflows/seal-extended-native-runtime-v4.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Seal extended native runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: seal-extended-native-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - seal: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 75 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Apply core native runtime integration - run: | - if [ -f scripts/seal_native_runtime_v4.py ]; then - uv run python scripts/seal_native_runtime_v4.py - elif [ -f scripts/apply_complete_native_runtime_v4.py ]; then - uv run python scripts/apply_complete_native_runtime_v4.py - elif [ -f scripts/integrate_native_runtime_v4.py ]; then - uv run python scripts/integrate_native_runtime_v4.py - fi - - name: Apply pipeline, experiment, and FAIR integration - run: uv run python scripts/integrate_extended_runtime_v4.py - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile all source - run: uv run python -m compileall -q src scripts - - name: Run native runtime tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' - uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' - uv run python -m unittest discover -s tests -p 'test_fair_export.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove all one-time integration resources - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f .github/workflows/seal-native-runtime-v4.yml - rm -f .github/workflows/seal-extended-native-runtime-v4.yml - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - rm -f scripts/repair_native_runtime_v4.py - rm -f scripts/seal_native_runtime_v4.py - rm -f scripts/integrate_extended_runtime_v4.py - - name: Commit clean extended implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Seal progressive references and extended scientific runtime v4" - git fetch origin "$branch" - git rebase "origin/$branch" - git push origin HEAD:"$branch" diff --git a/.github/workflows/seal-native-runtime-v4.yml b/.github/workflows/seal-native-runtime-v4.yml deleted file mode 100644 index 58ef4403..00000000 --- a/.github/workflows/seal-native-runtime-v4.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Seal native runtime v4 - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: seal-native-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - seal: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Seal source and progressive references - run: uv run python scripts/seal_native_runtime_v4.py - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile runtime - run: uv run python -m compileall -q src scripts - - name: Run focused runtime tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove one-time runtime resources - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f .github/workflows/seal-native-runtime-v4.yml - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - rm -f scripts/repair_native_runtime_v4.py - rm -f scripts/seal_native_runtime_v4.py - - name: Commit clean implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Seal native artifact, project, compute, and skill maturity runtimes" - git fetch origin "$branch" - git rebase "origin/$branch" - git push origin HEAD:"$branch" diff --git a/.github/workflows/ultimate-native-runtime-v4.yml b/.github/workflows/ultimate-native-runtime-v4.yml deleted file mode 100644 index 59c46861..00000000 --- a/.github/workflows/ultimate-native-runtime-v4.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Ultimate native scientific runtime v4 validation - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: ultimate-native-runtime-v4-${{ github.ref }} - cancel-in-progress: false - -jobs: - validate: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 120 - steps: - - name: Let earlier one-time jobs settle - run: sleep 180 - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Apply any remaining idempotent integrations - run: | - [ ! -f scripts/patch_permanent_runtime_v4.py ] || uv run python scripts/patch_permanent_runtime_v4.py - [ ! -f scripts/harden_extended_runtime_v4.py ] || uv run python scripts/harden_extended_runtime_v4.py - [ ! -f scripts/integrate_review_action_runtime_v4.py ] || uv run python scripts/integrate_review_action_runtime_v4.py - [ ! -f scripts/harden_review_action_runtime_v4.py ] || uv run python scripts/harden_review_action_runtime_v4.py - [ ! -f scripts/harden_core_runtime_v4.py ] || uv run python scripts/harden_core_runtime_v4.py - - name: Remove all one-time Python files before compilation - run: | - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - rm -f scripts/repair_native_runtime_v4.py - rm -f scripts/seal_native_runtime_v4.py - rm -f scripts/integrate_extended_runtime_v4.py - rm -f scripts/patch_permanent_runtime_v4.py - rm -f scripts/harden_extended_runtime_v4.py - rm -f scripts/integrate_reviewer_runtime_v4.py - rm -f scripts/integrate_review_action_runtime_v4.py - rm -f scripts/harden_review_action_runtime_v4.py - rm -f scripts/harden_core_runtime_v4.py - - name: Regenerate audited inventory and wrappers - run: | - uv run python scripts/audit_skills.py - uv run python scripts/generate_wrappers.py - - name: Compile permanent source - run: uv run python -m compileall -q src scripts - - name: Run all new runtime tests - run: | - uv run python -m unittest discover -s tests -p 'test_artifact_runtime.py' - uv run python -m unittest discover -s tests -p 'test_artifact_runtime_sidecars.py' - uv run python -m unittest discover -s tests -p 'test_project_store.py' - uv run python -m unittest discover -s tests -p 'test_compute_backends.py' - uv run python -m unittest discover -s tests -p 'test_skill_maturity.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler.py' - uv run python -m unittest discover -s tests -p 'test_pipeline_compiler_security.py' - uv run python -m unittest discover -s tests -p 'test_experiment_planner.py' - uv run python -m unittest discover -s tests -p 'test_fair_export.py' - uv run python -m unittest discover -s tests -p 'test_reviewer_runtime.py' - uv run python -m unittest discover -s tests -p 'test_action_connectors.py' - uv run python -m unittest discover -s tests -p 'test_review_action_receipts.py' - uv run python -m unittest discover -s tests -p 'test_runtime_hardening.py' - - name: Run complete repository validation - run: ./scripts/check.sh fast - - name: Remove all one-time workflows - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f .github/workflows/seal-native-runtime-v4.yml - rm -f .github/workflows/seal-extended-native-runtime-v4.yml - rm -f .github/workflows/finish-native-runtime-v4.yml - rm -f .github/workflows/commit-permanent-runtime-v4.yml - rm -f .github/workflows/finalize-all-runtime-v4.yml - rm -f .github/workflows/finalize-complete-runtime-v4.yml - rm -f .github/workflows/ultimate-native-runtime-v4.yml - - name: Commit clean validated implementation - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Validate and finalize progressive scientific runtime v4" - git fetch origin "$branch" - git rebase "origin/$branch" - git push origin HEAD:"$branch" diff --git a/.github/workflows/validate-native-runtime-v4-receipt.yml b/.github/workflows/validate-native-runtime-v4-receipt.yml deleted file mode 100644 index fa9dab24..00000000 --- a/.github/workflows/validate-native-runtime-v4-receipt.yml +++ /dev/null @@ -1,137 +0,0 @@ -name: Validate native runtime v4 and record receipt - -on: - push: - branches: [agent/reference-runtime-v3] - pull_request: - -permissions: - contents: write - -concurrency: - group: validate-native-runtime-v4-receipt-${{ github.ref }} - cancel-in-progress: false - -jobs: - validate: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - timeout-minutes: 140 - steps: - - name: Let integration workflows finish first - run: sleep 300 - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ github.head_ref || github.ref_name }} - submodules: recursive - fetch-depth: 0 - - name: Install uv and Python runtimes - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - with: - python-version: "3.11" - - name: Ensure Python 3.12 is available - run: uv python install 3.12 - - name: Remove any remaining one-time Python files - run: | - rm -f scripts/integrate_native_runtime_v4.py - rm -f scripts/finalize_native_runtime_v4.py - rm -f scripts/apply_complete_native_runtime_v4.py - rm -f scripts/repair_native_runtime_v4.py - rm -f scripts/seal_native_runtime_v4.py - rm -f scripts/integrate_extended_runtime_v4.py - rm -f scripts/patch_permanent_runtime_v4.py - rm -f scripts/harden_extended_runtime_v4.py - rm -f scripts/integrate_reviewer_runtime_v4.py - rm -f scripts/integrate_review_action_runtime_v4.py - rm -f scripts/harden_review_action_runtime_v4.py - rm -f scripts/harden_core_runtime_v4.py - - name: Regenerate and validate generated state - run: | - uv run --python 3.11 python scripts/audit_skills.py - uv run --python 3.11 python scripts/generate_wrappers.py - ./scripts/check.sh inventory - ./scripts/check.sh wrappers - - name: Run complete Python 3.11 validation - run: ./scripts/check.sh fast - - name: Run complete Python 3.12 unit suite - run: uv run --python 3.12 python -m unittest discover -s tests - - name: Write deterministic validation receipt - run: | - mkdir -p reports - python - <<'PY' - from __future__ import annotations - import hashlib - import json - import subprocess - from datetime import datetime, timezone - from pathlib import Path - - def digest(path: Path) -> str: - return hashlib.sha256(path.read_bytes()).hexdigest() - - root = Path('.') - payload = { - "schema_version": 1, - "status": "passed", - "validated_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace('+00:00', 'Z'), - "source_head": subprocess.check_output(['git', 'rev-parse', 'HEAD'], text=True).strip(), - "python": ["3.11", "3.12"], - "checks": [ - "compile", - "complete unit suite on Python 3.11", - "complete unit suite on Python 3.12", - "inventory determinism", - "wrapper determinism", - "release and connector contracts", - "artifact and sidecar contracts", - "native skill reference audit", - "native skill maturity audit", - "reviewer benchmark", - "model registry contracts", - "deterministic SBDD acceptance", - "plugin and skill validation when system validators are available" - ], - "inventory_sha256": digest(root / 'catalog' / 'inventory.json'), - "model_registry_v2_sha256": digest(root / 'models' / 'registry-v2.json'), - "plugin_manifest_sha256": digest(root / '.codex-plugin' / 'plugin.json'), - "limitations": [ - "CodeQL and hosted public-source drift are separate workflows.", - "External live services, remote clusters, and provider write adapters are not exercised by this local deterministic receipt.", - "A passed software contract does not establish scientific truth or empirical model performance." - ] - } - material = json.dumps(payload, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode() - payload['fingerprint'] = hashlib.sha256(material).hexdigest() - (root / 'reports' / 'native-runtime-v4-validation.json').write_text( - json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + '\n', - encoding='utf-8' - ) - PY - - name: Remove every one-time workflow - run: | - rm -f .github/workflows/integrate-native-runtime-v4.yml - rm -f .github/workflows/finalize-native-runtime-v4.yml - rm -f .github/workflows/complete-native-runtime-v4.yml - rm -f .github/workflows/apply-complete-native-runtime-v4.yml - rm -f .github/workflows/seal-native-runtime-v4.yml - rm -f .github/workflows/seal-extended-native-runtime-v4.yml - rm -f .github/workflows/finish-native-runtime-v4.yml - rm -f .github/workflows/commit-permanent-runtime-v4.yml - rm -f .github/workflows/finalize-all-runtime-v4.yml - rm -f .github/workflows/finalize-complete-runtime-v4.yml - rm -f .github/workflows/ultimate-native-runtime-v4.yml - rm -f .github/workflows/validate-native-runtime-v4-receipt.yml - - name: Commit clean validated runtime and receipt - run: | - branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "Validate progressive references and scientific runtime v4" - git fetch origin "$branch" - git rebase "origin/$branch" - git push origin HEAD:"$branch" diff --git a/authored-skills/remote-scientific-compute/SKILL.md b/authored-skills/remote-scientific-compute/SKILL.md index d798cd12..bc74273c 100644 --- a/authored-skills/remote-scientific-compute/SKILL.md +++ b/authored-skills/remote-scientific-compute/SKILL.md @@ -12,6 +12,8 @@ protecting credentials, login nodes, private data, budgets, and reproducibility. Read [the backend checklists](references/backends.md) for the selected SSH, Slurm/HPC, cloud GPU, or object-storage route before staging, submitting, allocating, or writing remotely. Do not infer scheduler flags, cost controls, storage semantics, or cleanup procedures from a backend name. +Read [the durable job runtime contract](references/job-runtime.md) before constructing, approving, submitting, polling, cancelling, or collecting a local or Slurm job. A process receipt records execution state; it is not scientific acceptance. + Record the reference version and SHA-256 in the run when it controls a submission or transfer. The backend reference governs execution safety; it is not evidence for the scientific result. ## Inspect without changing state @@ -74,3 +76,15 @@ Stop for unknown ownership, host-key changes, missing authorization, secrets in planned command, unapproved private-data transfer, unavailable cancellation, uncapped paid resources, a full filesystem, or a resource request unsuitable for the target. Report the blocker without weakening the boundary. + +## Decision contract + +Before execution, record the target, transfer boundary, argv or workflow, input hashes, runtime or container, resource and wall-time caps, cost cap, checkpoints, cancellation, outputs, and scientific acceptance criteria. Remote writes or allocation require explicit approval covering the exact job spec. + +## Outputs + +Save the job spec, preflight and approval receipts, job ID and states, stdout and stderr hashes, failure class, checkpoint references, collected output hashes, and `$science-provenance` and `$science-review` handoffs. + +## Boundaries + +Do not probe credentials, submit to unapproved accounts or partitions, stage sensitive data without approval, busy-poll, hide failed attempts, or treat process exit zero as scientific success. diff --git a/authored-skills/remote-scientific-compute/references/index.json b/authored-skills/remote-scientific-compute/references/index.json index 03e613ba..85ae5eb0 100644 --- a/authored-skills/remote-scientific-compute/references/index.json +++ b/authored-skills/remote-scientific-compute/references/index.json @@ -13,6 +13,17 @@ "authority": "repository-contract", "version": "1", "evidence_boundary": "Controls remote execution safety; it does not validate the scientific method or result." + }, + { + "id": "job-runtime", + "path": "references/job-runtime.md", + "purpose": "Durable local and Slurm job specs, approvals, submission, status, cancellation, checkpoint, and output collection receipts.", + "read_when": ["before constructing a durable job spec", "before submitting, polling, cancelling, or collecting a local or Slurm job", "before approving a non-local resource allocation"], + "required_before": ["calling scripts/science_job.py", "submitting work through LocalBackend or SlurmBackend"], + "search_patterns": ["## Job spec", "## Local execution", "## Approval receipt", "## Slurm execution", "## Checkpoint and resume", "## Failure handling"], + "authority": "Codex Science first-party compute runtime contract", + "version": "1", + "evidence_boundary": "A job receipt proves execution state and collected hashes, not scientific success." } ] } diff --git a/catalog/inventory.json b/catalog/inventory.json index d222bb3b..7340a64e 100644 --- a/catalog/inventory.json +++ b/catalog/inventory.json @@ -1314,7 +1314,7 @@ { "description": "Run reproducible scientific workloads through an existing SSH host, Slurm/HPC cluster, cloud GPU provider, or remote object storage. Use for remote execution, job submission and monitoring, checkpointed long runs, GPU allocation, data staging, result retrieval, queue diagnosis, or resuming work that can outlive the current Codex task.", "executable_count": 0, - "instruction_line_count": 76, + "instruction_line_count": 90, "license": "Apache-2.0", "name": "cx-remote-scientific-compute", "path": "authored-skills/remote-scientific-compute", diff --git a/docs/NATIVE_RUNTIME_QUICKSTART.md b/docs/NATIVE_RUNTIME_QUICKSTART.md index bcf748f8..0e60b9e1 100644 --- a/docs/NATIVE_RUNTIME_QUICKSTART.md +++ b/docs/NATIVE_RUNTIME_QUICKSTART.md @@ -117,3 +117,11 @@ Maturity is computed from actual instructions, progressive references, declared ## Completion boundary These runtime commands improve inspection, lineage, execution, and auditability. Scientific completion still requires a prespecified decision contract, valid domain methods, material evidence, uncertainty, and independent review. + +## 6. Prepare an independent review packet + +Read `skills/science-review/references/independent-review-runtime.md`, then use `scripts/science_reviewer.py prepare` and `finalize`. A producer second pass is not independent, and reproduction requires a separate execution. + +## 7. Validate an authenticated connector action + +Read `skills/codex-science/references/authenticated-connector-packs.md`. Use `scripts/connector_action_contract.py` for spec, preview, and approval validation. Provider execution belongs to an optional adapter pack; credentials never enter the action JSON. diff --git a/scripts/apply_complete_native_runtime_v4.py b/scripts/apply_complete_native_runtime_v4.py deleted file mode 100644 index 8f5c3b5e..00000000 --- a/scripts/apply_complete_native_runtime_v4.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 -"""Apply all native runtime v4 integrations and correctness patches idempotently.""" - -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def run_optional(path: Path) -> None: - if path.is_file(): - subprocess.run([sys.executable, str(path)], cwd=ROOT, check=True) - - -def patch_compute_submission() -> None: - path = ROOT / "src" / "codex_science" / "compute_backends.py" - text = path.read_text(encoding="utf-8") - class_start = text.index("class LocalBackend:") - submit_start = text.index(" def submit(", class_start) - process_start = text.index(" process = subprocess.Popen(", submit_start) - status_start = text.index("\n def status(", process_start) - section = text[process_start:status_start] - if "return submitted_receipt" in section: - return - candidates = [index for token in ("\n current = _read_json", "\n state = _state_material") if (index := section.find(token)) >= 0] - if not candidates: - raise SystemExit("cannot locate LocalBackend submission receipt tail") - tail_start = min(candidates) - replacement = ''' - current = _read_json(job_dir / "state.json") - # A short command may already have written running or terminal state. - # Persist the PID only while the original submitted state is current, - # but always return the historical submission receipt to the caller. - submitted_receipt = _state_material( - spec, - job_id=job_id, - state="submitted", - submitted_at=submitted_at, - worker_pid=process.pid, - message="local worker started", - ) - if current.get("state") == "submitted": - _atomic_json(job_dir / "state.json", submitted_receipt) - return submitted_receipt -''' - new_section = section[:tail_start] + replacement - path.write_text(text[:process_start] + new_section + text[status_start:], encoding="utf-8") - - -def patch_core_source() -> None: - artifact = ROOT / "src" / "codex_science" / "artifact_runtime.py" - text = artifact.read_text(encoding="utf-8") - old = " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n" - new = " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n" - if old in text: - artifact.write_text(text.replace(old, new, 1), encoding="utf-8") - elif new not in text: - raise SystemExit("artifact runtime descriptor construction marker is missing") - - cli = ROOT / "scripts" / "science_project.py" - text = cli.read_text(encoding="utf-8") - if "import sqlite3\n" not in text: - text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) - text = text.replace( - " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", - " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", - ) - cli.write_text(text, encoding="utf-8") - - maturity = ROOT / "src" / "codex_science" / "skill_maturity.py" - text = maturity.read_text(encoding="utf-8") - text = text.replace(' entries = payload.get("references")\n', ' entries = payload.get("references", payload.get("entries"))\n') - old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" - has_review = "$science-review" in text or skill_dir.name == "science-review" -''' - new = ''' if skill_dir.name in CORE_SECTION_ALIASES: - # Core coordinator/provenance/reviewer skills collectively implement - # the handoff and are not required to invoke themselves by token. - has_provenance = True - has_review = True - else: - has_provenance = "$science-provenance" in text - has_review = "$science-review" in text -''' - if old in text: - text = text.replace(old, new, 1) - elif new not in text: - raise SystemExit("skill maturity core handoff marker is missing") - maturity.write_text(text, encoding="utf-8") - - -def main() -> int: - run_optional(ROOT / "scripts" / "integrate_native_runtime_v4.py") - run_optional(ROOT / "scripts" / "finalize_native_runtime_v4.py") - patch_core_source() - patch_compute_submission() - print("complete native runtime v4 source: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/finalize_native_runtime_v4.py b/scripts/finalize_native_runtime_v4.py deleted file mode 100644 index e5db7c2f..00000000 --- a/scripts/finalize_native_runtime_v4.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python3 -"""Idempotent final correctness patches for native runtime v4.""" - -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def replace_if_present(path: Path, old: str, new: str) -> None: - text = path.read_text(encoding="utf-8") - if new in text: - return - if old not in text: - raise SystemExit(f"expected patch marker missing: {path}: {old[:80]!r}") - path.write_text(text.replace(old, new, 1), encoding="utf-8") - - -def main() -> int: - integration = ROOT / "scripts" / "integrate_native_runtime_v4.py" - if integration.is_file(): - subprocess.run([sys.executable, str(integration)], cwd=ROOT, check=True) - - artifact_runtime = ROOT / "src" / "codex_science" / "artifact_runtime.py" - replace_if_present( - artifact_runtime, - " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n", - " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n", - ) - - project_cli = ROOT / "scripts" / "science_project.py" - text = project_cli.read_text(encoding="utf-8") - if "import sqlite3\n" not in text: - text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) - text = text.replace( - " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", - " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", - ) - project_cli.write_text(text, encoding="utf-8") - - maturity = ROOT / "src" / "codex_science" / "skill_maturity.py" - text = maturity.read_text(encoding="utf-8") - text = text.replace( - ' entries = payload.get("references")\n', - ' entries = payload.get("references", payload.get("entries"))\n', - ) - old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" - has_review = "$science-review" in text or skill_dir.name == "science-review" -''' - new = ''' if skill_dir.name in CORE_SECTION_ALIASES: - # The three core skills collectively implement the provenance/review - # handoff; do not require them to invoke themselves by token. - has_provenance = True - has_review = True - else: - has_provenance = "$science-provenance" in text - has_review = "$science-review" in text -''' - if new not in text: - if old not in text: - raise SystemExit("skill maturity handoff marker is missing") - text = text.replace(old, new, 1) - maturity.write_text(text, encoding="utf-8") - - compute = ROOT / "src" / "codex_science" / "compute_backends.py" - text = compute.read_text(encoding="utf-8") - old = ''' state = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at, worker_pid=process.pid, message="local worker started") - _atomic_json(job_dir / "state.json", state) - return state -''' - new = ''' current = _read_json(job_dir / "state.json") - # A short command may already have written running or terminal state. - # Add the PID only while the original submitted state is still current; - # never overwrite a newer worker receipt. - if current.get("state") == "submitted": - state = _state_material( - spec, - job_id=job_id, - state="submitted", - submitted_at=submitted_at, - worker_pid=process.pid, - message="local worker started", - ) - _atomic_json(job_dir / "state.json", state) - else: - state = current - return state -''' - if new not in text: - if old not in text: - raise SystemExit("local submit race marker is missing") - text = text.replace(old, new, 1) - compute.write_text(text, encoding="utf-8") - - print("native runtime v4 finalization: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/harden_core_runtime_v4.py b/scripts/harden_core_runtime_v4.py deleted file mode 100644 index f57540c9..00000000 --- a/scripts/harden_core_runtime_v4.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/env python3 -"""Apply final resource, input-integrity, and secret-boundary hardening.""" - -from __future__ import annotations - -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def patch_artifact_runtime() -> None: - path = ROOT / "src" / "codex_science" / "artifact_runtime.py" - text = path.read_text(encoding="utf-8") - if "MAX_RUNTIME_BYTES" not in text: - marker = "VIEWER_SELECTIONS = {\n" - index = text.index(marker) - end = text.index("\n}\n", index) + 3 - text = text[:end] + "\nMAX_RUNTIME_BYTES = 16 * 1024 * 1024\nMAX_RUNTIME_RECORDS = 10_000\nMAX_SELECTION_BYTES = 64 * 1024\nMAX_PROPOSAL_PARAMETER_BYTES = 64 * 1024\nMAX_REASON_CHARACTERS = 4_000\n" + text[end:] - old = ''' if max_bytes < 1 or max_records < 1: - raise ValueError("max_bytes and max_records must be positive") -''' - new = ''' if max_bytes < 1 or max_records < 1: - raise ValueError("max_bytes and max_records must be positive") - if max_bytes > MAX_RUNTIME_BYTES: - raise ValueError(f"max_bytes exceeds hard runtime ceiling: {MAX_RUNTIME_BYTES}") - if max_records > MAX_RUNTIME_RECORDS: - raise ValueError(f"max_records exceeds hard runtime ceiling: {MAX_RUNTIME_RECORDS}") -''' - if old in text: - text = text.replace(old, new, 1) - old = ''' reason = _text(reason, "selection reason") - material = { -''' - new = ''' reason = _text(reason, "selection reason") - if len(reason) > MAX_REASON_CHARACTERS: - raise ValueError("selection reason is too long") - if len(_canonical_bytes(selector)) > MAX_SELECTION_BYTES: - raise ValueError("selection payload exceeds the runtime limit") - material = { -''' - if old in text: - text = text.replace(old, new, 1) - old = ''' reason = _text(reason, "proposal reason") - steps = sorted({_safe_relative(item, "affected step") for item in affected_steps}) -''' - new = ''' reason = _text(reason, "proposal reason") - if len(reason) > MAX_REASON_CHARACTERS: - raise ValueError("proposal reason is too long") - if len(_canonical_bytes(parameters)) > MAX_PROPOSAL_PARAMETER_BYTES: - raise ValueError("proposal parameters exceed the runtime limit") - steps = sorted({_safe_relative(item, "affected step") for item in affected_steps}) -''' - if old in text: - text = text.replace(old, new, 1) - path.write_text(text, encoding="utf-8") - - -def patch_compute_inputs() -> None: - path = ROOT / "src" / "codex_science" / "compute_backends.py" - text = path.read_text(encoding="utf-8") - if "def _normalize_inputs" not in text: - marker = "\n\n@dataclass(frozen=True)\nclass ResourceRequest:\n" - helper = ''' - -def _normalize_inputs(value: Any) -> tuple[dict[str, Any], ...]: - if value is None: - return () - if not isinstance(value, list): - raise ValueError("inputs must be a list") - results: list[dict[str, Any]] = [] - ids: set[str] = set() - for index, raw in enumerate(value): - if not isinstance(raw, Mapping): - raise ValueError(f"input {index} must be an object") - item = dict(raw) - input_id = _text(item.get("id", f"input-{index}"), f"input {index} id") - if input_id in ids: - raise ValueError(f"duplicate input ID: {input_id}") - ids.add(input_id) - item["id"] = input_id - if item.get("path") is not None: - item["path"] = _safe_relative(str(item["path"]), f"input {input_id} path") - elif not str(item.get("identifier", "")).strip(): - raise ValueError(f"input {input_id} needs path or identifier") - if item.get("sha256") is not None: - item["sha256"] = _sha(item["sha256"], f"input {input_id} sha256") - if item.get("root_sha256") is not None: - item["root_sha256"] = _sha(item["root_sha256"], f"input {input_id} root_sha256") - results.append(item) - return tuple(results) - - -def _input_preflight(spec: "JobSpec") -> list[dict[str, Any]]: - checks: list[dict[str, Any]] = [] - working = Path(spec.working_directory).resolve() - for item in spec.inputs: - input_id = str(item["id"]) - if item.get("path") is None: - checks.append({"name": f"input:{input_id}", "ready": True, "detail": "external identifier recorded"}) - continue - target = (working / str(item["path"])).resolve() - if not target.is_relative_to(working): - checks.append({"name": f"input:{input_id}", "ready": False, "detail": "path escapes working directory"}) - continue - if not target.exists(): - checks.append({"name": f"input:{input_id}", "ready": False, "detail": "missing"}) - continue - expected = item.get("sha256") or item.get("root_sha256") - if expected is None: - checks.append({"name": f"input:{input_id}", "ready": True, "detail": "exists; no digest declared"}) - continue - if target.is_file(): - actual, _size = stream_sha256(target) - elif target.is_dir(): - actual = describe_directory(target).root_sha256 - else: - checks.append({"name": f"input:{input_id}", "ready": False, "detail": "unsupported filesystem object"}) - continue - checks.append({"name": f"input:{input_id}", "ready": actual == expected, "detail": "digest matches" if actual == expected else f"digest mismatch: {actual}"}) - return checks -''' - if marker not in text: - raise SystemExit("compute input helper marker is missing") - text = text.replace(marker, helper + marker, 1) - text = text.replace( - ' inputs=tuple(dict(item) for item in payload.get("inputs", [])),\n', - ' inputs=_normalize_inputs(payload.get("inputs", [])),\n', - ) - local_marker = ''' checks = [ - {"name": "working-directory", "ready": Path(spec.working_directory).is_dir(), "detail": spec.working_directory}, - {"name": "executable", "ready": executable is not None, "detail": executable or "not found"}, - {"name": "resource-request-recorded", "ready": True, "detail": spec.resources.to_dict()}, - ] -''' - local_replacement = local_marker + " checks.extend(_input_preflight(spec))\n" - if local_marker in text and "checks.extend(_input_preflight(spec))" not in text[text.index(local_marker):text.index(local_marker) + len(local_marker) + 100]: - text = text.replace(local_marker, local_replacement, 1) - slurm_marker = ''' checks = [ - {"name": "working-directory", "ready": Path(spec.working_directory).is_dir(), "detail": spec.working_directory}, - {"name": "sbatch", "ready": sbatch is not None, "detail": sbatch or "not found"}, - {"name": "scancel", "ready": scancel is not None, "detail": scancel or "not found"}, - {"name": "status-command", "ready": sacct is not None or squeue is not None, "detail": sacct or squeue or "not found"}, - ] -''' - slurm_replacement = slurm_marker + " checks.extend(_input_preflight(spec))\n" - if slurm_marker in text: - text = text.replace(slurm_marker, slurm_replacement, 1) - path.write_text(text, encoding="utf-8") - - -def patch_pipeline_command_validation() -> None: - path = ROOT / "src" / "codex_science" / "pipeline_compiler.py" - text = path.read_text(encoding="utf-8") - if "def _validate_command" in text: - start = text.index("def _validate_command") - end = text.index("\n\ndef _write_json", start) - replacement = '''def _validate_command(command: list[str], index: int) -> None: - for argument in command: - lower = argument.lower() - if any(lower == flag or lower.startswith(flag + "=") for flag in SECRET_ARGUMENT_FLAGS): - raise ValueError(f"command_contract[{index}] contains a credential-bearing argument") - if "://" in argument and any( - token in lower - for token in ( - "token=", "password=", "secret=", "api_key=", "apikey=", - "access_key=", "client_secret=", "authorization=" - ) - ): - raise ValueError(f"command_contract[{index}] contains a credential-bearing URL") - -''' - text = text[:start] + replacement + text[end + 2:] - path.write_text(text, encoding="utf-8") - - -def patch_action_provider_records() -> None: - path = ROOT / "src" / "codex_science" / "action_connectors.py" - text = path.read_text(encoding="utf-8") - text = text.replace( - ' before = dict(adapter.snapshot(spec))\n before_sha = _fingerprint(before)\n', - ' before = dict(adapter.snapshot(spec))\n _json_value(before, "provider before state")\n before_sha = _fingerprint(before)\n', - ) - text = text.replace( - ' changes = dict(adapter.preview(spec, before))\n material = {\n', - ' changes = dict(adapter.preview(spec, before))\n _json_value(changes, "provider proposed changes")\n material = {\n', - ) - text = text.replace( - ' result = dict(adapter.execute(spec, preview))\n after = dict(result.get("after_state", result))\n', - ' result = dict(adapter.execute(spec, preview))\n _json_value(result, "provider execution result")\n after = dict(result.get("after_state", result))\n', - ) - path.write_text(text, encoding="utf-8") - - -def patch_experiment_integers() -> None: - path = ROOT / "src" / "codex_science" / "experiment_planner.py" - text = path.read_text(encoding="utf-8") - old = ''' batch_size = int(constraints.get("batch_size", 0)) - budget = _number(constraints.get("budget", 0), "budget") - group_cap = int(constraints.get("diversity_group_cap", constraints.get("scaffold_cap", batch_size))) - minimum_controls = int(constraints.get("minimum_controls", 0)) -''' - new = ''' batch_raw = constraints.get("batch_size", 0) - group_raw = constraints.get("diversity_group_cap", constraints.get("scaffold_cap", batch_raw)) - controls_raw = constraints.get("minimum_controls", 0) - if any(isinstance(item, bool) for item in (batch_raw, group_raw, controls_raw)): - raise ValueError("batch size, diversity cap, and minimum controls must be integers") - batch_size = int(batch_raw) - budget = _number(constraints.get("budget", 0), "budget") - group_cap = int(group_raw) - minimum_controls = int(controls_raw) -''' - if old in text: - text = text.replace(old, new, 1) - path.write_text(text, encoding="utf-8") - - -def main() -> int: - patch_artifact_runtime() - patch_compute_inputs() - patch_pipeline_command_validation() - patch_action_provider_records() - patch_experiment_integers() - print("core runtime v4 hardening: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/harden_extended_runtime_v4.py b/scripts/harden_extended_runtime_v4.py deleted file mode 100644 index da0e3901..00000000 --- a/scripts/harden_extended_runtime_v4.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 -"""Harden generated pipeline drafts against secret and private-input propagation.""" - -from __future__ import annotations - -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def main() -> int: - path = ROOT / "src" / "codex_science" / "pipeline_compiler.py" - text = path.read_text(encoding="utf-8") - if "SECRET_ARGUMENT_FLAGS" not in text: - marker = 'SKILL_NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")\n' - addition = '''SKILL_NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$") -SECRET_ARGUMENT_FLAGS = { - "--token", "--password", "--secret", "--credential", "--api-key", - "--apikey", "--private-key", "--access-key", "--client-secret" -} -SECRET_KEY_FRAGMENTS = { - "token", "password", "secret", "credential", "private_key", "api_key", - "apikey", "access_key", "client_secret" -} -''' - if marker not in text: - raise SystemExit("pipeline compiler constant marker is missing") - text = text.replace(marker, addition, 1) - if "def _redact_mapping" not in text: - marker = "\ndef _write_json(path: Path, payload: Any) -> None:\n" - helper = ''' -def _redact_mapping(value: Any) -> Any: - if isinstance(value, Mapping): - result: dict[str, Any] = {} - for key, item in value.items(): - key_text = str(key) - if any(fragment in key_text.lower() for fragment in SECRET_KEY_FRAGMENTS): - result[key_text] = "[REDACTED]" - else: - result[key_text] = _redact_mapping(item) - return result - if isinstance(value, list): - return [_redact_mapping(item) for item in value] - return value - - -def _validate_command(command: list[str], index: int) -> None: - previous = "" - for argument in command: - lower = argument.lower() - if previous in SECRET_ARGUMENT_FLAGS: - raise ValueError(f"command_contract[{index}] contains a credential value after {previous}") - if any(lower == flag or lower.startswith(flag + "=") for flag in SECRET_ARGUMENT_FLAGS): - if "=" in argument: - raise ValueError(f"command_contract[{index}] contains a credential-bearing argument") - previous = lower - continue - if "://" in argument and any( - token in lower - for token in ("token=", "password=", "secret=", "api_key=", "apikey=", "access_key=") - ): - raise ValueError(f"command_contract[{index}] contains a credential-bearing URL") - previous = lower - -''' - if marker not in text: - raise SystemExit("pipeline compiler helper marker is missing") - text = text.replace(marker, helper + marker, 1) - text = text.replace( - ' "x-source-run-inputs": list(manifest.get("inputs", []))\n', - ' "x-source-run-input-count": len(manifest.get("inputs", [])),\n "x-source-run-input-fields": sorted({str(key) for item in manifest.get("inputs", []) if isinstance(item, Mapping) for key in item.keys()})\n', - ) - text = text.replace( - ' safe_description = description.replace(chr(34), chr(39))\n', - ' safe_description = " ".join(description.replace(chr(34), chr(39)).split())\n', - ) - text = text.replace( - "json.dumps(manifest.get('environment', {}), indent=2, sort_keys=True, ensure_ascii=False)", - "json.dumps(_redact_mapping(manifest.get('environment', {})), indent=2, sort_keys=True, ensure_ascii=False)", - ) - old = ''' for index, command in enumerate(commands): - if not isinstance(command, list) or not command or not all(isinstance(item, str) and item for item in command): - raise ValueError(f"command_contract[{index}] must be a non-empty argv list") -''' - new = ''' for index, command in enumerate(commands): - if not isinstance(command, list) or not command or not all(isinstance(item, str) and item for item in command): - raise ValueError(f"command_contract[{index}] must be a non-empty argv list") - _validate_command(command, index) -''' - if old in text: - text = text.replace(old, new, 1) - elif "_validate_command(command, index)" not in text: - raise SystemExit("pipeline command validation marker is missing") - path.write_text(text, encoding="utf-8") - print("extended runtime hardening: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/harden_review_action_runtime_v4.py b/scripts/harden_review_action_runtime_v4.py deleted file mode 100644 index 627e76d9..00000000 --- a/scripts/harden_review_action_runtime_v4.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -"""Finalize canonical hashes and receipt validation for review/action runtimes.""" - -from __future__ import annotations - -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def patch_reviewer() -> None: - path = ROOT / "src" / "codex_science" / "reviewer_runtime.py" - text = path.read_text(encoding="utf-8") - text = text.replace( - "from codex_science.review_receipts import build_review_receipt, validate_review_receipt\n", - "from codex_science.review_receipts import build_review_receipt, canonical_sha256, validate_review_receipt\n", - ) - text = text.replace( - ' receipt["fingerprint"] = _fingerprint(material)\n validate_review_receipt(receipt)\n', - ' receipt["fingerprint"] = canonical_sha256(material)\n validate_review_receipt(receipt)\n', - ) - path.write_text(text, encoding="utf-8") - - -def patch_action() -> None: - path = ROOT / "src" / "codex_science" / "action_connectors.py" - text = path.read_text(encoding="utf-8") - marker = ''' validate_preview(preview, spec) - existing = ledger.get(spec.idempotency_key) -''' - replacement = ''' validate_preview(preview, spec) - if adapter.connector_name != spec.connector: - raise ValueError("adapter connector does not match action spec") - existing = ledger.get(spec.idempotency_key) -''' - if marker in text: - text = text.replace(marker, replacement, 1) - path.write_text(text, encoding="utf-8") - - -def main() -> int: - patch_reviewer() - patch_action() - print("review/action runtime hardening: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/integrate_extended_runtime_v4.py b/scripts/integrate_extended_runtime_v4.py deleted file mode 100644 index dc486e94..00000000 --- a/scripts/integrate_extended_runtime_v4.py +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/env python3 -"""Integrate pipeline promotion, experiment planning, and FAIR export references.""" - -from __future__ import annotations - -import json -import subprocess -import sys -from pathlib import Path -from typing import Any - -ROOT = Path(__file__).resolve().parents[1] - - -def write_json(path: Path, payload: Any) -> None: - path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") - - -def update_index(path: Path, entry: dict[str, Any]) -> None: - payload = json.loads(path.read_text(encoding="utf-8")) - key = "references" if "references" in payload else "entries" if "entries" in payload else "references" - entries = payload.setdefault(key, []) - if not isinstance(entries, list): - raise SystemExit(f"reference index list is invalid: {path}") - existing = next((item for item in entries if isinstance(item, dict) and item.get("id") == entry["id"]), None) - if existing is None: - entries.append(entry) - else: - existing.clear() - existing.update(entry) - entries.sort(key=lambda item: str(item.get("id", "")) if isinstance(item, dict) else "") - write_json(path, payload) - - -def add_skill_reference(path: Path, marker: str, paragraph: str) -> None: - text = path.read_text(encoding="utf-8") - if marker in text: - return - heading = "## Reference usage" - if heading in text: - start = text.index(heading) + len(heading) - next_heading = text.find("\n## ", start) - if next_heading < 0: - text = text.rstrip() + "\n\n" + paragraph + "\n" - else: - text = text[:next_heading].rstrip() + "\n\n" + paragraph + "\n" + text[next_heading:] - else: - anchor = "## Workflow" - if anchor not in text: - raise SystemExit(f"cannot insert reference route: {path}") - text = text.replace(anchor, f"## Reference usage\n\n{paragraph}\n\n{anchor}", 1) - path.write_text(text, encoding="utf-8") - - -def add_quality_outputs(path: Path, values: list[str]) -> None: - payload = json.loads(path.read_text(encoding="utf-8")) - outputs = payload.setdefault("output_schemas", []) - if not isinstance(outputs, list): - raise SystemExit(f"quality output_schemas is invalid: {path}") - for value in values: - if value not in outputs: - outputs.append(value) - outputs.sort() - write_json(path, payload) - - -def patch_pipeline_compiler() -> None: - path = ROOT / "src" / "codex_science" / "pipeline_compiler.py" - text = path.read_text(encoding="utf-8") - function_start = text.index("def _skill_text(") - step_line = text.index(" step_lines = []", function_start) - if "safe_description = description.replace" not in text[function_start:step_line]: - text = text[:step_line] + ' safe_description = description.replace(chr(34), chr(39))\n' + text[step_line:] - lines = text.splitlines() - for index, line in enumerate(lines): - if line.strip().startswith('description: "{description.replace('): - lines[index] = 'description: "{safe_description}"' - text = "\n".join(lines) + ("\n" if text.endswith("\n") else "") - text = text.replace("manifest.get('review', {{}})", "manifest.get('review', {})") - text = text.replace("manifest.get('environment', {{}})", "manifest.get('environment', {})") - path.write_text(text, encoding="utf-8") - - -def patch_experiment_planner() -> None: - path = ROOT / "src" / "codex_science" / "experiment_planner.py" - text = path.read_text(encoding="utf-8") - old = ' claim_ids = sorted({_text(item, "claim_id") for item in payload.get("claim_ids", [])})\n' - new = ''' claim_ids_raw = payload.get("claim_ids", []) - if not isinstance(claim_ids_raw, list): - raise ValueError("claim_ids must be a list") - claim_ids = sorted({_text(item, "claim_id") for item in claim_ids_raw}) -''' - if old in text: - text = text.replace(old, new, 1) - text = text.replace( - ' scored.append((score, -front_rank[candidate["id"]], candidate["id"], candidate))\n', - ' scored.append((score, front_rank[candidate["id"]], candidate["id"], candidate))\n', - ) - path.write_text(text, encoding="utf-8") - - -def patch_fair_export() -> None: - path = ROOT / "src" / "codex_science" / "fair_export.py" - text = path.read_text(encoding="utf-8") - old_used = ''' for entity_id in entities: - if entity_id.startswith("input:"): - used[f"used:{activity_id}:{entity_id}"] = {"prov:activity": activity_id, "prov:entity": entity_id} -''' - new_used = ''' recorded_inputs = execution.get("inputs", execution.get("input_ids", [])) - if isinstance(recorded_inputs, list): - for value in recorded_inputs: - entity_id = f"input:{value}" - if entity_id in entities: - used[f"used:{activity_id}:{entity_id}"] = { - "prov:activity": activity_id, - "prov:entity": entity_id, - } -''' - if old_used in text: - text = text.replace(old_used, new_used, 1) - marker = ' activities[run_activity] = {"prov:type": "ScientificRun",' - if 'used[f"used:{run_activity}:{entity_id}"]' not in text: - line_end = text.index("\n", text.index(marker)) + 1 - addition = ''' for entity_id in entities: - if entity_id.startswith("input:"): - used[f"used:{run_activity}:{entity_id}"] = { - "prov:activity": run_activity, - "prov:entity": entity_id, - } -''' - text = text[:line_end] + addition + text[line_end:] - old_output = ''' output_dir = output_dir.resolve() - output_dir.mkdir(parents=True, exist_ok=True) -''' - new_output = ''' output_dir = output_dir.resolve() - if output_dir.exists() and any(output_dir.iterdir()): - raise ValueError(f"export output directory is not empty: {output_dir}") - output_dir.mkdir(parents=True, exist_ok=True) -''' - if old_output in text: - text = text.replace(old_output, new_output, 1) - path.write_text(text, encoding="utf-8") - - -def patch_references() -> None: - codex_index = ROOT / "skills" / "codex-science" / "references" / "index.json" - update_index( - codex_index, - { - "id": "pipeline-promotion", - "path": "references/pipeline-promotion.md", - "purpose": "Generate a non-active reusable skill draft from a currently passed run without transferring conclusions or review receipts.", - "read_when": ["before converting a reviewed run into a reusable workflow", "before generating a skill draft from artifacts"], - "required_before": ["calling scripts/promote_run_to_skill.py", "adding a generated draft to authored-skills or a catalog"], - "search_patterns": ["## Preconditions", "## Generated draft", "## Promotion path", "## Failure handling"], - "authority": "Codex Science first-party promotion contract", - "version": "1", - "evidence_boundary": "Promotion preserves workflow shape; it does not activate a skill or prove generalization." - }, - ) - update_index( - codex_index, - { - "id": "next-experiment-planning", - "path": "references/next-experiment-planning.md", - "purpose": "Deterministic Pareto, uncertainty, control, diversity, budget, and batch planning for a non-executing experiment proposal.", - "read_when": ["before ranking candidates for a new experiment", "before using model uncertainty or multi-objective utility for selection"], - "required_before": ["calling scripts/plan_next_experiment.py", "treating a selected batch as an approved experiment"], - "search_patterns": ["## Input contract", "## Selection method", "## Output contract", "## Failure handling"], - "authority": "Codex Science first-party experiment proposal contract", - "version": "1", - "evidence_boundary": "The planner ranks declared properties; it does not calibrate uncertainty, infer similarity, or execute experiments." - }, - ) - add_skill_reference( - ROOT / "skills" / "codex-science" / "SKILL.md", - "references/pipeline-promotion.md", - "Before turning a reviewed run into a reusable workflow, **MUST** read [the pipeline promotion contract](references/pipeline-promotion.md). `scripts/promote_run_to_skill.py` creates a non-active draft only; acceptance fixtures, seeded failures, independent review, maturity declaration, and catalog policy are still required.", - ) - add_skill_reference( - ROOT / "skills" / "codex-science" / "SKILL.md", - "references/next-experiment-planning.md", - "Before ranking compounds, assays, datasets, simulations, or other candidates for a next experiment, **MUST** read [the next-experiment planning contract](references/next-experiment-planning.md). Save objectives, controls, budget, diversity groups, uncertainty boundary, selected and rejected records, and approval state; a proposal is not an executed experiment.", - ) - - provenance_index = ROOT / "skills" / "science-provenance" / "references" / "index.json" - update_index( - provenance_index, - { - "id": "fair-export", - "path": "references/fair-export.md", - "purpose": "Export validated run metadata as RO-Crate-oriented, W3C PROV-oriented, and scientific dependency BOM records.", - "read_when": ["before archival or interoperable export", "before generating a scientific dependency or license handoff"], - "required_before": ["calling scripts/export_scientific_run.py", "describing an export as portable, FAIR, or compliance-supporting"], - "search_patterns": ["## RO-Crate-oriented metadata", "## W3C PROV-oriented lineage", "## Scientific dependency BOM", "## Failure handling"], - "authority": "Codex Science first-party export contract", - "version": "1", - "evidence_boundary": "The exporter packages recorded metadata; it does not certify completeness, licensing, regulatory compliance, or scientific validity." - }, - ) - add_skill_reference( - ROOT / "skills" / "science-provenance" / "SKILL.md", - "references/fair-export.md", - "Before exporting a run for archival, interoperability, dependency review, or compliance-supporting handoff, **MUST** read [the FAIR-oriented export contract](references/fair-export.md). Validate the source bundle first and preserve explicit non-certification, licensing, omitted-byte, and provenance-completeness boundaries.", - ) - - -def patch_quality_and_docs() -> None: - add_quality_outputs(ROOT / "skills" / "codex-science" / "quality.json", ["pipeline-promotion-v1", "experiment-proposal-v1"]) - add_quality_outputs(ROOT / "skills" / "science-provenance" / "quality.json", ["fair-export-receipt-v1", "scientific-bom-v1", "ro-crate-oriented-v1", "prov-oriented-v1"]) - quickstart = ROOT / "docs" / "NATIVE_RUNTIME_QUICKSTART.md" - text = quickstart.read_text(encoding="utf-8") - marker = "## 5. Promote, plan, and export" - if marker not in text: - text = text.rstrip() + ''' - -## 5. Promote, plan, and export - -Read `skills/codex-science/references/pipeline-promotion.md` before generating a skill draft with `scripts/promote_run_to_skill.py`. The generated draft is not active. - -Read `skills/codex-science/references/next-experiment-planning.md` before generating `experiment-proposal` records with `scripts/plan_next_experiment.py`. The proposal is not executed and always requires approval. - -Read `skills/science-provenance/references/fair-export.md` before using `scripts/export_scientific_run.py`. The export is metadata-oriented and explicitly does not certify regulatory, legal, or formal standards compliance. -''' - quickstart.write_text(text, encoding="utf-8") - platform = ROOT / "docs" / "PLATFORM_CONTRACTS.md" - text = platform.read_text(encoding="utf-8") - marker = "## Pipeline promotion, next experiments, and FAIR export" - if marker not in text: - platform.write_text(text.rstrip() + ''' - -## Pipeline promotion, next experiments, and FAIR export - -A passed run may be converted into a non-active skill draft with `scripts/promote_run_to_skill.py`. The draft preserves source manifest identity, argv contracts, explicit schemas, limitations, and a promotion checklist; it does not enter the catalog automatically. - -`scripts/plan_next_experiment.py` creates deterministic, non-executing proposals from declared objectives, uncertainty, controls, diversity groups, budget, and batch constraints. Selected and rejected records remain inspectable and approval is still required. - -`scripts/export_scientific_run.py` emits RO-Crate-oriented metadata, W3C PROV-oriented lineage, a scientific dependency BOM, and a hash-covered receipt from a validated bundle. The export does not certify formal conformance, licensing, regulatory compliance, or provenance completeness. -''', encoding="utf-8") - - -def main() -> int: - patch_pipeline_compiler() - patch_experiment_planner() - patch_fair_export() - patch_references() - patch_quality_and_docs() - subprocess.run([sys.executable, "-m", "compileall", "-q", "src", "scripts"], cwd=ROOT, check=True) - print("extended native runtime v4 integration: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/integrate_native_runtime_v4.py b/scripts/integrate_native_runtime_v4.py deleted file mode 100644 index dbdac93d..00000000 --- a/scripts/integrate_native_runtime_v4.py +++ /dev/null @@ -1,430 +0,0 @@ -#!/usr/bin/env python3 -"""One-time idempotent integration for native runtime v4 source additions.""" - -from __future__ import annotations - -import json -from pathlib import Path -from typing import Any - -ROOT = Path(__file__).resolve().parents[1] - - -def write_json(path: Path, payload: dict[str, Any]) -> None: - path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") - - -def update_reference_index(path: Path, entry: dict[str, Any]) -> None: - payload = json.loads(path.read_text(encoding="utf-8")) - if not isinstance(payload, dict): - raise SystemExit(f"reference index must be an object: {path}") - key = "references" if "references" in payload else "entries" if "entries" in payload else "references" - entries = payload.setdefault(key, []) - if not isinstance(entries, list): - raise SystemExit(f"reference index {key} must be a list: {path}") - existing = next((item for item in entries if isinstance(item, dict) and item.get("id") == entry["id"]), None) - if existing is None: - entries.append(entry) - elif existing != entry: - existing.clear() - existing.update(entry) - entries.sort(key=lambda item: str(item.get("id", "")) if isinstance(item, dict) else "") - write_json(path, payload) - - -def add_reference_paragraph(path: Path, marker: str, paragraph: str) -> None: - text = path.read_text(encoding="utf-8") - if marker in text: - return - heading = "## Reference usage" - if heading in text: - start = text.index(heading) + len(heading) - next_heading = text.find("\n## ", start) - if next_heading < 0: - text = text.rstrip() + "\n\n" + paragraph + "\n" - else: - text = text[:next_heading].rstrip() + "\n\n" + paragraph + "\n" + text[next_heading:] - else: - anchor = "## Workflow" - if anchor not in text: - raise SystemExit(f"cannot find reference insertion point: {path}") - text = text.replace(anchor, f"## Reference usage\n\n{paragraph}\n\n{anchor}", 1) - path.write_text(text, encoding="utf-8") - - -def append_once(path: Path, marker: str, content: str) -> None: - text = path.read_text(encoding="utf-8") - if marker in text: - return - path.write_text(text.rstrip() + "\n\n" + content.strip() + "\n", encoding="utf-8") - - -def replace_once(path: Path, old: str, new: str, label: str) -> None: - text = path.read_text(encoding="utf-8") - if new in text: - return - if old not in text: - raise SystemExit(f"missing {label} marker in {path}") - path.write_text(text.replace(old, new, 1), encoding="utf-8") - - -def patch_reference_indexes_and_skills() -> None: - update_reference_index( - ROOT / "skills" / "science-provenance" / "references" / "index.json", - { - "id": "artifact-runtime", - "path": "references/artifact-runtime.md", - "purpose": "Bounded native views, hash-bound selections, and non-executing transform proposals for scientific artifacts.", - "read_when": [ - "before rendering a scientific artifact", - "before creating an atom, residue, locus, cell, table, graph, figure, or trajectory selection", - "before converting an annotation into a proposed edit or rerun" - ], - "required_before": [ - "calling scripts/render_artifact_runtime.py", - "recording artifact-runtime-descriptor, artifact-selection, or transform-proposal sidecars" - ], - "search_patterns": [ - "### Describe an artifact", - "### Create a typed selection", - "### Propose a transform", - "## Failure handling", - "## Common mistakes" - ], - "authority": "Codex Science first-party runtime contract", - "version": "1", - "evidence_boundary": "A bounded runtime view improves inspection but does not validate a scientific interpretation or execute a transform." - }, - ) - add_reference_paragraph( - ROOT / "skills" / "science-provenance" / "SKILL.md", - "references/artifact-runtime.md", - "Before rendering, selecting, or proposing a change to a structure, molecule, genome track, single-cell object, table, figure, evidence graph, trajectory, or large dataset, **MUST** read [the native artifact runtime contract](references/artifact-runtime.md). Generate a hash-bound descriptor and typed selection before any transform proposal; never mutate artifact bytes from a visual annotation alone.", - ) - - update_reference_index( - ROOT / "skills" / "codex-science" / "references" / "index.json", - { - "id": "project-evidence-store", - "path": "references/project-evidence-store.md", - "purpose": "Project-level immutable run imports, evidence assertions, hypothesis forks, comparisons, and non-executing merge plans.", - "read_when": [ - "before importing more than one run into a project", - "before creating a hypothesis or experiment branch", - "before recording a quantitative evidence assertion", - "before comparing or merging scientific branches" - ], - "required_before": [ - "calling scripts/science_project.py", - "treating project-level state as scientific lineage" - ], - "search_patterns": [ - "## Import a reviewed run", - "## Fork a hypothesis branch", - "## Add a quantitative evidence assertion", - "## Prepare a merge plan", - "## Failure handling" - ], - "authority": "Codex Science first-party project lineage contract", - "version": "1", - "evidence_boundary": "The project store indexes immutable runs and decisions; it does not replace manifests, evidence, or independent review." - }, - ) - add_reference_paragraph( - ROOT / "skills" / "codex-science" / "SKILL.md", - "references/project-evidence-store.md", - "For a multi-run research program, hypothesis fork, quantitative evidence assertion, or branch comparison, **MUST** read [the project evidence store contract](references/project-evidence-store.md) before calling `scripts/science_project.py`. Imported manifests and artifact hashes remain authoritative; merge plans are non-executing and always require a new scientific synthesis and review.", - ) - - update_reference_index( - ROOT / "authored-skills" / "remote-scientific-compute" / "references" / "index.json", - { - "id": "job-runtime", - "path": "references/job-runtime.md", - "purpose": "Durable local and Slurm job specs, approvals, submission, status, cancellation, checkpoint, and output collection receipts.", - "read_when": [ - "before constructing a durable job spec", - "before submitting, polling, cancelling, or collecting a local or Slurm job", - "before approving a non-local resource allocation" - ], - "required_before": [ - "calling scripts/science_job.py", - "submitting work through LocalBackend or SlurmBackend" - ], - "search_patterns": [ - "## Job spec", - "## Local execution", - "## Approval receipt", - "## Slurm execution", - "## Checkpoint and resume", - "## Failure handling" - ], - "authority": "Codex Science first-party compute runtime contract", - "version": "1", - "evidence_boundary": "A job receipt proves execution state and collected hashes, not scientific success." - }, - ) - add_reference_paragraph( - ROOT / "authored-skills" / "remote-scientific-compute" / "SKILL.md", - "references/job-runtime.md", - "Before constructing, approving, submitting, polling, cancelling, or collecting a durable local or Slurm job, **MUST** read [the job runtime contract](references/job-runtime.md). Use argv lists, exact resource and timeout caps, hash-bound approvals for Slurm, and terminal output receipts; a zero exit code is not scientific acceptance.", - ) - - -def patch_source_bugs() -> None: - replace_once( - ROOT / "src" / "codex_science" / "artifact_runtime.py", - " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n", - " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n", - "artifact runtime warning tuple", - ) - project_cli = ROOT / "scripts" / "science_project.py" - text = project_cli.read_text(encoding="utf-8") - if "import sqlite3\n" not in text: - text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) - text = text.replace( - " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", - " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", - ) - project_cli.write_text(text, encoding="utf-8") - maturity = ROOT / "src" / "codex_science" / "skill_maturity.py" - text = maturity.read_text(encoding="utf-8") - text = text.replace( - ' entries = payload.get("references")\n', - ' entries = payload.get("references", payload.get("entries"))\n', - ) - maturity.write_text(text, encoding="utf-8") - - -def patch_advanced_sidecars() -> None: - path = ROOT / "src" / "codex_science" / "advanced_sidecars.py" - append_once( - path, - "# Native artifact runtime sidecar extension v1", - r''' -# Native artifact runtime sidecar extension v1 -from codex_science.artifact_runtime import ( - stale_selection as _runtime_stale_selection, - validate_runtime_descriptor as _validate_runtime_descriptor, - validate_selection as _validate_runtime_selection, - validate_transform_proposal as _validate_transform_proposal, -) - -_base_validate_advanced_sidecars = validate_advanced_sidecars -_base_review_advanced_sidecars = review_advanced_sidecars - - -def validate_advanced_sidecars( - manifest: dict[str, Any], - run_dir: Path, - verified: dict[str, Path], - *, - base_sidecars: dict[str, Any] | None = None, -) -> dict[str, Any]: - result = _base_validate_advanced_sidecars( - manifest, run_dir, verified, base_sidecars=base_sidecars - ) - result.setdefault("runtime_descriptors", []) - result.setdefault("artifact_selections", []) - result.setdefault("transform_proposals", []) - result.setdefault("runtime_descriptor_stale", []) - result.setdefault("runtime_paths", {}) - artifact_hashes = result.get("artifact_hashes", {}) - selections_by_id: dict[str, dict[str, Any]] = {} - pending_proposals: list[dict[str, Any]] = [] - for record in manifest.get("artifacts", []): - kind = str(record.get("kind", "")) - relative = str(record.get("path", "")) - if kind not in { - "artifact-runtime-descriptor", - "artifact-selection", - "transform-proposal", - }: - continue - payload = _load(verified[relative], kind) - result["runtime_paths"].setdefault(kind, []).append(relative) - if kind == "artifact-runtime-descriptor": - _validate_runtime_descriptor(payload) - if artifact_hashes.get(str(payload["artifact_path"])) != str( - payload["artifact_sha256"] - ).lower(): - result["runtime_descriptor_stale"].append(payload) - result["runtime_descriptors"].append(payload) - elif kind == "artifact-selection": - _validate_runtime_selection(payload) - selection = _runtime_stale_selection(payload, artifact_hashes) - result["artifact_selections"].append(selection) - selections_by_id[str(selection["selection_id"])] = selection - else: - pending_proposals.append(payload) - for proposal in pending_proposals: - selection = selections_by_id.get(str(proposal.get("selection_id", ""))) - _validate_transform_proposal(proposal, selection) - result["transform_proposals"].append(proposal) - return result - - -def review_advanced_sidecars( - sidecars: dict[str, Any], *, registry_path: Path | None = None -) -> list[dict[str, str]]: - findings = list( - _base_review_advanced_sidecars(sidecars, registry_path=registry_path) - ) - for descriptor in sidecars.get("runtime_descriptor_stale", []): - findings.append( - { - "code": "stale-runtime-descriptor", - "severity": "minor", - "message": ( - "Artifact runtime descriptor covers changed or missing bytes: " - f"{descriptor.get('artifact_path', '')}" - ), - } - ) - for selection in sidecars.get("artifact_selections", []): - if selection.get("status") == "stale-anchor": - findings.append( - { - "code": "stale-artifact-selection", - "severity": "minor", - "message": ( - f"Artifact selection {selection.get('selection_id')} points to " - "changed or missing bytes." - ), - } - ) - selection_ids = { - str(item.get("selection_id")) - for item in sidecars.get("artifact_selections", []) - } - for proposal in sidecars.get("transform_proposals", []): - if str(proposal.get("selection_id")) not in selection_ids: - findings.append( - { - "code": "missing-transform-selection", - "severity": "major", - "message": ( - f"Transform proposal {proposal.get('proposal_id')} references " - "a selection not present in the bundle." - ), - } - ) - unique = { - (item["code"], item.get("claim_id", ""), item["message"]): item - for item in findings - } - return sorted( - unique.values(), - key=lambda item: ( - item.get("severity", ""), - item["code"], - item.get("claim_id", ""), - item["message"], - ), - ) -''', - ) - - -def patch_validation_gates() -> None: - check = ROOT / "scripts" / "check.sh" - text = check.read_text(encoding="utf-8") - command = " uv run python scripts/audit_native_skill_maturity.py --require-clean\n" - if command not in text: - marker = " uv run python scripts/audit_skill_references.py --require-clean\n" - if marker in text: - text = text.replace(marker, marker + command, 1) - else: - echo = ' echo "scientific contracts: ok"\n' - if echo not in text: - raise SystemExit("cannot locate scientific contract gate in check.sh") - text = text.replace(echo, command + echo, 1) - check.write_text(text, encoding="utf-8") - - candidate = ROOT / "scripts" / "candidate_contract_check.py" - text = candidate.read_text(encoding="utf-8") - needle = 'run([python, "scripts/audit_native_skill_maturity.py", "--require-clean"], cwd=root)' - if needle not in text: - marker = 'run([python, "scripts/audit_skill_references.py", "--require-clean"], cwd=root)' - if marker in text: - text = text.replace(marker, marker + "\n " + needle, 1) - else: - marker = 'run([python, "scripts/generate_wrappers.py", "--check"], cwd=root)' - if marker not in text: - raise SystemExit("cannot locate candidate reference/wrapper gate") - text = text.replace(marker, marker + "\n " + needle, 1) - candidate.write_text(text, encoding="utf-8") - - -def patch_docs() -> None: - append_once( - ROOT / "docs" / "PLATFORM_CONTRACTS.md", - "## Native artifact runtime and project evidence memory", - ''' -## Native artifact runtime and project evidence memory - -Codex Science separates bounded artifact views, typed selections, and non-executing transform proposals. Read `skills/science-provenance/references/artifact-runtime.md` before using `scripts/render_artifact_runtime.py`. Runtime descriptors and selections are bound to the manifest artifact SHA-256; changed bytes make them stale. - -Multi-run programs use `scripts/science_project.py` and the contract in `skills/codex-science/references/project-evidence-store.md`. The SQLite database indexes immutable run manifests, branch lineage, quantitative evidence assertions, comparisons, and merge plans. Run bundles remain authoritative and merge plans never combine bytes automatically. - -## Durable compute runtime - -`scripts/science_job.py` implements a durable local backend and an approval-gated Slurm backend. Read `authored-skills/remote-scientific-compute/references/job-runtime.md` before constructing or submitting a job. Job state, logs, cancellation, checkpoints, and output hashes are operational provenance; process success is not scientific acceptance. - -## Native skill maturity - -`scripts/audit_native_skill_maturity.py --require-clean` audits first-party skills against `catalog/native-skill-policy.json`. L3 requires the full decision contract, progressive reference index, and machine-readable output declarations. L4 additionally requires checked-in acceptance fixtures, seeded failure classes, and test files. A declared level above the computed level is a blocking policy finding. -''', - ) - append_once( - ROOT / "docs" / "NATIVE_SKILL_STANDARD.md", - "## Machine-readable maturity declarations", - ''' -## Machine-readable maturity declarations - -A decision-bearing first-party skill may include `quality.json` beside `SKILL.md`: - -```json -{ - "schema_version": 1, - "skill": "example-skill", - "roles": ["executor", "reviewer"], - "declared_maturity": "L4", - "output_schemas": ["example-result-v1"], - "acceptance_fixtures": ["examples/example/input.json"], - "seeded_failures": ["wrong-unit"], - "test_files": ["tests/test_example.py"], - "limitations": ["Fixture-only validation."] -} -``` - -The repository policy is `catalog/native-skill-policy.json`; the deterministic auditor is `scripts/audit_native_skill_maturity.py`. Declarations do not grant maturity. The auditor computes maturity from the actual instruction contract, progressive references, declared outputs, existing fixtures, seeded failures, and tests. -''', - ) - append_once( - ROOT / "skills" / "science-provenance" / "references" / "artifact-contract.md", - "### Runtime collaboration sidecars", - ''' -### Runtime collaboration sidecars - -- `artifact-runtime-descriptor`: bounded native view bound to source artifact path and SHA-256; -- `artifact-selection`: typed atom, residue, locus, cell, table, figure, graph, frame, text, or byte selection bound to artifact SHA-256; -- `transform-proposal`: non-executing operation, parameters, affected steps, expected outputs, and approval boundary bound to a selection. - -Changed source bytes make descriptors and selections stale. A proposal never authorizes or executes a mutation and cannot be used as evidence that a rerun occurred. -''', - ) - - -def main() -> int: - patch_reference_indexes_and_skills() - patch_source_bugs() - patch_advanced_sidecars() - patch_validation_gates() - patch_docs() - print("native runtime v4 integration: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/integrate_review_action_runtime_v4.py b/scripts/integrate_review_action_runtime_v4.py deleted file mode 100644 index b148d874..00000000 --- a/scripts/integrate_review_action_runtime_v4.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env python3 -"""Integrate reviewer and authenticated connector action runtimes.""" - -from __future__ import annotations - -import json -from pathlib import Path -from typing import Any - -ROOT = Path(__file__).resolve().parents[1] - - -def write_json(path: Path, payload: Any) -> None: - path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") - - -def update_index(path: Path, entry: dict[str, Any]) -> None: - payload = json.loads(path.read_text(encoding="utf-8")) - key = "references" if "references" in payload else "entries" if "entries" in payload else "references" - entries = payload.setdefault(key, []) - if not isinstance(entries, list): - raise SystemExit(f"reference index is invalid: {path}") - existing = next((item for item in entries if isinstance(item, dict) and item.get("id") == entry["id"]), None) - if existing is None: - entries.append(entry) - else: - existing.clear() - existing.update(entry) - entries.sort(key=lambda item: str(item.get("id", "")) if isinstance(item, dict) else "") - write_json(path, payload) - - -def add_route(path: Path, marker: str, paragraph: str, *, anchor: str) -> None: - text = path.read_text(encoding="utf-8") - if marker in text: - return - heading = "## Reference usage" - if heading in text: - start = text.index(heading) + len(heading) - next_heading = text.find("\n## ", start) - if next_heading < 0: - text = text.rstrip() + "\n\n" + paragraph + "\n" - else: - text = text[:next_heading].rstrip() + "\n\n" + paragraph + "\n" + text[next_heading:] - else: - if anchor not in text: - raise SystemExit(f"cannot insert reference route: {path}") - text = text.replace(anchor, f"## Reference usage\n\n{paragraph}\n\n{anchor}", 1) - path.write_text(text, encoding="utf-8") - - -def add_quality(path: Path, values: tuple[str, ...]) -> None: - payload = json.loads(path.read_text(encoding="utf-8")) - outputs = payload.setdefault("output_schemas", []) - if not isinstance(outputs, list): - raise SystemExit(f"quality output_schemas is invalid: {path}") - for value in values: - if value not in outputs: - outputs.append(value) - outputs.sort() - write_json(path, payload) - - -def patch_reviewer() -> None: - path = ROOT / "src" / "codex_science" / "reviewer_runtime.py" - text = path.read_text(encoding="utf-8") - if "import urllib.parse\n" not in text: - text = text.replace("import json\n", "import json\nimport urllib.parse\n", 1) - old = ''' if isinstance(value, list): - return [_redact(item) for item in value] - return value -''' - new = ''' if isinstance(value, list): - return [_redact(item) for item in value] - if isinstance(value, str) and "://" in value: - try: - parsed = urllib.parse.urlsplit(value) - query = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True) - if query: - query = [ - ( - key, - "[REDACTED]" - if any(fragment in key.lower() for fragment in SENSITIVE_FRAGMENTS) - else item, - ) - for key, item in query - ] - return urllib.parse.urlunsplit( - ( - parsed.scheme, - parsed.netloc, - parsed.path, - urllib.parse.urlencode(query), - parsed.fragment, - ) - ) - except ValueError: - return "[REDACTED-INVALID-URL]" - return value -''' - if old in text: - text = text.replace(old, new, 1) - old_modes = ' response_modes = sorted({_text(item, "response review mode") for item in response.get("review_modes", [])})\n' - new_modes = ''' response_modes_raw = response.get("review_modes", []) - if not isinstance(response_modes_raw, list): - raise ValueError("response review_modes must be a list") - response_modes = sorted({_text(item, "response review mode") for item in response_modes_raw}) -''' - if old_modes in text: - text = text.replace(old_modes, new_modes, 1) - old_claims = ' reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in response.get("reviewed_claim_ids", [])})\n' - new_claims = ''' reviewed_claims_raw = response.get("reviewed_claim_ids", []) - if not isinstance(reviewed_claims_raw, list): - raise ValueError("reviewed_claim_ids must be a list") - reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in reviewed_claims_raw}) -''' - if old_claims in text: - text = text.replace(old_claims, new_claims, 1) - path.write_text(text, encoding="utf-8") - - -def patch_action_framework() -> None: - path = ROOT / "src" / "codex_science" / "action_connectors.py" - text = path.read_text(encoding="utf-8") - text = text.replace( - 'SECRET_FRAGMENTS = {"token", "password", "secret", "credential", "private_key", "api_key", "apikey", "access_key", "client_secret"}\n', - 'SECRET_FRAGMENTS = {"token", "password", "secret", "credential", "private_key", "api_key", "apikey", "access_key", "client_secret", "authorization", "bearer", "session_cookie"}\n', - ) - execute_marker = ''' validate_preview(preview, spec) - existing = ledger.get(spec.idempotency_key) -''' - execute_replacement = ''' validate_preview(preview, spec) - if adapter.connector_name != spec.connector: - raise ValueError("adapter connector does not match action spec") - existing = ledger.get(spec.idempotency_key) -''' - if execute_marker in text: - text = text.replace(execute_marker, execute_replacement, 1) - if "def validate_action_receipt" not in text: - text += ''' - -def validate_action_receipt(payload: Mapping[str, Any], spec: ActionSpec | None = None) -> None: - if payload.get("schema_version") != 1 or payload.get("status") != "executed": - raise ValueError("invalid action receipt state") - for field in ( - "action_id", "connector", "operation", "mode", "target", - "action_spec_sha256", "preview_id", "preview_fingerprint", - "idempotency_key", "before_state_sha256", "after_state_sha256", - "executed_at", "evidence_boundary", - ): - _text(payload.get(field), field) - for field in ("action_spec_sha256", "preview_fingerprint", "before_state_sha256", "after_state_sha256"): - _sha(payload.get(field), field) - material = dict(payload) - fingerprint = str(material.pop("fingerprint", "")).lower() - if _fingerprint(material) != fingerprint: - raise ValueError("action receipt fingerprint mismatch") - if spec is not None and payload.get("action_spec_sha256") != spec.fingerprint: - raise ValueError("action receipt covers a different spec") -''' - path.write_text(text, encoding="utf-8") - - -def patch_references() -> None: - update_index( - ROOT / "skills" / "science-review" / "references" / "index.json", - { - "id": "independent-review-runtime", - "path": "references/independent-review-runtime.md", - "purpose": "Prepare blinded, hash-bound review packets and validate separate reviewer responses without producer-only rationale.", - "read_when": ["before handing a run to a separate reviewer", "before accepting a reviewer response", "before calling a review independent or reproduced"], - "required_before": ["calling scripts/science_reviewer.py", "attaching a packet-derived review receipt"], - "search_patterns": ["## Independence boundary", "## Prepare a review packet", "## Reviewer response contract", "## Reproduction mode", "## Failure handling"], - "authority": "Codex Science first-party review packet contract", - "version": "1", - "evidence_boundary": "The packet standardizes coverage but does not authenticate identity or guarantee independence." - }, - ) - add_route( - ROOT / "skills" / "science-review" / "SKILL.md", - "references/independent-review-runtime.md", - "Before preparing a separate human or agent review, accepting a response, or claiming independence or reproduction, **MUST** read [the independent reviewer packet contract](references/independent-review-runtime.md). Use `scripts/science_reviewer.py` to bind the packet to source manifest and artifact hashes; do not expose producer-only rationale or transfer a response to changed bytes.", - anchor="## Review workflow", - ) - add_quality(ROOT / "skills" / "science-review" / "quality.json", ("review-task-v1", "review-response-v1", "review-receipt-v2")) - - update_index( - ROOT / "skills" / "codex-science" / "references" / "index.json", - { - "id": "authenticated-connector-packs", - "path": "references/authenticated-connector-packs.md", - "purpose": "Approval-gated optional connector packs with out-of-band credentials, preview, scopes, idempotency, concurrency, and before/after receipts.", - "read_when": ["before installing or designing an authenticated connector pack", "before previewing or executing a provider write", "before transferring sensitive data to an external service"], - "required_before": ["issuing a connector action approval", "executing through ConnectorActionAdapter", "claiming a provider write integration is safe"], - "search_patterns": ["## Action spec", "## Provider preview", "## Approval", "## Execute through an optional pack", "## Failure handling"], - "authority": "Codex Science first-party optional connector contract", - "version": "1", - "evidence_boundary": "The framework standardizes action safety; it does not supply credentials, provider implementations, or compliance certification." - }, - ) - add_route( - ROOT / "skills" / "codex-science" / "SKILL.md", - "references/authenticated-connector-packs.md", - "Before designing, installing, approving, or using an authenticated or write-capable provider connector, **MUST** read [the optional connector pack contract](references/authenticated-connector-packs.md). Credentials stay out of band; every write requires a hash-bound preview, exact scopes, idempotency key, fresh before-state check, explicit approval, and before/after receipt.", - anchor="## Workflow", - ) - add_quality(ROOT / "skills" / "codex-science" / "quality.json", ("connector-action-spec-v1", "connector-action-preview-v1", "connector-action-approval-v1", "connector-action-receipt-v1")) - - -def patch_docs() -> None: - quickstart = ROOT / "docs" / "NATIVE_RUNTIME_QUICKSTART.md" - text = quickstart.read_text(encoding="utf-8") - if "## 6. Prepare an independent review packet" not in text: - text = text.rstrip() + ''' - -## 6. Prepare an independent review packet - -Read `skills/science-review/references/independent-review-runtime.md`, then use `scripts/science_reviewer.py prepare` and `finalize`. A producer second pass is not independent, and reproduction requires a separate execution. -''' - if "## 7. Validate an authenticated connector action" not in text: - text = text.rstrip() + ''' - -## 7. Validate an authenticated connector action - -Read `skills/codex-science/references/authenticated-connector-packs.md`. Use `scripts/connector_action_contract.py` for spec, preview, and approval validation. Provider execution belongs to an optional adapter pack; credentials never enter the action JSON. -''' - quickstart.write_text(text, encoding="utf-8") - - -def main() -> int: - patch_reviewer() - patch_action_framework() - patch_references() - patch_docs() - print("review and action runtime integration: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/integrate_reviewer_runtime_v4.py b/scripts/integrate_reviewer_runtime_v4.py deleted file mode 100644 index 5528d345..00000000 --- a/scripts/integrate_reviewer_runtime_v4.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python3 -"""Integrate independent reviewer packet runtime into progressive skill references.""" - -from __future__ import annotations - -import json -import subprocess -import sys -from pathlib import Path -from typing import Any - -ROOT = Path(__file__).resolve().parents[1] - - -def write_json(path: Path, payload: Any) -> None: - path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") - - -def update_index(path: Path, entry: dict[str, Any]) -> None: - payload = json.loads(path.read_text(encoding="utf-8")) - key = "references" if "references" in payload else "entries" if "entries" in payload else "references" - entries = payload.setdefault(key, []) - if not isinstance(entries, list): - raise SystemExit(f"reference index is invalid: {path}") - existing = next((item for item in entries if isinstance(item, dict) and item.get("id") == entry["id"]), None) - if existing is None: - entries.append(entry) - else: - existing.clear() - existing.update(entry) - entries.sort(key=lambda item: str(item.get("id", "")) if isinstance(item, dict) else "") - write_json(path, payload) - - -def add_skill_route(path: Path, marker: str, paragraph: str) -> None: - text = path.read_text(encoding="utf-8") - if marker in text: - return - heading = "## Reference usage" - if heading in text: - start = text.index(heading) + len(heading) - next_heading = text.find("\n## ", start) - if next_heading < 0: - text = text.rstrip() + "\n\n" + paragraph + "\n" - else: - text = text[:next_heading].rstrip() + "\n\n" + paragraph + "\n" + text[next_heading:] - else: - anchor = "## Review workflow" - if anchor not in text: - raise SystemExit(f"cannot insert reviewer reference route: {path}") - text = text.replace(anchor, f"## Reference usage\n\n{paragraph}\n\n{anchor}", 1) - path.write_text(text, encoding="utf-8") - - -def patch_redaction() -> None: - path = ROOT / "src" / "codex_science" / "reviewer_runtime.py" - text = path.read_text(encoding="utf-8") - if "import urllib.parse\n" not in text: - text = text.replace("import json\n", "import json\nimport urllib.parse\n", 1) - old = ''' if isinstance(value, list): - return [_redact(item) for item in value] - return value -''' - new = ''' if isinstance(value, list): - return [_redact(item) for item in value] - if isinstance(value, str) and "://" in value: - try: - parsed = urllib.parse.urlsplit(value) - query = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True) - if query: - query = [ - (key, "[REDACTED]" if any(fragment in key.lower() for fragment in SENSITIVE_FRAGMENTS) else item) - for key, item in query - ] - return urllib.parse.urlunsplit( - (parsed.scheme, parsed.netloc, parsed.path, urllib.parse.urlencode(query), parsed.fragment) - ) - except ValueError: - return "[REDACTED-INVALID-URL]" - return value -''' - if old in text: - text = text.replace(old, new, 1) - elif "REDACTED-INVALID-URL" not in text: - raise SystemExit("reviewer redaction marker is missing") - old_modes = ''' response_modes = sorted({_text(item, "response review mode") for item in response.get("review_modes", [])}) -''' - new_modes = ''' response_modes_raw = response.get("review_modes", []) - if not isinstance(response_modes_raw, list): - raise ValueError("response review_modes must be a list") - response_modes = sorted({_text(item, "response review mode") for item in response_modes_raw}) -''' - if old_modes in text: - text = text.replace(old_modes, new_modes, 1) - old_claims = ''' reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in response.get("reviewed_claim_ids", [])}) -''' - new_claims = ''' reviewed_claims_raw = response.get("reviewed_claim_ids", []) - if not isinstance(reviewed_claims_raw, list): - raise ValueError("reviewed_claim_ids must be a list") - reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in reviewed_claims_raw}) -''' - if old_claims in text: - text = text.replace(old_claims, new_claims, 1) - path.write_text(text, encoding="utf-8") - - -def patch_reference_and_quality() -> None: - update_index( - ROOT / "skills" / "science-review" / "references" / "index.json", - { - "id": "independent-review-runtime", - "path": "references/independent-review-runtime.md", - "purpose": "Prepare blinded, hash-bound review packets and validate separate reviewer responses without exposing producer-only rationale.", - "read_when": ["before handing a run to a separate reviewer", "before accepting a reviewer response", "before calling a review independent or reproduced"], - "required_before": ["calling scripts/science_reviewer.py", "attaching a packet-derived review receipt to a run"], - "search_patterns": ["## Independence boundary", "## Prepare a review packet", "## Reviewer response contract", "## Reproduction mode", "## Failure handling"], - "authority": "Codex Science first-party review packet contract", - "version": "1", - "evidence_boundary": "The runtime standardizes packet and receipt coverage but does not authenticate reviewer identity or guarantee independence." - }, - ) - add_skill_route( - ROOT / "skills" / "science-review" / "SKILL.md", - "references/independent-review-runtime.md", - "Before preparing a separate human or agent review, accepting a response, or claiming independence or reproduction, **MUST** read [the independent reviewer packet contract](references/independent-review-runtime.md). Use `scripts/science_reviewer.py` to bind the packet to source manifest and artifact hashes; do not expose producer-only rationale or transfer a response to changed bytes.", - ) - quality = ROOT / "skills" / "science-review" / "quality.json" - payload = json.loads(quality.read_text(encoding="utf-8")) - outputs = payload.setdefault("output_schemas", []) - for value in ("review-task-v1", "review-response-v1", "review-receipt-v2"): - if value not in outputs: - outputs.append(value) - outputs.sort() - write_json(quality, payload) - quickstart = ROOT / "docs" / "NATIVE_RUNTIME_QUICKSTART.md" - text = quickstart.read_text(encoding="utf-8") - marker = "## 6. Prepare an independent review packet" - if marker not in text: - quickstart.write_text(text.rstrip() + ''' - -## 6. Prepare an independent review packet - -Read `skills/science-review/references/independent-review-runtime.md`, then use `scripts/science_reviewer.py prepare` to create a hash-bound packet and `finalize` to validate a separate response. A second pass by the producer must use `independent: false`; reproduction requires a separate execution. -''', encoding="utf-8") - - -def main() -> int: - patch_redaction() - patch_reference_and_quality() - subprocess.run([sys.executable, "-m", "compileall", "-q", "src", "scripts"], cwd=ROOT, check=True) - print("reviewer runtime integration: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/patch_permanent_runtime_v4.py b/scripts/patch_permanent_runtime_v4.py deleted file mode 100644 index fc4ae7ac..00000000 --- a/scripts/patch_permanent_runtime_v4.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/env python3 -"""Apply native runtime integrations without compiling one-time helper scripts.""" - -from __future__ import annotations - -import importlib.util -import sys -from pathlib import Path -from types import ModuleType - -ROOT = Path(__file__).resolve().parents[1] - - -def load_optional(path: Path, name: str) -> ModuleType | None: - if not path.is_file(): - return None - spec = importlib.util.spec_from_file_location(name, path) - if spec is None or spec.loader is None: - raise SystemExit(f"could not load integration module: {path}") - module = importlib.util.module_from_spec(spec) - sys.modules[name] = module - spec.loader.exec_module(module) - return module - - -def apply_optional(module: ModuleType | None, functions: tuple[str, ...]) -> None: - if module is None: - return - for name in functions: - function = getattr(module, name, None) - if function is None: - raise SystemExit(f"integration function is missing: {module.__name__}.{name}") - function() - - -def replace_region(path: Path, start_marker: str, end_marker: str, replacement: str) -> None: - text = path.read_text(encoding="utf-8") - start = text.index(start_marker) - end = text.index(end_marker, start) - path.write_text(text[:start] + replacement + text[end:], encoding="utf-8") - - -def patch_artifact_runtime() -> None: - path = ROOT / "src" / "codex_science" / "artifact_runtime.py" - text = path.read_text(encoding="utf-8") - old = " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n" - new = " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n" - if old in text: - path.write_text(text.replace(old, new, 1), encoding="utf-8") - elif new not in text: - raise SystemExit("artifact runtime descriptor marker is missing") - - -def patch_project_cli() -> None: - path = ROOT / "scripts" / "science_project.py" - text = path.read_text(encoding="utf-8") - if "import sqlite3\n" not in text: - text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) - text = text.replace( - " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", - " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", - ) - path.write_text(text, encoding="utf-8") - - -def patch_skill_maturity() -> None: - path = ROOT / "src" / "codex_science" / "skill_maturity.py" - frontmatter = '''def _frontmatter(text: str) -> dict[str, str]: - match = FRONTMATTER_RE.match(text) - if match is None: - return {} - result: dict[str, str] = {} - for item in FIELD_RE.finditer(match.group("body")): - value = item.group("value").strip() - if len(value) >= 2 and value[0] == value[-1] and value[0] in {chr(34), chr(39)}: - value = value[1:-1] - result[item.group("key")] = value - return result -''' - replace_region(path, "def _frontmatter", "\n\ndef _load_json", frontmatter) - text = path.read_text(encoding="utf-8") - text = text.replace(' entries = payload.get("references")\n', ' entries = payload.get("references", payload.get("entries"))\n') - old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" - has_review = "$science-review" in text or skill_dir.name == "science-review" -''' - new = ''' if skill_dir.name in CORE_SECTION_ALIASES: - # Core coordinator/provenance/reviewer skills collectively implement - # the handoff and are not required to invoke themselves by token. - has_provenance = True - has_review = True - else: - has_provenance = "$science-provenance" in text - has_review = "$science-review" in text -''' - if old in text: - text = text.replace(old, new, 1) - elif new not in text: - raise SystemExit("skill maturity handoff marker is missing") - path.write_text(text, encoding="utf-8") - - -def patch_remote_sections() -> None: - path = ROOT / "authored-skills" / "remote-scientific-compute" / "SKILL.md" - text = path.read_text(encoding="utf-8") - sections: list[str] = [] - if "## Decision contract" not in text: - sections.append("## Decision contract\n\nBefore execution, record the target, transfer boundary, argv or workflow, input hashes, runtime or container, resource and wall-time caps, cost cap, checkpoints, cancellation, outputs, and scientific acceptance criteria. Remote writes or allocation require explicit approval covering the exact job spec.") - if "## Outputs" not in text: - sections.append("## Outputs\n\nSave the job spec, preflight and approval receipts, job ID and states, stdout and stderr hashes, failure class, checkpoint references, collected output hashes, and `$science-provenance` and `$science-review` handoffs.") - if "## Boundaries" not in text: - sections.append("## Boundaries\n\nDo not probe credentials, submit to unapproved accounts or partitions, stage sensitive data without approval, busy-poll, hide failed attempts, or treat process exit zero as scientific success.") - if sections: - path.write_text(text.rstrip() + "\n\n" + "\n\n".join(sections) + "\n", encoding="utf-8") - - -def patch_local_submit() -> None: - path = ROOT / "src" / "codex_science" / "compute_backends.py" - replacement = ''' def submit(self, spec: JobSpec, *, approval: Mapping[str, Any] | None = None) -> dict[str, Any]: - if spec.backend != "local": - raise ValueError("LocalBackend requires backend=local") - preflight = self.preflight(spec) - if not preflight["ready"]: - raise ValueError("local preflight failed") - if spec.approval_required: - if approval is None: - raise ValueError("job spec requires explicit approval") - validate_approval_receipt(approval, spec) - job_id = "local-" + uuid.uuid4().hex[:20] - job_dir = self._job_dir(job_id) - job_dir.mkdir(parents=True) - _atomic_json(job_dir / "spec.json", spec.to_dict()) - submitted_at = _now() - initial = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at) - _atomic_json(job_dir / "state.json", initial) - worker_environment = os.environ.copy() - source_root = str(Path(__file__).resolve().parents[1]) - existing_pythonpath = worker_environment.get("PYTHONPATH", "") - worker_environment["PYTHONPATH"] = source_root + (os.pathsep + existing_pythonpath if existing_pythonpath else "") - process = subprocess.Popen( - [sys.executable, "-m", "codex_science.compute_backends", "_worker", str(job_dir)], - env=worker_environment, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - start_new_session=True, - close_fds=True, - ) - current = _read_json(job_dir / "state.json") - submitted_receipt = _state_material( - spec, - job_id=job_id, - state="submitted", - submitted_at=submitted_at, - worker_pid=process.pid, - message="local worker started", - ) - if current.get("state") == "submitted": - _atomic_json(job_dir / "state.json", submitted_receipt) - return submitted_receipt -''' - text = path.read_text(encoding="utf-8") - local_start = text.index("class LocalBackend:") - submit_start = text.index(" def submit(", local_start) - status_start = text.index("\n def status(", submit_start) - path.write_text(text[:submit_start] + replacement + text[status_start:], encoding="utf-8") - - -def patch_advanced_sidecars() -> None: - path = ROOT / "src" / "codex_science" / "advanced_sidecars.py" - if not path.is_file(): - return - text = path.read_text(encoding="utf-8") - text = text.replace(' selections_by_id[str(selection["selection_id"])] = selection\n', ' selections_by_id[str(selection["selection_id"])] = payload\n') - path.write_text(text, encoding="utf-8") - - -def patch_project_branch() -> None: - path = ROOT / "src" / "codex_science" / "project_store.py" - text = path.read_text(encoding="utf-8") - old = ''' if branch is None: - connection.execute( - "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", - (project_id, branch_name, run_id, run_id, "active", imported_at, imported_at), - ) -''' - new = ''' if branch is None: - branch_base = parent_run_id or run_id - connection.execute( - "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", - (project_id, branch_name, branch_base, run_id, "active", imported_at, imported_at), - ) -''' - if old in text: - text = text.replace(old, new, 1) - path.write_text(text, encoding="utf-8") - - -def main() -> int: - native = load_optional(ROOT / "scripts" / "integrate_native_runtime_v4.py", "integrate_native_runtime_v4") - apply_optional(native, ("patch_reference_indexes_and_skills", "patch_source_bugs", "patch_advanced_sidecars", "patch_validation_gates", "patch_docs")) - extended = load_optional(ROOT / "scripts" / "integrate_extended_runtime_v4.py", "integrate_extended_runtime_v4") - apply_optional(extended, ("patch_pipeline_compiler", "patch_experiment_planner", "patch_fair_export", "patch_references", "patch_quality_and_docs")) - patch_artifact_runtime() - patch_project_cli() - patch_skill_maturity() - patch_remote_sections() - patch_local_submit() - patch_advanced_sidecars() - patch_project_branch() - print("permanent native runtime v4 patch: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/repair_native_runtime_v4.py b/scripts/repair_native_runtime_v4.py deleted file mode 100644 index 83623323..00000000 --- a/scripts/repair_native_runtime_v4.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env python3 -"""Final idempotent repairs for native runtime v4 before repository validation.""" - -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def run_optional(name: str) -> None: - path = ROOT / "scripts" / name - if path.is_file() and path.name != Path(__file__).name: - subprocess.run([sys.executable, str(path)], cwd=ROOT, check=True) - - -def replace(path: Path, old: str, new: str, label: str) -> None: - text = path.read_text(encoding="utf-8") - if new in text: - return - if old not in text: - raise SystemExit(f"missing {label} marker: {path}") - path.write_text(text.replace(old, new, 1), encoding="utf-8") - - -def patch_skill_maturity() -> None: - path = ROOT / "src" / "codex_science" / "skill_maturity.py" - text = path.read_text(encoding="utf-8") - # Use an unambiguous string literal containing both quote characters. - text = text.replace(".strip('\\\"\\\\\'')", '.strip("\\\"\'")') - text = text.replace(".strip('\\\"\\\'')", '.strip("\\\"\'")') - text = text.replace(".strip('\\\"\'')", '.strip("\\\"\'")') - if 'strip("\\\"\'")' not in text: - old_line_start = ' return {item.group("key"):' - for line in text.splitlines(): - if line.startswith(old_line_start): - text = text.replace(line, ' return {item.group("key"): item.group("value").strip().strip("\\\"\'") for item in FIELD_RE.finditer(match.group("body"))}', 1) - break - text = text.replace( - ' entries = payload.get("references")\n', - ' entries = payload.get("references", payload.get("entries"))\n', - ) - old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" - has_review = "$science-review" in text or skill_dir.name == "science-review" -''' - new = ''' if skill_dir.name in CORE_SECTION_ALIASES: - # Core coordinator/provenance/reviewer skills collectively implement - # the handoff and are not required to invoke themselves by token. - has_provenance = True - has_review = True - else: - has_provenance = "$science-provenance" in text - has_review = "$science-review" in text -''' - if old in text: - text = text.replace(old, new, 1) - path.write_text(text, encoding="utf-8") - - -def patch_remote_sections() -> None: - path = ROOT / "authored-skills" / "remote-scientific-compute" / "SKILL.md" - text = path.read_text(encoding="utf-8") - additions: list[str] = [] - if "## Decision contract" not in text: - additions.append( - "## Decision contract\n\n" - "Before execution, record the target, data-transfer boundary, command or workflow, input hashes, runtime or container, requested resources, wall-time and cost caps, cancellation route, checkpoint policy, expected outputs, and scientific acceptance criteria. Remote writes or allocation require one explicit approval receipt covering the exact job spec." - ) - if "## Outputs" not in text: - additions.append( - "## Outputs\n\n" - "Save the job spec, preflight and approval receipts, scheduler or local job ID, state transitions, stdout and stderr hashes, resource use when reported, checkpoint references, cancellation or failure class, collected output hashes, and the downstream claim/review handoff with `$science-provenance`." - ) - if "## Boundaries" not in text: - additions.append( - "## Boundaries\n\n" - "Do not discover credentials by probing, submit to unapproved accounts or partitions, stage sensitive data without an approved route, busy-poll a scheduler, hide failed attempts, or treat a zero exit code as scientific success. Run `$science-review` after material outputs are collected." - ) - if additions: - path.write_text(text.rstrip() + "\n\n" + "\n\n".join(additions) + "\n", encoding="utf-8") - - -def patch_compute_worker() -> None: - path = ROOT / "src" / "codex_science" / "compute_backends.py" - text = path.read_text(encoding="utf-8") - old = ''' process = subprocess.Popen( - [sys.executable, str(Path(__file__).resolve()), "_worker", str(job_dir)], - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - start_new_session=True, - close_fds=True, - ) -''' - new = ''' worker_environment = os.environ.copy() - source_root = str(Path(__file__).resolve().parents[1]) - existing_pythonpath = worker_environment.get("PYTHONPATH", "") - worker_environment["PYTHONPATH"] = source_root + ( - os.pathsep + existing_pythonpath if existing_pythonpath else "" - ) - process = subprocess.Popen( - [sys.executable, "-m", "codex_science.compute_backends", "_worker", str(job_dir)], - env=worker_environment, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - start_new_session=True, - close_fds=True, - ) -''' - if old in text: - text = text.replace(old, new, 1) - # Replace any earlier race-safe tail with a deterministic submitted receipt. - class_start = text.index("class LocalBackend:") - submit_start = text.index(" def submit(", class_start) - process_start = text.index(" process = subprocess.Popen(", submit_start) - status_start = text.index("\n def status(", process_start) - section = text[process_start:status_start] - if "return submitted_receipt" not in section: - candidates = [index for token in ("\n current = _read_json", "\n state = _state_material") if (index := section.find(token)) >= 0] - if not candidates: - raise SystemExit("cannot locate LocalBackend submission receipt tail") - tail_start = min(candidates) - replacement = ''' - current = _read_json(job_dir / "state.json") - submitted_receipt = _state_material( - spec, - job_id=job_id, - state="submitted", - submitted_at=submitted_at, - worker_pid=process.pid, - message="local worker started", - ) - if current.get("state") == "submitted": - _atomic_json(job_dir / "state.json", submitted_receipt) - return submitted_receipt -''' - section = section[:tail_start] + replacement - text = text[:process_start] + section + text[status_start:] - path.write_text(text, encoding="utf-8") - - -def patch_advanced_sidecars() -> None: - path = ROOT / "src" / "codex_science" / "advanced_sidecars.py" - if not path.is_file(): - return - text = path.read_text(encoding="utf-8") - text = text.replace( - ' selections_by_id[str(selection["selection_id"])] = selection\n', - ' selections_by_id[str(selection["selection_id"])] = payload\n', - ) - path.write_text(text, encoding="utf-8") - - -def patch_project_branch_base() -> None: - path = ROOT / "src" / "codex_science" / "project_store.py" - text = path.read_text(encoding="utf-8") - old = ''' if branch is None: - connection.execute( - "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", - (project_id, branch_name, run_id, run_id, "active", imported_at, imported_at), - ) -''' - new = ''' if branch is None: - branch_base = parent_run_id or run_id - connection.execute( - "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", - (project_id, branch_name, branch_base, run_id, "active", imported_at, imported_at), - ) -''' - if old in text: - text = text.replace(old, new, 1) - path.write_text(text, encoding="utf-8") - - -def main() -> int: - for name in ( - "integrate_native_runtime_v4.py", - "finalize_native_runtime_v4.py", - "apply_complete_native_runtime_v4.py", - ): - run_optional(name) - patch_skill_maturity() - patch_remote_sections() - patch_compute_worker() - patch_advanced_sidecars() - patch_project_branch_base() - subprocess.run([sys.executable, "-m", "compileall", "-q", "src", "scripts"], cwd=ROOT, check=True) - print("native runtime v4 repairs: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/seal_native_runtime_v4.py b/scripts/seal_native_runtime_v4.py deleted file mode 100644 index 6ae42534..00000000 --- a/scripts/seal_native_runtime_v4.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env python3 -"""Seal native runtime v4 using function-boundary rewrites, then compile it.""" - -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] - - -def run_optional(name: str) -> None: - path = ROOT / "scripts" / name - if path.is_file() and path.name != Path(__file__).name: - subprocess.run([sys.executable, str(path)], cwd=ROOT, check=True) - - -def replace_region(path: Path, start_marker: str, end_marker: str, replacement: str) -> None: - text = path.read_text(encoding="utf-8") - start = text.index(start_marker) - end = text.index(end_marker, start) - current = text[start:end] - if current == replacement: - return - path.write_text(text[:start] + replacement + text[end:], encoding="utf-8") - - -def patch_artifact_runtime() -> None: - path = ROOT / "src" / "codex_science" / "artifact_runtime.py" - text = path.read_text(encoding="utf-8") - old = " return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings))\n" - new = " material[\"warnings\"] = tuple(warnings)\n return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material))\n" - if old in text: - path.write_text(text.replace(old, new, 1), encoding="utf-8") - elif new not in text: - raise SystemExit("artifact descriptor construction marker is missing") - - -def patch_project_cli() -> None: - path = ROOT / "scripts" / "science_project.py" - text = path.read_text(encoding="utf-8") - if "import sqlite3\n" not in text: - text = text.replace("import json\n", "import json\nimport sqlite3\n", 1) - text = text.replace( - " except (OSError, ValueError, sqlite3.Error if False else ValueError) as error: # type: ignore[misc]\n", - " except (OSError, ValueError, sqlite3.Error, json.JSONDecodeError) as error:\n", - ) - path.write_text(text, encoding="utf-8") - - -def patch_skill_maturity() -> None: - path = ROOT / "src" / "codex_science" / "skill_maturity.py" - frontmatter = '''def _frontmatter(text: str) -> dict[str, str]: - match = FRONTMATTER_RE.match(text) - if match is None: - return {} - result: dict[str, str] = {} - for item in FIELD_RE.finditer(match.group("body")): - value = item.group("value").strip() - if len(value) >= 2 and value[0] == value[-1] and value[0] in {chr(34), chr(39)}: - value = value[1:-1] - result[item.group("key")] = value - return result -''' - replace_region(path, "def _frontmatter", "\n\ndef _load_json", frontmatter) - text = path.read_text(encoding="utf-8") - text = text.replace( - ' entries = payload.get("references")\n', - ' entries = payload.get("references", payload.get("entries"))\n', - ) - old = ''' has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" - has_review = "$science-review" in text or skill_dir.name == "science-review" -''' - new = ''' if skill_dir.name in CORE_SECTION_ALIASES: - # Core coordinator/provenance/reviewer skills collectively implement - # the handoff and are not required to invoke themselves by token. - has_provenance = True - has_review = True - else: - has_provenance = "$science-provenance" in text - has_review = "$science-review" in text -''' - if old in text: - text = text.replace(old, new, 1) - elif new not in text: - raise SystemExit("skill maturity handoff marker is missing") - path.write_text(text, encoding="utf-8") - - -def patch_remote_skill() -> None: - path = ROOT / "authored-skills" / "remote-scientific-compute" / "SKILL.md" - text = path.read_text(encoding="utf-8") - sections: list[str] = [] - if "## Decision contract" not in text: - sections.append( - "## Decision contract\n\n" - "Before execution, record the target, transfer boundary, argv or workflow, input hashes, runtime or container, resource and wall-time caps, cost cap, checkpoint and cancellation routes, expected outputs, and scientific acceptance criteria. Remote writes or allocation require one explicit approval receipt covering the exact job spec." - ) - if "## Outputs" not in text: - sections.append( - "## Outputs\n\n" - "Save the job spec, preflight and approval receipts, job ID and state transitions, stdout and stderr hashes, failure class, resource use when reported, checkpoint references, collected output hashes, and the `$science-provenance` and `$science-review` handoff." - ) - if "## Boundaries" not in text: - sections.append( - "## Boundaries\n\n" - "Do not probe for credentials, submit to unapproved accounts or partitions, stage sensitive data without an approved route, busy-poll, hide failed attempts, or treat process exit zero as scientific success." - ) - if sections: - path.write_text(text.rstrip() + "\n\n" + "\n\n".join(sections) + "\n", encoding="utf-8") - - -def patch_local_submit() -> None: - path = ROOT / "src" / "codex_science" / "compute_backends.py" - replacement = ''' def submit(self, spec: JobSpec, *, approval: Mapping[str, Any] | None = None) -> dict[str, Any]: - if spec.backend != "local": - raise ValueError("LocalBackend requires backend=local") - preflight = self.preflight(spec) - if not preflight["ready"]: - raise ValueError("local preflight failed") - if spec.approval_required: - if approval is None: - raise ValueError("job spec requires explicit approval") - validate_approval_receipt(approval, spec) - job_id = "local-" + uuid.uuid4().hex[:20] - job_dir = self._job_dir(job_id) - job_dir.mkdir(parents=True) - _atomic_json(job_dir / "spec.json", spec.to_dict()) - submitted_at = _now() - initial = _state_material( - spec, - job_id=job_id, - state="submitted", - submitted_at=submitted_at, - ) - _atomic_json(job_dir / "state.json", initial) - worker_environment = os.environ.copy() - source_root = str(Path(__file__).resolve().parents[1]) - existing_pythonpath = worker_environment.get("PYTHONPATH", "") - worker_environment["PYTHONPATH"] = source_root + ( - os.pathsep + existing_pythonpath if existing_pythonpath else "" - ) - process = subprocess.Popen( - [ - sys.executable, - "-m", - "codex_science.compute_backends", - "_worker", - str(job_dir), - ], - env=worker_environment, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - start_new_session=True, - close_fds=True, - ) - current = _read_json(job_dir / "state.json") - submitted_receipt = _state_material( - spec, - job_id=job_id, - state="submitted", - submitted_at=submitted_at, - worker_pid=process.pid, - message="local worker started", - ) - if current.get("state") == "submitted": - _atomic_json(job_dir / "state.json", submitted_receipt) - return submitted_receipt -''' - text = path.read_text(encoding="utf-8") - local_start = text.index("class LocalBackend:") - submit_start = text.index(" def submit(", local_start) - status_start = text.index("\n def status(", submit_start) - path.write_text(text[:submit_start] + replacement + text[status_start:], encoding="utf-8") - - -def patch_advanced_sidecars() -> None: - path = ROOT / "src" / "codex_science" / "advanced_sidecars.py" - if not path.is_file(): - return - text = path.read_text(encoding="utf-8") - text = text.replace( - ' selections_by_id[str(selection["selection_id"])] = selection\n', - ' selections_by_id[str(selection["selection_id"])] = payload\n', - ) - path.write_text(text, encoding="utf-8") - - -def patch_project_branch_base() -> None: - path = ROOT / "src" / "codex_science" / "project_store.py" - text = path.read_text(encoding="utf-8") - old = ''' if branch is None: - connection.execute( - "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", - (project_id, branch_name, run_id, run_id, "active", imported_at, imported_at), - ) -''' - new = ''' if branch is None: - branch_base = parent_run_id or run_id - connection.execute( - "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", - (project_id, branch_name, branch_base, run_id, "active", imported_at, imported_at), - ) -''' - if old in text: - text = text.replace(old, new, 1) - path.write_text(text, encoding="utf-8") - - -def main() -> int: - run_optional("integrate_native_runtime_v4.py") - patch_artifact_runtime() - patch_project_cli() - patch_skill_maturity() - patch_remote_skill() - patch_local_submit() - patch_advanced_sidecars() - patch_project_branch_base() - subprocess.run([sys.executable, "-m", "compileall", "-q", "src", "scripts"], cwd=ROOT, check=True) - print("native runtime v4 sealed: ok") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/skills/codex-science/SKILL.md b/skills/codex-science/SKILL.md index fee375cd..8db41013 100644 --- a/skills/codex-science/SKILL.md +++ b/skills/codex-science/SKILL.md @@ -40,6 +40,8 @@ Fall back to general-purpose tools or skills only when no suitable Science capab After selecting a skill, inspect its `references/index.json` when present and load only the entries required by the active route. Read [the reference-loading protocol](references/reference-loading.md) before any reference-controlled operation; do not infer arguments, output schemas, source semantics, thresholds, or licenses from names alone. Read [the catalog policy](references/catalog-policy.md) before selecting or inspecting an inactive catalog skill. Read [the Goal-loop contract](references/goal-loop-contract.md) before starting, resuming, or completing a native Goal run. +Read [the project evidence store contract](references/project-evidence-store.md) before managing multi-run lineage, hypotheses, comparisons, or merge plans. Read [the pipeline promotion contract](references/pipeline-promotion.md) before generating a reusable draft from a reviewed run, and [the next-experiment planning contract](references/next-experiment-planning.md) before ranking a proposed batch. Read [the authenticated connector pack contract](references/authenticated-connector-packs.md) before designing, approving, or using a credentialed or write-capable provider action. + When a reference materially controls a query, command, transformation, threshold, or claim, record its path, SHA-256, read reason, and affected claim or artifact in a `reference-use-ledger`. A reference explains procedure; the actual source record or computation remains the scientific evidence. ## Research contract and evidence graph diff --git a/skills/codex-science/quality.json b/skills/codex-science/quality.json index 1ae679d5..5f81d86b 100644 --- a/skills/codex-science/quality.json +++ b/skills/codex-science/quality.json @@ -8,7 +8,13 @@ "checkpoint-v4", "artifact-manifest-v1", "lane-receipt-v1", - "reference-use-ledger-v1" + "reference-use-ledger-v1", + "pipeline-promotion-v1", + "experiment-proposal-v1", + "connector-action-spec-v1", + "connector-action-preview-v1", + "connector-action-approval-v1", + "connector-action-receipt-v1" ], "acceptance_fixtures": [], "seeded_failures": [], diff --git a/skills/codex-science/references/index.json b/skills/codex-science/references/index.json index 734a5d25..93bb4ff2 100644 --- a/skills/codex-science/references/index.json +++ b/skills/codex-science/references/index.json @@ -65,6 +65,50 @@ "Explicit acknowledgement" ], "version": "1" + }, + { + "id": "authenticated-connector-packs", + "path": "references/authenticated-connector-packs.md", + "purpose": "Approval-gated optional connector packs with out-of-band credentials, preview, scopes, idempotency, concurrency, and before/after receipts.", + "read_when": ["before installing or designing an authenticated connector pack", "before previewing or executing a provider write", "before transferring sensitive data to an external service"], + "required_before": ["issuing a connector action approval", "executing through ConnectorActionAdapter", "claiming a provider write integration is safe"], + "search_patterns": ["## Action spec", "## Provider preview", "## Approval", "## Execute through an optional pack", "## Failure handling"], + "authority": "Codex Science first-party optional connector contract", + "version": "1", + "evidence_boundary": "The framework standardizes action safety; it does not supply credentials, provider implementations, or compliance certification." + }, + { + "id": "next-experiment-planning", + "path": "references/next-experiment-planning.md", + "purpose": "Deterministic Pareto, uncertainty, control, diversity, budget, and batch planning for a non-executing experiment proposal.", + "read_when": ["before ranking candidates for a new experiment", "before using model uncertainty or multi-objective utility for selection"], + "required_before": ["calling scripts/plan_next_experiment.py", "treating a selected batch as an approved experiment"], + "search_patterns": ["## Input contract", "## Selection method", "## Output contract", "## Failure handling"], + "authority": "Codex Science first-party experiment proposal contract", + "version": "1", + "evidence_boundary": "The planner ranks declared properties; it does not calibrate uncertainty, infer similarity, or execute experiments." + }, + { + "id": "pipeline-promotion", + "path": "references/pipeline-promotion.md", + "purpose": "Generate a non-active reusable skill draft from a currently passed run without transferring conclusions or review receipts.", + "read_when": ["before converting a reviewed run into a reusable workflow", "before generating a skill draft from artifacts"], + "required_before": ["calling scripts/promote_run_to_skill.py", "adding a generated draft to authored-skills or a catalog"], + "search_patterns": ["## Preconditions", "## Generated draft", "## Promotion path", "## Failure handling"], + "authority": "Codex Science first-party promotion contract", + "version": "1", + "evidence_boundary": "Promotion preserves workflow shape; it does not activate a skill or prove generalization." + }, + { + "id": "project-evidence-store", + "path": "references/project-evidence-store.md", + "purpose": "Project-level immutable run imports, evidence assertions, hypothesis forks, comparisons, and non-executing merge plans.", + "read_when": ["before importing more than one run into a project", "before creating a hypothesis or experiment branch", "before recording a quantitative evidence assertion", "before comparing or merging scientific branches"], + "required_before": ["calling scripts/science_project.py", "treating project-level state as scientific lineage"], + "search_patterns": ["## Import a reviewed run", "## Fork a hypothesis branch", "## Add a quantitative evidence assertion", "## Prepare a merge plan", "## Failure handling"], + "authority": "Codex Science first-party project lineage contract", + "version": "1", + "evidence_boundary": "The project store indexes immutable runs and decisions; it does not replace manifests, evidence, or independent review." } ], "schema_version": 1, diff --git a/skills/science-provenance/SKILL.md b/skills/science-provenance/SKILL.md index 13385b56..9e46735e 100644 --- a/skills/science-provenance/SKILL.md +++ b/skills/science-provenance/SKILL.md @@ -13,6 +13,8 @@ For a non-trivial active run, keep `checkpoint.json` beside the manifest as muta Read [the artifact contract](references/artifact-contract.md) before authoring a manifest, sidecar, or bundle-version change. Read [the large-artifact protocol](references/large-artifacts.md) before registering a directory, a file too large for whole-memory loading, a chunked dataset, model weights, or an immutable external object. Do not guess sidecar fields, Merkle rules, or invalidation semantics. +Read [the native artifact runtime contract](references/artifact-runtime.md) before rendering an artifact, recording a typed selection, or proposing a transform. Read [the FAIR-oriented export contract](references/fair-export.md) before archival, interoperable export, or scientific dependency and license handoff. + When a reference controls artifact registration or review, save a `reference-use-ledger` with the reference hash. A changed reference hash invalidates a receipt that explicitly covered its prior procedural assumptions. ## Run contract diff --git a/skills/science-provenance/quality.json b/skills/science-provenance/quality.json index cba6f8c8..2662fbb8 100644 --- a/skills/science-provenance/quality.json +++ b/skills/science-provenance/quality.json @@ -10,7 +10,11 @@ "reference-use-ledger-v1", "artifact-descriptor-v1", "artifact-selection-v1", - "transform-proposal-v1" + "transform-proposal-v1", + "fair-export-receipt-v1", + "scientific-bom-v1", + "ro-crate-oriented-v1", + "prov-oriented-v1" ], "acceptance_fixtures": [], "seeded_failures": [], diff --git a/skills/science-provenance/references/index.json b/skills/science-provenance/references/index.json index c1cbbba0..f2c21b4c 100644 --- a/skills/science-provenance/references/index.json +++ b/skills/science-provenance/references/index.json @@ -48,6 +48,28 @@ "authority": "repository-contract", "version": "1", "evidence_boundary": "Validates artifact identity and transfer semantics, not the scientific meaning of the artifact." + }, + { + "id": "artifact-runtime", + "path": "references/artifact-runtime.md", + "purpose": "Bounded native views, hash-bound selections, and non-executing transform proposals for scientific artifacts.", + "read_when": ["before rendering a scientific artifact", "before creating an atom, residue, locus, cell, table, graph, figure, or trajectory selection", "before converting an annotation into a proposed edit or rerun"], + "required_before": ["calling scripts/render_artifact_runtime.py", "recording artifact-runtime-descriptor, artifact-selection, or transform-proposal sidecars"], + "search_patterns": ["### Describe an artifact", "### Create a typed selection", "### Propose a transform", "## Failure handling", "## Common mistakes"], + "authority": "Codex Science first-party runtime contract", + "version": "1", + "evidence_boundary": "A bounded runtime view improves inspection but does not validate a scientific interpretation or execute a transform." + }, + { + "id": "fair-export", + "path": "references/fair-export.md", + "purpose": "Export validated run metadata as RO-Crate-oriented, W3C PROV-oriented, and scientific dependency BOM records.", + "read_when": ["before archival or interoperable export", "before generating a scientific dependency or license handoff"], + "required_before": ["calling scripts/export_scientific_run.py", "describing an export as portable, FAIR, or compliance-supporting"], + "search_patterns": ["## RO-Crate-oriented metadata", "## W3C PROV-oriented lineage", "## Scientific dependency BOM", "## Failure handling"], + "authority": "Codex Science first-party export contract", + "version": "1", + "evidence_boundary": "The exporter packages recorded metadata; it does not certify completeness, licensing, regulatory compliance, or scientific validity." } ] } diff --git a/skills/science-review/SKILL.md b/skills/science-review/SKILL.md index f2f3c5ec..77733221 100644 --- a/skills/science-review/SKILL.md +++ b/skills/science-review/SKILL.md @@ -11,6 +11,8 @@ Review the record adversarially; do not assume the producing agent's conclusion, Read [the review checklist](references/review-checklist.md) before assigning severity, deciding whether a review passes, or closing a material finding. Use the indexed headings rather than loading unrelated reference material. Do not infer a pass rule or downgrade a finding from prose convenience. +Read [the independent reviewer packet contract](references/independent-review-runtime.md) before preparing a separate review packet, accepting a reviewer response, or claiming independence or reproduction. + If the checklist materially controls a review receipt, include its SHA-256 in the run's `reference-use-ledger`. The checklist defines review procedure; it does not prove the reviewed conclusion true. ## Inputs and review mode diff --git a/skills/science-review/quality.json b/skills/science-review/quality.json index 85dc4531..359bfea2 100644 --- a/skills/science-review/quality.json +++ b/skills/science-review/quality.json @@ -6,7 +6,9 @@ "output_schemas": [ "review-receipt-v2", "scientific-finding-v1", - "review-resolution-v1" + "review-resolution-v1", + "review-task-v1", + "review-response-v1" ], "acceptance_fixtures": [], "seeded_failures": [], diff --git a/skills/science-review/references/index.json b/skills/science-review/references/index.json index 5c0efe76..cdd49618 100644 --- a/skills/science-review/references/index.json +++ b/skills/science-review/references/index.json @@ -24,6 +24,17 @@ "authority": "repository-contract", "version": "1", "evidence_boundary": "Defines review procedure; reviewer success does not establish scientific truth." + }, + { + "id": "independent-review-runtime", + "path": "references/independent-review-runtime.md", + "purpose": "Prepare blinded, hash-bound review packets and validate separate reviewer responses without exposing producer-only rationale.", + "read_when": ["before handing a run to a separate reviewer", "before accepting a reviewer response", "before calling a review independent or reproduced"], + "required_before": ["calling scripts/science_reviewer.py", "attaching a packet-derived review receipt to a run"], + "search_patterns": ["## Independence boundary", "## Prepare a review packet", "## Reviewer response contract", "## Reproduction mode", "## Failure handling"], + "authority": "Codex Science first-party review packet contract", + "version": "1", + "evidence_boundary": "The runtime standardizes packet and receipt coverage but does not authenticate reviewer identity or guarantee independence." } ] } diff --git a/src/codex_science/action_connectors.py b/src/codex_science/action_connectors.py index a9a086b6..b2d8c821 100644 --- a/src/codex_science/action_connectors.py +++ b/src/codex_science/action_connectors.py @@ -19,7 +19,20 @@ MODES = {"read", "write"} -SECRET_FRAGMENTS = {"token", "password", "secret", "credential", "private_key", "api_key", "apikey", "access_key", "client_secret"} +SECRET_FRAGMENTS = { + "token", + "password", + "secret", + "credential", + "private_key", + "api_key", + "apikey", + "access_key", + "client_secret", + "authorization", + "bearer", + "session_cookie", +} def _now() -> str: @@ -146,10 +159,12 @@ def build_preview(adapter: ConnectorActionAdapter, spec: ActionSpec, *, created_ if adapter.connector_name != spec.connector: raise ValueError("adapter connector does not match action spec") before = dict(adapter.snapshot(spec)) + _json_value(before, "provider before state") before_sha = _fingerprint(before) if spec.expected_before_sha256 is not None and before_sha != spec.expected_before_sha256: raise ValueError("current remote state does not match expected_before_sha256") changes = dict(adapter.preview(spec, before)) + _json_value(changes, "provider proposed changes") material = { "schema_version": 1, "connector": spec.connector, @@ -268,6 +283,8 @@ def execute_action( executed_at: str | None = None, ) -> dict[str, Any]: validate_preview(preview, spec) + if adapter.connector_name != spec.connector: + raise ValueError("adapter connector does not match action spec") existing = ledger.get(spec.idempotency_key) if existing is not None: if existing.get("action_spec_sha256") != spec.fingerprint: @@ -278,10 +295,12 @@ def execute_action( raise ValueError("write action requires explicit approval") validate_action_approval(approval, preview) current = dict(adapter.snapshot(spec)) + _json_value(current, "provider current state") current_sha = _fingerprint(current) if current_sha != preview["before_state_sha256"]: raise ValueError("remote state changed after preview; create a new preview") result = dict(adapter.execute(spec, preview)) + _json_value(result, "provider execution result") after = dict(result.get("after_state", result)) after_sha = _fingerprint(after) material = { @@ -309,3 +328,37 @@ def execute_action( receipt = {**material, "fingerprint": _fingerprint(material)} ledger.record(key=spec.idempotency_key, spec_sha256=spec.fingerprint, receipt=receipt) return receipt + + +def validate_action_receipt(payload: Mapping[str, Any], spec: ActionSpec | None = None) -> None: + if payload.get("schema_version") != 1 or payload.get("status") != "executed": + raise ValueError("invalid action receipt state") + for field in ( + "action_id", + "connector", + "operation", + "mode", + "target", + "action_spec_sha256", + "preview_id", + "preview_fingerprint", + "idempotency_key", + "before_state_sha256", + "after_state_sha256", + "executed_at", + "evidence_boundary", + ): + _text(payload.get(field), field) + for field in ( + "action_spec_sha256", + "preview_fingerprint", + "before_state_sha256", + "after_state_sha256", + ): + _sha(payload.get(field), field) + material = dict(payload) + fingerprint = str(material.pop("fingerprint", "")).lower() + if _fingerprint(material) != fingerprint: + raise ValueError("action receipt fingerprint mismatch") + if spec is not None and payload.get("action_spec_sha256") != spec.fingerprint: + raise ValueError("action receipt covers a different spec") diff --git a/src/codex_science/advanced_sidecars.py b/src/codex_science/advanced_sidecars.py index 2d23cf4e..3ab1503a 100644 --- a/src/codex_science/advanced_sidecars.py +++ b/src/codex_science/advanced_sidecars.py @@ -6,6 +6,12 @@ from typing import Any from codex_science.artifact_store import validate_descriptor +from codex_science.artifact_runtime import ( + stale_selection, + validate_runtime_descriptor, + validate_selection, + validate_transform_proposal, +) from codex_science.collaboration import stale_annotations, validate_annotation from codex_science.evidence_graph_v2 import independent_support_groups, validate_graph_payload from codex_science.literature_v2 import validate_risk_of_bias @@ -16,6 +22,7 @@ ADVANCED_KINDS = { "evidence-graph-v2", "review-receipt", "review-receipt-v2", "annotation", "model-receipt-v2", "risk-of-bias", "reference-use-ledger", "artifact-descriptor", + "artifact-runtime-descriptor", "artifact-selection", "transform-proposal", } @@ -43,6 +50,10 @@ def validate_advanced_sidecars( "risk_of_bias": [], "reference_use_ledgers": [], "artifact_descriptors": [], + "runtime_descriptors": [], + "artifact_selections": [], + "transform_proposals": [], + "runtime_descriptor_stale": [], "artifact_hashes": { str(item["path"]): str(item["sha256"]).lower() for item in manifest.get("artifacts", []) @@ -50,6 +61,8 @@ def validate_advanced_sidecars( "advanced_paths": {}, } seen_graph = False + selections_by_id: dict[str, dict[str, Any]] = {} + pending_proposals: list[dict[str, Any]] = [] for record in manifest.get("artifacts", []): kind, relative = str(record.get("kind", "")), str(record.get("path", "")) if kind not in ADVANCED_KINDS: @@ -88,6 +101,24 @@ def validate_advanced_sidecars( result["reference_use_ledgers"].append(payload) elif kind == "artifact-descriptor": result["artifact_descriptors"].append(validate_descriptor(payload).to_dict()) + elif kind == "artifact-runtime-descriptor": + validate_runtime_descriptor(payload) + if result["artifact_hashes"].get(str(payload["artifact_path"])) != str( + payload["artifact_sha256"] + ).lower(): + result["runtime_descriptor_stale"].append(payload) + result["runtime_descriptors"].append(payload) + elif kind == "artifact-selection": + validate_selection(payload) + selection = stale_selection(payload, result["artifact_hashes"]) + result["artifact_selections"].append(selection) + selections_by_id[str(selection["selection_id"])] = payload + elif kind == "transform-proposal": + pending_proposals.append(payload) + for proposal in pending_proposals: + selection = selections_by_id.get(str(proposal.get("selection_id", ""))) + validate_transform_proposal(proposal, selection) + result["transform_proposals"].append(proposal) result["annotations"] = stale_annotations(result["annotations"], result["artifact_hashes"]) if base_sidecars: result["base_sidecars"] = base_sidecars @@ -129,6 +160,39 @@ def review_advanced_sidecars(sidecars: dict[str, Any], *, registry_path: Path | "severity": "minor", "message": f"Annotation {annotation['annotation_id']} points to changed artifact bytes.", }) + for descriptor in sidecars.get("runtime_descriptor_stale", []): + findings.append({ + "code": "stale-runtime-descriptor", + "severity": "minor", + "message": ( + "Artifact runtime descriptor covers changed or missing bytes: " + f"{descriptor.get('artifact_path', '')}" + ), + }) + for selection in sidecars.get("artifact_selections", []): + if selection.get("status") == "stale-anchor": + findings.append({ + "code": "stale-artifact-selection", + "severity": "minor", + "message": ( + f"Artifact selection {selection.get('selection_id')} points to " + "changed or missing bytes." + ), + }) + selection_ids = { + str(item.get("selection_id")) + for item in sidecars.get("artifact_selections", []) + } + for proposal in sidecars.get("transform_proposals", []): + if str(proposal.get("selection_id")) not in selection_ids: + findings.append({ + "code": "missing-transform-selection", + "severity": "major", + "message": ( + f"Transform proposal {proposal.get('proposal_id')} references " + "a selection not present in the bundle." + ), + }) base = sidecars.get("base_sidecars", {}) claims = base.get("claim_by_id", {}) if isinstance(base, dict) else {} nodes, edges = sidecars.get("graph_v2_nodes", {}), sidecars.get("graph_v2_edges", []) diff --git a/src/codex_science/artifact_runtime.py b/src/codex_science/artifact_runtime.py index 57f9a358..03e2dde2 100644 --- a/src/codex_science/artifact_runtime.py +++ b/src/codex_science/artifact_runtime.py @@ -44,6 +44,12 @@ "binary": frozenset({"byte-range", "record"}), } +MAX_RUNTIME_BYTES = 16 * 1024 * 1024 +MAX_RUNTIME_RECORDS = 10_000 +MAX_SELECTION_BYTES = 64 * 1024 +MAX_PROPOSAL_PARAMETER_BYTES = 64 * 1024 +MAX_REASON_CHARACTERS = 4_000 + STRUCTURE_SUFFIXES = {".pdb", ".ent", ".cif", ".mmcif"} MOLECULE_SUFFIXES = {".sdf", ".mol", ".mol2", ".smi", ".smiles"} GENOME_SUFFIXES = {".vcf", ".bed", ".gff", ".gff3", ".gtf", ".wig", ".bedgraph", ".bigwig", ".bw"} @@ -489,8 +495,12 @@ def describe_runtime( path = path.resolve() if not path.exists(): raise ValueError(f"artifact does not exist: {path}") - if max_records < 1: - raise ValueError("max_records must be positive") + if max_bytes < 1 or max_records < 1: + raise ValueError("max_bytes and max_records must be positive") + if max_bytes > MAX_RUNTIME_BYTES: + raise ValueError(f"max_bytes exceeds hard runtime ceiling: {MAX_RUNTIME_BYTES}") + if max_records > MAX_RUNTIME_RECORDS: + raise ValueError(f"max_records exceeds hard runtime ceiling: {MAX_RUNTIME_RECORDS}") relative = _safe_relative(artifact_path or path.name, "artifact_path") viewer = detect_viewer(path, kind=kind, media_type=media_type) if path.is_dir(): @@ -530,7 +540,10 @@ def describe_runtime( "warnings": warnings, "evidence_boundary": "This is a bounded derived view of hash-validated bytes. It does not prove a scientific claim, execute an edit, or replace format-specific validation.", } - return ArtifactRuntimeDescriptor(**material, fingerprint=_fingerprint(material), warnings=tuple(warnings)) + return ArtifactRuntimeDescriptor( + **{**material, "warnings": tuple(warnings)}, + fingerprint=_fingerprint(material), + ) def validate_runtime_descriptor(payload: Mapping[str, Any]) -> None: @@ -574,6 +587,10 @@ def build_selection( if not isinstance(selector, Mapping) or not selector: raise ValueError("selector must be a non-empty object") _json_value(dict(selector), "selector") + if len(reason) > MAX_REASON_CHARACTERS: + raise ValueError("selection reason is too long") + if len(_canonical_bytes(selector)) > MAX_SELECTION_BYTES: + raise ValueError("selection payload exceeds the runtime limit") for value, field in ((selected_by, "selected_by"), (reason, "reason")): if not value.strip(): raise ValueError(f"{field} is required") @@ -647,6 +664,10 @@ def build_transform_proposal( if not isinstance(parameters, Mapping): raise ValueError("parameters must be an object") _json_value(dict(parameters), "parameters") + if len(reason) > MAX_REASON_CHARACTERS: + raise ValueError("proposal reason is too long") + if len(_canonical_bytes(parameters)) > MAX_PROPOSAL_PARAMETER_BYTES: + raise ValueError("proposal parameters exceed the runtime limit") steps = sorted({_safe_relative(str(item), "affected step") for item in affected_steps}) outputs = sorted({_safe_relative(str(item), "expected output") for item in expected_outputs}) if not steps: diff --git a/src/codex_science/compute_backends.py b/src/codex_science/compute_backends.py index 5c3edabe..c4a9f322 100644 --- a/src/codex_science/compute_backends.py +++ b/src/codex_science/compute_backends.py @@ -105,6 +105,85 @@ def _reject_secret_environment(environment: Mapping[str, Any]) -> dict[str, str] return result +def _sha(value: Any, label: str) -> str: + digest = _text(value, label).lower() + if len(digest) != 64 or any(character not in "0123456789abcdef" for character in digest): + raise ValueError(f"{label} must be a SHA-256 digest") + return digest + + +def _normalize_inputs(value: Any) -> tuple[dict[str, Any], ...]: + if value is None: + return () + if not isinstance(value, list): + raise ValueError("inputs must be a list") + results: list[dict[str, Any]] = [] + ids: set[str] = set() + for index, raw in enumerate(value): + if not isinstance(raw, Mapping): + raise ValueError(f"input {index} must be an object") + item = dict(raw) + input_id = _text(item.get("id", f"input-{index}"), f"input {index} id") + if input_id in ids: + raise ValueError(f"duplicate input ID: {input_id}") + ids.add(input_id) + item["id"] = input_id + if item.get("path") is not None: + item["path"] = _safe_relative(item["path"], f"input {input_id} path") + elif not str(item.get("identifier", "")).strip(): + raise ValueError(f"input {input_id} needs path or identifier") + if item.get("sha256") is not None: + item["sha256"] = _sha(item["sha256"], f"input {input_id} sha256") + if item.get("root_sha256") is not None: + item["root_sha256"] = _sha(item["root_sha256"], f"input {input_id} root_sha256") + results.append(item) + return tuple(results) + + +def _input_preflight(spec: "JobSpec") -> list[dict[str, Any]]: + checks: list[dict[str, Any]] = [] + working = Path(spec.working_directory).resolve() + for item in spec.inputs: + input_id = str(item["id"]) + if item.get("path") is None: + checks.append( + {"name": f"input:{input_id}", "ready": True, "detail": "external identifier recorded"} + ) + continue + target = (working / str(item["path"])).resolve() + if not target.is_relative_to(working): + checks.append( + {"name": f"input:{input_id}", "ready": False, "detail": "path escapes working directory"} + ) + continue + if not target.exists(): + checks.append({"name": f"input:{input_id}", "ready": False, "detail": "missing"}) + continue + expected = item.get("sha256") or item.get("root_sha256") + if expected is None: + checks.append( + {"name": f"input:{input_id}", "ready": True, "detail": "exists; no digest declared"} + ) + continue + if target.is_file(): + actual, _size = stream_sha256(target) + elif target.is_dir(): + actual = describe_directory(target).root_sha256 + else: + checks.append( + {"name": f"input:{input_id}", "ready": False, "detail": "unsupported filesystem object"} + ) + continue + checks.append( + { + "name": f"input:{input_id}", + "ready": actual == expected, + "detail": "digest matches" if actual == expected else f"digest mismatch: {actual}", + } + ) + return checks + + @dataclass(frozen=True) class ResourceRequest: cpus: int = 1 @@ -174,8 +253,6 @@ def from_payload(cls, payload: Mapping[str, Any]) -> "JobSpec": if not isinstance(environment_raw, Mapping): raise ValueError("environment must be an object") inputs_raw = payload.get("inputs", []) - if not isinstance(inputs_raw, list) or not all(isinstance(item, Mapping) for item in inputs_raw): - raise ValueError("inputs must be object records") outputs_raw = payload.get("outputs", []) if not isinstance(outputs_raw, list): raise ValueError("outputs must be a list") @@ -196,7 +273,7 @@ def from_payload(cls, payload: Mapping[str, Any]) -> "JobSpec": working_directory=str(Path(_text(payload.get("working_directory"), "working_directory")).resolve()), environment=_reject_secret_environment(environment_raw), inherit_environment=bool(payload.get("inherit_environment", True)), - inputs=tuple(dict(item) for item in inputs_raw), + inputs=_normalize_inputs(inputs_raw), outputs=tuple(_safe_relative(item, "output") for item in outputs_raw), resources=ResourceRequest.from_payload(payload.get("resources") if isinstance(payload.get("resources"), Mapping) else None), timeout_seconds=timeout, @@ -344,10 +421,25 @@ def _job_dir(self, job_id: str) -> Path: def preflight(self, spec: JobSpec) -> dict[str, Any]: spec.validate() executable = shutil.which(spec.command[0]) if not Path(spec.command[0]).is_absolute() else spec.command[0] + checks = [ + { + "name": "working-directory", + "ready": Path(spec.working_directory).is_dir(), + "detail": spec.working_directory, + }, + {"name": "executable", "ready": executable is not None, "detail": executable or "not found"}, + { + "name": "resource-request-recorded", + "ready": True, + "detail": asdict(spec.resources), + }, + ] + checks.extend(_input_preflight(spec)) return { "schema_version": 1, "backend": "local", - "ready": executable is not None and Path(spec.working_directory).is_dir(), + "ready": all(check["ready"] for check in checks), + "checks": checks, "executable": executable, "working_directory": spec.working_directory, "job_spec_sha256": spec.fingerprint, @@ -372,17 +464,33 @@ def submit(self, spec: JobSpec, *, approval: Mapping[str, Any] | None = None) -> submitted_at = _now() initial = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at) _atomic_json(job_dir / "state.json", initial) + worker_environment = os.environ.copy() + source_root = str(Path(__file__).resolve().parents[1]) + existing_pythonpath = worker_environment.get("PYTHONPATH", "") + worker_environment["PYTHONPATH"] = source_root + ( + os.pathsep + existing_pythonpath if existing_pythonpath else "" + ) process = subprocess.Popen( - [sys.executable, str(Path(__file__).resolve()), "_worker", str(job_dir)], + [sys.executable, "-m", "codex_science.compute_backends", "_worker", str(job_dir)], + env=worker_environment, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True, close_fds=True, ) - state = _state_material(spec, job_id=job_id, state="submitted", submitted_at=submitted_at, worker_pid=process.pid, message="local worker started") - _atomic_json(job_dir / "state.json", state) - return state + current = _read_json(job_dir / "state.json") + submitted_receipt = _state_material( + spec, + job_id=job_id, + state="submitted", + submitted_at=submitted_at, + worker_pid=process.pid, + message="local worker started", + ) + if current.get("state") == "submitted": + _atomic_json(job_dir / "state.json", submitted_receipt) + return submitted_receipt def status(self, job_id: str) -> dict[str, Any]: job_dir = self._job_dir(job_id) @@ -552,10 +660,26 @@ def _job_dir(self, job_id: str) -> Path: def preflight(self, spec: JobSpec) -> dict[str, Any]: spec.validate() tools = {name: shutil.which(name) for name in ("sbatch", "squeue", "sacct", "scancel")} + checks = [ + { + "name": "working-directory", + "ready": Path(spec.working_directory).is_dir(), + "detail": spec.working_directory, + }, + {"name": "sbatch", "ready": tools["sbatch"] is not None, "detail": tools["sbatch"] or "not found"}, + {"name": "scancel", "ready": tools["scancel"] is not None, "detail": tools["scancel"] or "not found"}, + { + "name": "status-command", + "ready": tools["sacct"] is not None or tools["squeue"] is not None, + "detail": tools["sacct"] or tools["squeue"] or "not found", + }, + ] + checks.extend(_input_preflight(spec)) return { "schema_version": 1, "backend": "slurm", - "ready": tools["sbatch"] is not None and tools["scancel"] is not None and (tools["sacct"] is not None or tools["squeue"] is not None), + "ready": all(check["ready"] for check in checks), + "checks": checks, "tools": tools, "job_spec_sha256": spec.fingerprint, "resource_request": asdict(spec.resources), diff --git a/src/codex_science/experiment_planner.py b/src/codex_science/experiment_planner.py index 5bb609e7..dffc8845 100644 --- a/src/codex_science/experiment_planner.py +++ b/src/codex_science/experiment_planner.py @@ -169,10 +169,15 @@ def plan_next_experiment(payload: Mapping[str, Any], *, created_at: str | None = constraints = payload.get("constraints") if not isinstance(constraints, Mapping): raise ValueError("constraints must be an object") - batch_size = int(constraints.get("batch_size", 0)) + batch_raw = constraints.get("batch_size", 0) + group_raw = constraints.get("diversity_group_cap", constraints.get("scaffold_cap", batch_raw)) + controls_raw = constraints.get("minimum_controls", 0) + if any(isinstance(item, bool) for item in (batch_raw, group_raw, controls_raw)): + raise ValueError("batch size, diversity cap, and minimum controls must be integers") + batch_size = int(batch_raw) budget = _number(constraints.get("budget", 0), "budget") - group_cap = int(constraints.get("diversity_group_cap", constraints.get("scaffold_cap", batch_size))) - minimum_controls = int(constraints.get("minimum_controls", 0)) + group_cap = int(group_raw) + minimum_controls = int(controls_raw) if batch_size < 1 or budget < 0 or group_cap < 1 or minimum_controls < 0: raise ValueError("batch size, budget, diversity cap, and minimum controls are invalid") uncertainty_weight = _number(payload.get("uncertainty_weight", 0.25), "uncertainty_weight") diff --git a/src/codex_science/pipeline_compiler.py b/src/codex_science/pipeline_compiler.py index 418090ed..928f0ee1 100644 --- a/src/codex_science/pipeline_compiler.py +++ b/src/codex_science/pipeline_compiler.py @@ -20,6 +20,30 @@ SKILL_NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$") +SECRET_ARGUMENT_FLAGS = { + "--token", + "--password", + "--secret", + "--credential", + "--api-key", + "--apikey", + "--private-key", + "--access-key", + "--client-secret", + "--authorization", +} +SECRET_KEY_FRAGMENTS = { + "token", + "password", + "secret", + "credential", + "private_key", + "api_key", + "apikey", + "access_key", + "client_secret", + "authorization", +} def _now() -> str: @@ -41,6 +65,42 @@ def _text(value: Any, label: str) -> str: return text +def _redact_mapping(value: Any) -> Any: + if isinstance(value, Mapping): + result: dict[str, Any] = {} + for key, item in value.items(): + key_text = str(key) + if any(fragment in key_text.lower() for fragment in SECRET_KEY_FRAGMENTS): + result[key_text] = "[REDACTED]" + else: + result[key_text] = _redact_mapping(item) + return result + if isinstance(value, list): + return [_redact_mapping(item) for item in value] + return value + + +def _validate_command(command: list[str], index: int) -> None: + for argument in command: + lower = argument.lower() + if any(lower == flag or lower.startswith(flag + "=") for flag in SECRET_ARGUMENT_FLAGS): + raise ValueError(f"command_contract[{index}] contains a credential-bearing argument") + if "://" in argument and any( + token in lower + for token in ( + "token=", + "password=", + "secret=", + "api_key=", + "apikey=", + "access_key=", + "client_secret=", + "authorization=", + ) + ): + raise ValueError(f"command_contract[{index}] contains a credential-bearing URL") + + def _write_json(path: Path, payload: Any) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=False) + "\n", encoding="utf-8") @@ -97,7 +157,15 @@ def _schema_inputs(manifest: Mapping[str, Any]) -> dict[str, Any]: }, "required": ["question", "parameters", "inputs", "acceptance"], "additionalProperties": False, - "x-source-run-inputs": list(manifest.get("inputs", [])) + "x-source-run-input-count": len(manifest.get("inputs", [])), + "x-source-run-input-fields": sorted( + { + str(key) + for item in manifest.get("inputs", []) + if isinstance(item, Mapping) + for key in item.keys() + } + ), } @@ -196,7 +264,7 @@ def _reference_text( - source run: `{manifest.get('run_id')}` - source manifest: `{source_manifest_path}` - source manifest SHA-256: `{source_manifest_sha256}` -- source review status: `{manifest.get('review', {{}}).get('status', 'unknown')}` +- source review status: `{manifest.get('review', {}).get('status', 'unknown')}` - generated skill: `{name}` The source manifest path may be local-machine-specific. The SHA-256 is the stable identity. Never use this reference to bypass source bundle validation. @@ -210,7 +278,7 @@ def _reference_text( Reconstruct and review the runtime, lockfile/container, code revision, model and weight revisions, databases, hardware, seed, determinism settings, and material non-secret environment variables. The source environment was: ```json -{json.dumps(manifest.get('environment', {{}}), indent=2, sort_keys=True, ensure_ascii=False)} +{json.dumps(_redact_mapping(manifest.get('environment', {})), indent=2, sort_keys=True, ensure_ascii=False)} ``` ## Source workflow shape @@ -316,6 +384,7 @@ def compile_pipeline_draft( for index, command in enumerate(commands): if not isinstance(command, list) or not command or not all(isinstance(item, str) and item for item in command): raise ValueError(f"command_contract[{index}] must be a non-empty argv list") + _validate_command(command, index) limit_values = limitations or [ "Generated from one reviewed run and not validated for a new input distribution.", "External sources, models, weights, databases, and endpoints may drift.", diff --git a/src/codex_science/project_store.py b/src/codex_science/project_store.py index 5d1bbd12..b4a3583d 100644 --- a/src/codex_science/project_store.py +++ b/src/codex_science/project_store.py @@ -348,9 +348,10 @@ def import_run( (project_id, run_id, claim_id, json.dumps(claim, sort_keys=True)), ) if branch is None: + branch_base = parent_run_id or run_id connection.execute( "INSERT INTO branches(project_id, branch_name, base_run_id, head_run_id, status, created_at, updated_at) VALUES(?,?,?,?,?,?,?)", - (project_id, branch_name, run_id, run_id, "active", imported_at, imported_at), + (project_id, branch_name, branch_base, run_id, "active", imported_at, imported_at), ) else: connection.execute( diff --git a/src/codex_science/review_receipts.py b/src/codex_science/review_receipts.py index 3a7daf56..cb637896 100644 --- a/src/codex_science/review_receipts.py +++ b/src/codex_science/review_receipts.py @@ -24,7 +24,14 @@ def _sha(value: Any, label: str) -> str: def canonical_sha256(payload: Mapping[str, Any]) -> str: - return hashlib.sha256(json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")).hexdigest() + return hashlib.sha256( + json.dumps( + payload, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + ).encode("utf-8") + ).hexdigest() def validate_review_receipt(payload: dict[str, Any]) -> None: diff --git a/src/codex_science/reviewer_runtime.py b/src/codex_science/reviewer_runtime.py index 50364527..0a1642b6 100644 --- a/src/codex_science/reviewer_runtime.py +++ b/src/codex_science/reviewer_runtime.py @@ -10,13 +10,14 @@ import hashlib import json +import urllib.parse from datetime import datetime, timezone from pathlib import Path from typing import Any, Mapping from codex_science.artifact_store import stream_sha256 from codex_science.artifacts import validate_bundle -from codex_science.review_receipts import build_review_receipt, validate_review_receipt +from codex_science.review_receipts import build_review_receipt, canonical_sha256, validate_review_receipt REVIEW_MODES = {"record", "source", "method", "reproduction"} @@ -66,6 +67,31 @@ def _redact(value: Any) -> Any: return result if isinstance(value, list): return [_redact(item) for item in value] + if isinstance(value, str) and "://" in value: + try: + parsed = urllib.parse.urlsplit(value) + query = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True) + if query: + query = [ + ( + key, + "[REDACTED]" + if any(fragment in key.lower() for fragment in SENSITIVE_FRAGMENTS) + else item, + ) + for key, item in query + ] + return urllib.parse.urlunsplit( + ( + parsed.scheme, + parsed.netloc, + parsed.path, + urllib.parse.urlencode(query), + parsed.fragment, + ) + ) + except ValueError: + return "[REDACTED-INVALID-URL]" return value @@ -205,10 +231,16 @@ def finalize_review_response(packet: Mapping[str, Any], response: Mapping[str, A independent = response.get("independent") if not isinstance(independent, bool): raise ValueError("independent must be a boolean attestation") - response_modes = sorted({_text(item, "response review mode") for item in response.get("review_modes", [])}) + response_modes_raw = response.get("review_modes", []) + if not isinstance(response_modes_raw, list): + raise ValueError("response review_modes must be a list") + response_modes = sorted({_text(item, "response review mode") for item in response_modes_raw}) if not response_modes or set(response_modes) - set(packet["review_modes"]): raise ValueError("response review modes are empty or exceed the packet") - reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in response.get("reviewed_claim_ids", [])}) + reviewed_claims_raw = response.get("reviewed_claim_ids", []) + if not isinstance(reviewed_claims_raw, list): + raise ValueError("reviewed_claim_ids must be a list") + reviewed_claims = sorted({_text(item, "reviewed claim ID") for item in reviewed_claims_raw}) material_claims = set(map(str, packet["material_claim_ids"])) unknown_claims = sorted(set(reviewed_claims) - material_claims) if unknown_claims: @@ -287,6 +319,6 @@ def finalize_review_response(packet: Mapping[str, Any], response: Mapping[str, A # Rebuild the receipt fingerprint after adding packet linkage. material = dict(receipt) material.pop("fingerprint", None) - receipt["fingerprint"] = _fingerprint(material) + receipt["fingerprint"] = canonical_sha256(material) validate_review_receipt(receipt) return receipt diff --git a/src/codex_science/skill_maturity.py b/src/codex_science/skill_maturity.py index 62486932..41dfc6d5 100644 --- a/src/codex_science/skill_maturity.py +++ b/src/codex_science/skill_maturity.py @@ -16,6 +16,12 @@ "codex-science": ("## Research contract and evidence graph", "## Workflow", "## Completion test", "## Boundaries"), "science-provenance": ("## Run contract", "## Inputs and retrieval", "## Outputs and claims", "## Safety and integrity"), "science-review": ("## Inputs and review mode", "## Review workflow", "## Receipt and independence", "## Boundary"), + "remote-scientific-compute": ( + "## Decision contract", + "## Execute and monitor", + "## Outputs", + "## Boundaries", + ), } FRONTMATTER_RE = re.compile(r"\A---\s*\n(?P.*?)\n---\s*\n", re.DOTALL) FIELD_RE = re.compile(r"^(?P[A-Za-z0-9_-]+):\s*(?P.+?)\s*$", re.MULTILINE) @@ -36,7 +42,13 @@ def _frontmatter(text: str) -> dict[str, str]: match = FRONTMATTER_RE.match(text) if match is None: return {} - return {item.group("key"): item.group("value").strip().strip('"\'') for item in FIELD_RE.finditer(match.group("body"))} + result: dict[str, str] = {} + for item in FIELD_RE.finditer(match.group("body")): + value = item.group("value").strip() + if len(value) >= 2 and value[0] == value[-1] and value[0] in {chr(34), chr(39)}: + value = value[1:-1] + result[item.group("key")] = value + return result def _load_json(path: Path, label: str) -> dict[str, Any]: @@ -172,8 +184,14 @@ def evaluate_skill(root: Path, skill_dir: Path) -> dict[str, Any]: record["name"] = name computed = 1 complete_sections, missing_sections = _sections_for(skill_dir, skill_dir.name, text) - has_provenance = "$science-provenance" in text or skill_dir.name == "science-provenance" - has_review = "$science-review" in text or skill_dir.name == "science-review" + if skill_dir.name in {"codex-science", "science-provenance", "science-review"}: + # These three skills jointly define the first-party handoff contract; + # requiring circular self-references would not add evidence. + has_provenance = True + has_review = True + else: + has_provenance = "$science-provenance" in text + has_review = "$science-review" in text if complete_sections and has_provenance and has_review: computed = 2 else: