diff --git a/.kiro/specs/ha-commit-barrier-rollout/tasks.md b/.kiro/specs/ha-commit-barrier-rollout/tasks.md index 01f8d5f1..6a0b86a1 100644 --- a/.kiro/specs/ha-commit-barrier-rollout/tasks.md +++ b/.kiro/specs/ha-commit-barrier-rollout/tasks.md @@ -5,7 +5,7 @@ > Verify exact line numbers with grep before editing (research.md §Verified Code Anchors has the 2026-06-11 verified table; files drift). - [ ] 1. Write-path classification: table, dispatch, RETAIN routing -- [ ] 1.1 Write RED tests for classification totality and RETAIN registration +- [x] 1.1 Write RED tests for classification totality and RETAIN registration - Create `testsuite/test_commit_barrier_rollout.c` (XCE_TESTING pattern, AAA structure) and register it in `controller_unit_test_src` (meson.build); declare `wal_commit_mode_t` + `wal_classify()` in src/include/persist.h so tests compile - Tests: every value of `wal_entry_type` (wal_types.h:44-58) maps to a mode — WAL_JOB_SUBMIT/WAL_QUEUE_CTRL → WAIT, WAL_WORKER_CTRL with detail JOIN/UPDATE → WAIT and detail DEAD → RETAIN, all WAL_JOB_* state types → RETAIN, WAL_CHECKPOINT → LOCAL; an unknown type triggers the XCE_TESTING fail-fast path; a fact-type append through `persist_job_hook_fun` registers the entry with the engine in RETAIN mode (observable via the engine's retained/registered accounting against a stub engine) and returns without blocking - Observable completion: build succeeds with the new header declarations; all new test cases FAIL (red) against the unmodified persist.c @@ -104,3 +104,10 @@ - Confirm downstream readiness: classification guard active, sweep audit recorded — the hard precondition for ha-single-leader-role ("barrier before state removal") is satisfied - Observable completion: full testsuite green under ASAN; audit + ordering evidence recorded - _Requirements: 1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2, 3.3, 3.4, 4.1, 4.2, 4.3, 4.4, 5.1, 5.2, 5.3_ + +## Implementation Notes + +- (1.1) **commit_submit(COMMIT_MODE_RETAIN) is registration-free** (commit.c:233-239, reviewer-confirmed): commit state derives purely from registry watermarks at ACK time, so RETAIN routing has NO per-entry engine observable. Task 1.2's RETAIN registration call is therefore contract-documentation, not bookkeeping — the observable contract is: non-blocking return, local-append-rc-only propagation, pending via lsn > committed_lsn, ACK-driven advance. Test 8's behavioral pins execute only at GREEN (Unity longjmp at the wal_classify carrier assert). +- (1.1) The unknown-type fail-fast test forks and asserts WTERMSIG==SIGABRT — task 1.2 must implement the XCE_TESTING guard as assert/abort (or the test needs a matching tweak). +- (1.1) wal_entry_type has 13 values (wal_types.h:44-58), no WAL_NOOP yet (lands in ha-single-leader-role, which must add a classification row + sweep entry — the totality sweep will catch it). +- (1.1) Suite baseline is ZERO failures since ha-commit-engine 6.1. detect_leaks=0 remains the harness norm (6 inventoried pre-existing leak groups). diff --git a/.wolf/anatomy.md b/.wolf/anatomy.md index bf8c2de6..74b2a86b 100644 --- a/.wolf/anatomy.md +++ b/.wolf/anatomy.md @@ -1,6 +1,6 @@ # anatomy.md -> Auto-maintained by OpenWolf. Last scanned: 2026-06-11T14:56:17.297Z +> Auto-maintained by OpenWolf. Last scanned: 2026-06-12T04:13:06.312Z > Files: 511 tracked | Anatomy hits: 0 | Misses: 0 ## ./ @@ -187,7 +187,7 @@ - `.gitignore` — Git ignore rules (~47 tok) - `codegraph.db-shm` (~8738 tok) -- `daemon.log` (~2464 tok) +- `daemon.log` (~3298 tok) - `daemon.pid` (~44 tok) ## .kiro/settings/rules/ @@ -253,7 +253,7 @@ - `requirements.md` — Requirements Document (~3181 tok) - `research.md` — Research & Design Decisions: ha-commit-barrier-rollout (~3672 tok) - `spec.json` (~143 tok) -- `tasks.md` — Implementation Plan: ha-commit-barrier-rollout (~3468 tok) +- `tasks.md` — Implementation Plan: ha-commit-barrier-rollout (~3743 tok) ## .kiro/specs/ha-commit-engine/ @@ -262,7 +262,7 @@ - `requirements.md` — Requirements Document (~2715 tok) - `research.md` — Research Log: ha-commit-engine (~1345 tok) - `spec.json` (~140 tok) -- `tasks.md` — Implementation Plan: ha-commit-engine (~3031 tok) +- `tasks.md` — Implementation Plan: ha-commit-engine (~4593 tok) ## .kiro/specs/ha-config-bootstrap/ @@ -374,7 +374,7 @@ - `error-handling.md` — Error Handling Standards (~782 tok) - `ha-async-io.md` — HA Protocol & Async I/O (~1360 tok) - `product.md` — Product Overview (~1155 tok) -- `roadmap.md` — Roadmap (~2182 tok) +- `roadmap.md` — Roadmap (~2206 tok) - `security.md` — Security Standards (~804 tok) - `structure.md` — Project Structure (~1139 tok) - `tech.md` — Technology Stack (~1144 tok) @@ -393,21 +393,25 @@ ## graphify-out/ -- `.graphify_labels.json` (~5294 tok) +- `.graphify_labels.json` (~5614 tok) - `.graphify_root` (~1 tok) -- `GRAPH_REPORT.md` — Graph Report - persistent_ha (2026-06-10) (~44230 tok) -- `manifest.json` (~33884 tok) +- `GRAPH_REPORT.md` — Graph Report - persistent_ha (2026-06-12) (~45713 tok) +- `manifest.json` (~35154 tok) ## graphify-out/cache/ -- `stat-index.json` (~43037 tok) +- `stat-index.json` (~44628 tok) ## graphify-out/cache/ast/ +- `004cc8d07f2f670da29d7c7f144c8875f274b8438fb1d4306fb6cc676c745fea.json` (~1050 tok) - `005d423b344fb7001a76e9a01e7226b28957f0318f503b6acd0f7f72a3fcde8e.json` (~227 tok) +- `008610a0f4315b9a68f7cde989a73b27de885c7ab2c96a9e7440be16cf2fa094.json` (~2422 tok) - `00a5ff6db9d63a87d19ca8b0a68d7105c6ea2fa89be5f94c98b257bced832042.json` (~11282 tok) - `00bf4ac62988ea0494c2ffda33b92affa91df5763081a54b4c7d6c308fec999e.json` (~269 tok) -- `00ed358e3d7e97d92e2173d4a7e6c435df35f1b8d78f4394232a5cc2bb771e72.json` (~2234 tok) +- `010e4ba8e25fd6fecc0ab19203ed3f62aeb3bb1a42aa4b4712f2040eb7816392.json` (~10023 tok) +- `01181572b4bf0d966f8391007f85c7a58838d83ef521a9fb455e180c704c78be.json` (~4182 tok) +- `012463e815ba3f31b95b398fa0242f2f331578618542a0910dfc9e9be8c2a0a1.json` (~15340 tok) - `016845199833895f368a40f072564f7b5502cd652cef19f58dffb9c1931bd179.json` (~1092 tok) - `01c91aa6fa18cc3129d3f6451a04b235c9528a88a165b1b6126f87c37bdc4048.json` (~14877 tok) - `01d5ae01a90dcbf2812ac05362927a7f9320cd5f20875838057caf559be8c5be.json` (~224 tok) @@ -415,71 +419,80 @@ - `0211c966a13950b059d40d37ff94e62d5b7ba25b554407097e4f964eaac39102.json` (~206 tok) - `023e107588bcbc8780b5fe90b346a96c5243e9192778aefb9bc9fcd92b5cf692.json` (~3708 tok) - `0297e5997050ad938795b7335ba2e08338aa3e4b36046f19b34c3069fd8d4e60.json` (~57184 tok) -- `035ce098fbf2e2d9573c9b3f8d0cb7c1a586cba8ff7db053607295e428ee8776.json` (~4604 tok) - `0401198757a993070c98c754996a960299d605d55ee524b2f1aad817fa94322a.json` (~3011 tok) -- `040c728803592a93b3b620ed64704be56eda59d5622db431d38be905fcf267a0.json` (~2520 tok) +- `0438e6d96c89517e2640f19067b6685b2fb95af119ecc5df3f896adbba6c1933.json` (~8120 tok) +- `0440846ef240f3abb98b1ca27304c5cdbe56779ba1cc857850dd7a26cad49b66.json` (~2935 tok) - `04583d91aa48af789b952136fb7c52041b0d632793246105f3af6fe070727a07.json` (~2049 tok) +- `0490c7b9fd5bba4286f161b03204a38f9ec48a4889ab597b70a20172aacbf4c6.json` (~8120 tok) - `04e3a10a165fc6dea955a31ec5c48db1e75c23545a74a734cb538d8ef8ac06ef.json` (~7004 tok) - `053929f384ca0a4fdabda00415d3f0e0a7f8ccbd86f273deadd54ac0590492a2.json` (~18832 tok) +- `0550909cf1a1da30dee6c2653f79809399374ee35919a467be9583fa7d27f5c5.json` (~71978 tok) - `057d67f05183f6fe7fd3414cb6f46de234ebe58fa3dbca7dddc5441ee3f8a2f1.json` (~6218 tok) +- `06578221cb7a75ee06e0460d5a42c48037e3ddcebbc21fae45825886ceaa0355.json` (~15340 tok) - `06a0b286e4c22fb4c981239820aea2042950d1d21dc96de40226b14cc696065e.json` (~8244 tok) -- `06c67fa5ce909423cf3cbb56ebbab4e76f1c75b94c38514f3b9a728ecfab4a0a.json` (~4252 tok) - `07014154947b85f86499894c59be83a316bf3fc006c8fc55c316cb893462977e.json` (~3083 tok) - `074a8bce08af84573c3e948b6dabbecdb6a7e99daca393ddaae4f816b1b24411.json` (~309 tok) - `074b4a288109008bdb35dccbcbc81a7e2b029bd17e4707fb967a94d962a77b69.json` (~4508 tok) - `0752d0d43dbdaac573d4c7129d228ad267a5a5d8bc6820fde4b6e6a6a3062100.json` (~586 tok) - `0799df8da46779b19ce58b3ae776614f647d6d5f009f418520eec8897f538f9d.json` (~1961 tok) - `07d0b1dbd63fc8b71cab7d602d56453c4ed0a4170b3efe8d387de29c09487ead.json` (~467 tok) +- `07e3abfd90acf1c5d2611d59e5b4f199a6575888c2fb5e9e3b799ec4059c85e5.json` (~374 tok) +- `0857f9be25466f6f3c0da48438f7c7cf35d0140010f4639a673c2f35ae98a002.json` (~2468 tok) - `0894c6309b9aae7fdb3b1aef7fb1dfaf1b55fd8f31ac96a8112916d55ebbedba.json` (~315 tok) - `0895960bce4ca499c4505de3f178508e1cd9dda69a3e8ab43195493957793a34.json` (~7103 tok) +- `08e2728caebdaff0489f8e7bac962b9af1da74bab7072aa9b430024055889dfe.json` (~4802 tok) - `09cb38261b447a3240b1361064d5bd8dada4c7eb8430b961c0476e902bc11ea0.json` (~8966 tok) - `09fc4c36a264a7b935bf7ffe9e8506fc3452b8e90c8ecb7e22d6a740854ebc76.json` (~1802 tok) +- `0ac5f46d303d3f4b45966029f5cf09643eca5e14117e4976e097b8e83120401c.json` (~76 tok) +- `0b84abe002c91c4608ec56b28a1a39a066c1d64f8bad5182c89a905e0f51138f.json` (~6400 tok) - `0b91b1fcfc8ae3bd247107dd81b84a03aea885632599033c06e8f9f372f8d967.json` (~504 tok) - `0bf7323dd6f181b3c8536da6fda90904a6d3ea5fb99d9a9211175beadd33ae49.json` (~1973 tok) - `0bfddfa19374691c60a82a0208b83881a8c2c8adc9e888576e7aa64801be9f9b.json` (~4672 tok) +- `0c349cee86e9ad96945f644460c1e3faa3c9af3df65079ae786207179039cd55.json` (~2248 tok) - `0c86267402031c08bcc3b4b5a1202712295c728654b1958f36368c636c0416a0.json` (~3581 tok) +- `0ccb2e06c2139ef03aef6ba894efe1025c61daed9a9ef15b891319ecc4d9652e.json` (~5859 tok) +- `0d10839a95bf89b63b21c49624d5447044dcb1e9264157ccde87e557edb0a640.json` (~28018 tok) - `0db57ca4ac0c3ee6abc97242d0a04f274b18b83ba32d3b9c2b8c39b949c52fba.json` (~2878 tok) -- `0db5ed270aa5110886470637e2424f43fbccb568c93504cf43665072d93f5694.json` (~28384 tok) -- `0e5bd9bc77b2146c3f6aeb493534b1a157e6574c461f8c65f2ce410277d1f3a5.json` (~14508 tok) - `0e688ceec91f4b7c192ec781324e9ddaa517ac653dc2256095ec17c49cb12928.json` (~309 tok) - `0eada2ef529a3511cb04b120cb3d2247c7822f9aa4c4d9dab4f7cfdc72d31e8a.json` (~1980 tok) - `0ee129793c23e5933eade7198c1f08c5d84c9c26139e7eb7ff067d5c52d08d1e.json` (~6165 tok) +- `0ef710579ddbf54eabe4f4ef870293e98d3ba4fc8f88351e31cbd7b579054d0b.json` (~60278 tok) - `0f5341ff6ca93ba2938f8f8745a9739a2f5da1705bdb8b6abca03899d71e75c6.json` (~224 tok) - `0fa8ec51434b17edc5a26d6a037f6799618b873ebae92d6eac751f0f3578c038.json` (~2079 tok) - `0fb70f67355c6ee9bbb1dd9f5d55d21fdd019790f551898c33e6dd97d0742331.json` (~468 tok) -- `1033638fc4ff4387b3a1b6b6562ee70e235d06db560c03029e91195b4ab4aa51.json` (~23075 tok) - `103e27126a9e5950aa8c8063c54df342d6737d340c5411cf667b24a00dcc28a1.json` (~3251 tok) -- `10bcf391471d3f7d46ba88ee5071d942477959fa93883770c806a6bfd697ace6.json` (~215 tok) - `111178800ae1876a431e527f3e0211c41e066afcf3636483674e1de0177fe726.json` (~3496 tok) - `1115d59cae0de03be0ebfeec800e4bc7a3142b1df07c22537ff00164f6d4becd.json` (~845 tok) - `114f295685c5796b4dcd914d8c84d0e74823a3000bce5dbf796f52519176b793.json` (~275 tok) +- `11572830a363253a8645bfc2cb739462796329cfa74713b04ceab4b9abb7d6e9.json` (~60916 tok) - `1164b6d75143d4caf1a8c3ccd18258c793607dae4d869a5490fc09564b26c2af.json` (~214 tok) - `1196f6d0f42ffbd96922026767a312e1d25869970da006eda64602bb96691c22.json` (~228 tok) - `11a454492a215c5707704555427cc5a8a8d8c9e37140369fe3fd48964b6cd6be.json` (~3120 tok) +- `11d0e37a4fec59ec78b60e8460549aaa4e31c61dda04508aae456719ef213d74.json` (~81 tok) +- `12606235c64f192ab75e049b7dc8c659af9d9cefa4c8f0ecd043ddd1087ca8c6.json` (~81 tok) - `12c733acf3d86ce393643e971e1f4940fd1758a2696817d2f2a4247f41b2f480.json` (~5120 tok) - `1308673b6c4853c3a6bffa60799745c5db7ee0be57b2f2217e66a5fefded692c.json` (~1110 tok) - `1312e11eefb522023a6437df3bef6b7a618dc2a482005da8315b46a9fe0264a6.json` (~7909 tok) - `1459add9026c6903d390de90c7d1dea4d5a9d381a3930785e7a480dc27b5ec00.json` (~6672 tok) -- `146c3cb1dabeeb0028a522ccbcababaebf2936d037895d455d55b456d8ed300e.json` (~24262 tok) - `14a93f201d19d457092b9bf881e8ac62f44caf42d70192b2106944c8380374d3.json` (~76 tok) - `151a099eec558a9e4c89bc08aae88ba096681971be1a94c7625343fd9a3a88e5.json` (~7640 tok) - `15224225923ce6697e098312e1dc8429b2768b415534886f344c212446813d83.json` (~495 tok) - `152d9c2445a8247d38b52f6ecab0cf6f36ba144aeb93e0bc7603445e7ed2c6cc.json` (~5075 tok) -- `15690502c2967a3acc8b758b1349bf7bb913b0f7c751db503e9c0a838405d0a3.json` (~1494 tok) +- `157041af18754d4729c7ead7ed02190c14fa052b3c65deac29309014894e67a8.json` (~10023 tok) - `157c7929f5a2274e2f98c623d21343ab2f01711d4fd4e742caef92e2bf2b3463.json` (~4857 tok) - `15cc2d188fbb7c7bc937a39311efdc8cd2536b01947309e45f5563c0e40a7923.json` (~1376 tok) - `15f43122fe941435ef4d1c92bf796b0198998b92df0b915e6e173a4a0d3e0dcd.json` (~2943 tok) - `16307f083b22977db26185b0f7407bc40e4f74e2098040ae978316d99d4444d8.json` (~335 tok) -- `165b7e741198f9b5307232284c264951a8e81f74d8bab63b5ef970178948d037.json` (~15473 tok) +- `16831fd1cef7d7b478af79b83f1cc33f276badb9f9bc9f20c827cc88ff417cff.json` (~10294 tok) +- `1776308554d389fe728a6b9a5c091cc650242a088a08818acaba4aaef16ff4e6.json` (~81 tok) - `17c6bf76cfaf2951c6567ecaba00b057c5bf1a8c380a669df83e1a3cbecf630f.json` (~482 tok) - `1814c8441ce6baf90c83e273914a1f472136a2e348e12e9eaf902367a650574c.json` (~323 tok) -- `1818c3dc5b35e364d6e65e3f6380b764812be244ceb3d0e47d08f2360a33d20f.json` (~2695 tok) - `181955f831ace827cab57775ab5c606ca3546d23e3b084d7bb3a386ab30603dc.json` (~182 tok) - `1870fbb4d9d0c1c96f6d0206b980cc4acaa128a432b581bcacec7f6fe866eecd.json` (~1988 tok) - `1880ee75786ce6a428cdaa0f911f58973080482a358b7886fb6aee65c43beda1.json` (~2444 tok) - `18dc5928206967173c007cbb765877d41488abd0fcb0de1d56abecbaf3f724b7.json` (~9240 tok) - `191b4446d235d84fd3681d7e952ad2552fe572d3a5757a5074074549c4e27d50.json` (~1829 tok) -- `192384f95ec32a2a4bd7c5ae2b29b1aa406c4b67feb27b17b9bf0119e8d73073.json` (~8827 tok) +- `19b8b2c7c84599c0cf67fb94d4245dc4ca47aa88b54194aece2463712f9e2269.json` (~374 tok) - `19c8003bcc5314fd08d9000b104fd6d443d7959d5babc40fe8b7eb1397056352.json` (~2145 tok) - `19ef06be285e1769d58acf0223c5a445d872c31d629d30dee1c5eeb06c69ded7.json` (~35085 tok) - `19f40018949a73961f1be70427bfa744ba1dbb6c67e597c65b3f652d357be8f0.json` (~324 tok) @@ -488,27 +501,26 @@ - `1b3b474cc46958f3b63e6ef226c2f6b5d7b0275ca7d4e62a2e92c331fc09972c.json` (~6923 tok) - `1b3eb8209fe8ae83a46fe284c12c8577df135d756ac662156e4a61eab79e5601.json` (~1934 tok) - `1b5a6250958790565da71172c0f2da9c26e4651aaec7a353d34f33555f38f4c2.json` (~1904 tok) +- `1c0cfaeda41c83a55365b1b5b340cf80581217b083b93ce0dd156fc886276256.json` (~3131 tok) - `1c86ed59f0918f5ea0f47f54917854eb2ce2a9b9b6724a107c9237ed0a7f7ea2.json` (~2916 tok) - `1c906780916fd91e042adaa801f385dc55bc1f29c851908ad61f8c5db0040107.json` (~2163 tok) - `1c9619334c216e67eec7d7a1c420d26476001173e926088e46d4d2acdd52f8d4.json` (~320 tok) -- `1e9ab2a0496af02f33eb7ebea127e78c55ead91d669f3a9100f3a842f0b12d43.json` (~376 tok) -- `1ea8dbf931720d6f5c9a0a633e049cfab3c0ca64a8f62c73649c5f3a9623ea89.json` (~2023 tok) +- `1e8313e77c1ec1f11b656b60fdcefe81f0d61322c4cdcbf51cf918c7e7965420.json` (~15340 tok) - `1ef77ec32c809ea9f6abe455804f5630dc3a9693e633b03068f3546dfeeab099.json` (~3149 tok) -- `1f2309e6fc38f86aff26c6279d13cd099b7dc28a6dacb1c0449f2a3895c7f972.json` (~8709 tok) - `1f6c6535ebbdcedd95bf44e6b96f07f1234d7ea54cb747d6e113f90ccf4bb7ea.json` (~13910 tok) - `1f7e683af7e73fefdcf14ce40c2ee81dfa8aa113b7aa90b51067d870710baa7a.json` (~332 tok) +- `1fdd469bb6d51958ab2ba15266b745abf7014618c2055cf5b098276ea4a501bd.json` (~62630 tok) - `1fe907805f188b6cf4e490bbab545a31e875b83a02bb0e7220e1b8666ddee3e6.json` (~332 tok) +- `20456a4402821d8eeb5ccfb2b27a16965b9d4ed85058de966a1f4894fe8443eb.json` (~9852 tok) - `204755bab645cbf0ae12bb0bf8787de41a1f94e10e2988eff7357b93085e7466.json` (~8392 tok) -- `2058b10a526a0b92739453147a88ab714f1f59969571fa32d606e2b782e07706.json` (~269 tok) -- `20e9879259b5e86a625a3f73c0c761cf95586203098387d2107f3306fccfc414.json` (~25199 tok) - `20f4f40d81b0a264990d7aab7acdf82a1b66fb32d803b2b878f056973584921a.json` (~474 tok) - `2155a5a1575dcef9f688750a7ab5aef21abf6857b99ca64f0140ec2d95679d8a.json` (~464 tok) - `21646b4f891df0a3a87bbf8bc22fbed445cf2865d4fa7c6b1c3366b5b5a55b37.json` (~1737 tok) +- `217a1a381b51231bf05ab3ee23119b669fd864061aa1f9b9e2446263ff38d372.json` (~40340 tok) - `2205e0519599088ede54092b2c13d182833ad49f15a64411c26758d1cbcc30e4.json` (~6577 tok) - `2218f88cc182f9064384f7ef7a3ac49908b837dd2d067cd8dbced90e8d043884.json` (~8526 tok) - `226d09a3ccfa62cc05d1b61facad2fd5f269e9f41888caa8457f1dc846435b4e.json` (~3797 tok) - `2272750056f162f7c74228b5a1cf2324b1299841d24f98d0dd31696682e18f28.json` (~2727 tok) -- `233b1c66b43b57be1da6cb39948414796dba89ab3a4c5e87d39532914bf514d1.json` (~81 tok) - `236dbc464b50c074e467d53cab78fb8af06c934b191349a006f12477317ddb91.json` (~637 tok) - `237f88b4759f44668ce8cd4941b4231ee77fcb94c932ef3f283e596de3de41af.json` (~1262 tok) - `23ae45376874ad36b52972b9b9031b66aa76f34d1de4b793801908d2dc0bdbc4.json` (~4128 tok) @@ -522,21 +534,27 @@ - `25f6c2a43106baedfadd95c63a6756af97347a44dec2331761762e474aae4efa.json` (~2638 tok) - `267c67c5b1f429f43ed99481e42652c822ffcf51d0d0c6ba4121d120c9f74e4c.json` (~441 tok) - `2729cd44ff4befe72984d232b7ae9d0a2bda3c0ad487f7907c73680caec307e4.json` (~14268 tok) -- `27ef54aa90a7e990b60d7303e1b89bd5c3ea6157f3836962c13b9b455a9c6c29.json` (~376 tok) +- `273726998e57f52d3c0ddf9359e675185f77ff4652c613f53575dc278f326d13.json` (~374 tok) - `28d052ea57fd7eef39d0ee9fe1d7c7531b0ddea082a123a6997c50f0703f2d98.json` (~1340 tok) - `2930482f7a15ac6d50037b6465a084fdc9cbed52609068751020b5c3016088c3.json` (~1279 tok) - `29cd8d6bcb6dadf0782652207d01f8eb0435d068aaa91c582d98a961fe3ad8fc.json` (~2251 tok) - `29f9d3dbb7661a1fec506165923e621ed0a0c0f1fdab925cb1759bdaf50e5f03.json` (~154 tok) +- `2a0380b1dc6fa6fc26764acc5139a7e3de3f3973646f38c58141c13f958bd090.json` (~10023 tok) - `2a7e32f31ea531530978500ba347d3eb463270ec6c4f4b0e300c84a9fca4729c.json` (~1131 tok) - `2ad72e7a427f8046c048381f2292e12f34926b58456a02043b407d6cf490a77a.json` (~28558 tok) -- `2c6eb25824f1bfed0a1768121ab45bc0095a0476b08d4bad6000e3d3016d6a94.json` (~9514 tok) +- `2b0b0f1eb49df36a2e8fd4f88317124dd1edf9a0f08545a1a53accd6b47b16b1.json` (~309 tok) +- `2b9e4e051f5e39e6df4dc1c1ff7611d9a3590262f41b88e978fa664c596524b8.json` (~269 tok) +- `2baff6d343f2ec98cec20237f085bb8804623059aa145279f05e1d921aa1f8bc.json` (~68072 tok) +- `2cbca2b1bf0345399a27e19131777f4d537a479e5d9eea2aefe60bfa6f0c848d.json` (~5279 tok) +- `2d59df43c2cc4d09ea8566c4a94d70cf1a59b8d86e8fc4f9d7c49e92fd356d75.json` (~702 tok) - `2e097eddc32d49647dfd7b1c33bb454265b5587ec5bf148029c1ea496321073c.json` (~5588 tok) - `2efd2ac807fbaf51e1d509f8703b3a911bd6a2e99f11b7738f040cd777e02232.json` (~565 tok) - `2f314917390ab9fc2f54f98e76033a35b0ac32f11d28a23c6cef9b10c03440d5.json` (~296 tok) -- `3051558896d80ce15c2165110b58a5107c38692f9a33975e19154d8c5cd3a3d5.json` (~9256 tok) +- `2f57014ed460815e6563369443502723f1348db370e70fbc68644596d7ac3dcf.json` (~10023 tok) - `30e46776a7b0b61fbf25b89f2cbfc05cce0db668f4e79ff9e2b5094e3b968fb9.json` (~2029 tok) +- `30f7481b9127ef4a3254ec2366cdb021459b664d02579f3dd1e68507565b835c.json` (~81 tok) +- `310e465ea5ffc9068fb51b3d4fe4023772368a11d980ff9dad684b2c35f085c5.json` (~652 tok) - `31925f74354626a58d815d51a97d505725f05f1cdd757f6d07c35080d040c3b6.json` (~10147 tok) -- `31a0674a78506546e9a046eca0d98ddbe8f4e101c9fa456f61cf25f04c0ee1c1.json` (~15335 tok) - `31e922879882c5b3f25d90a04b60bd1d7379d4c9129844b17a6829bc4bf89615.json` (~2210 tok) - `323b81424dba12471c44d1dc784633bf1d5a69a98fe31afafbdc964b63dd28ed.json` (~7914 tok) - `324d5a22da8a2bc8a6a657073dcd1ca83f95aafde5553a4425c41fdfd18b1502.json` (~844 tok) @@ -545,32 +563,36 @@ - `3345b676a5c6e6c2392fc43615a0e0da1824dd13c83d3598598690f8c63697ef.json` (~15396 tok) - `336119f075fb4d93e7e000da36699438170281aeb6626e539422c74dbdef4df4.json` (~36810 tok) - `3399458fe3fc893d4f104368ef5ecd4afb95871b46436b2d09d9c71e700cefd8.json` (~785 tok) +- `3484ae0eb95d4002544aa8cf72d51923c87611c889de4d867fa3a4f2a24fc153.json` (~8120 tok) +- `34a0845d9715d767248d397f0f2234418c0a1828a171bccb04ffba3e1929f668.json` (~652 tok) - `34a753617eaab17b4b08bbf3025b584347ef95d7cda711babde228191f653647.json` (~9568 tok) -- `34cb4fe40371cf6daf535c8965c8137b3a5f62e542eb9a36b5c8a291d06f00f4.json` (~15585 tok) - `34ec637d899925d6db1ba12bc8bf368183f2b0e3636ba7212b2056bf7bd642c6.json` (~85 tok) - `350898ae8fe9d6a099d42d9d292c43efb8a1a37182085f7acadb6b34602d6024.json` (~11199 tok) +- `351d8046e5f9f289979f6864f99509fc2fc25b015c2ad343ab4b99193132e56e.json` (~715 tok) - `35687c768af0e0ff77d76bd1dcc018cd30b6b20c597a67586137ed0a9878bd61.json` (~965 tok) -- `3599a8dc3b0feedcd96953c5b9a542297ab758a162d0b0088459ab77402ae921.json` (~81 tok) -- `35d17cd5762e1f784ec9dba4716f7a03bdbddea7e4146a7131797d189fc8c8f6.json` (~2637 tok) -- `35d814bbc3744c3dca9dbab5cc823ed663560fdac1134dc98b955aec777d2f30.json` (~269 tok) - `35ec72e792b59b19044577eb54a5e7540f026ac35c6003306f597a5f1387d227.json` (~5448 tok) - `35f823871a12ed1809e20d70a663c8a8929fb35f3748705499e32df6cc4ec5c6.json` (~277 tok) - `35fd2a3ceb004b1d47fe5ccc509be97f4d380faebcfff32399237e9dfab59c00.json` (~6584 tok) +- `367e004bbee4bd3a32e05eaf817f2dae69e39266c36b1649e4d3929362e383da.json` (~930 tok) - `3702a2af17c73eed0470df46cecddc0b4c9a93aaab893ff5292d5c5b9b4ed512.json` (~9636 tok) +- `3714706b1961d4945195e8613ba7a5fcbf550c040e671d5860f2e8d747dc477c.json` (~6708 tok) - `37a543436eff088729eaa2a790c9ef9e58f59f226122e12aa2a0e6c38bc6f5a0.json` (~1870 tok) - `37cfdb1b9139384b7afd6b71a19606306bde221b63a2c4b3eb65ec5307855e45.json` (~17007 tok) - `37d60444e71080df18fa511b971b5c8d3c62ac5e8cf74527dcdab21a2ae69a76.json` (~561 tok) -- `38db881e9738aac568fc0a6738a7411f3c5775c45083d09a4598df0922b8a2fb.json` (~652 tok) +- `381ebaece388d903a5945752e2c6a1b923416e6e1f5373b13b903bc69639919a.json` (~6344 tok) +- `383a6850c6e5fd218700f04e4f3fa5a8d616ed90af9f0ce63cc9fbbd828ca42c.json` (~374 tok) +- `388dd55aa581a70dbce06969cd866d8f46cdfaa55cbe211302cf06acdc9fd64e.json` (~2999 tok) - `396e466ffa3cc396b083d8500b44a94b3d48f18866394c5d7cc8d1e2488bfc13.json` (~801 tok) - `3993a289fb925bac9ad356f94888ff857a44d592cf7d4c1815c3702493f24568.json` (~9385 tok) -- `39e862c842dfdb9cb947a20b0ed848e903b77dde54f68a2cc990c543cd8cc85f.json` (~592 tok) +- `3a08cad607debb49f322e478b38e10607c1af8bdda7e250c1c1a9600ee1eb05a.json` (~269 tok) - `3a1c0a4d790a5bb1e032f9fb3e075ae112f12ebdcf0d096e53d65eddb606120b.json` (~300 tok) - `3a5d7638d9686a803cdaf98d5696e7dce79ce8876e7dad5a4f4a9cd9b26f473b.json` (~14751 tok) +- `3a6a97b8b8e2a07a3727870d8d4cd2bf8e52b8c30bf792aca0ac507bc99df405.json` (~9546 tok) - `3b3d41a9d9e7bf1a198634ee72bea3a02b87747fa073455ea33e203dca82b5f8.json` (~3990 tok) - `3b69e25c707eab4010cdbda8e2318a464bea4be6a2afbd7a985f7ea7706e09d6.json` (~2171 tok) - `3bc0e6a7a00dd29f382b9c836dbb598929fd4e4edb1b450959d00c8e4712bd8e.json` (~636 tok) +- `3bccc4b1e70a15f6411c964ff222a7ec3485bd8599710bfc408b3420fa03469f.json` (~10023 tok) - `3c576da7137df22cc5fbd982fd231d9d40d374a77c7d618081ba7018f5eec36c.json` (~4654 tok) -- `3cbe30740569afd57d7747d83aba7902d3a10aaa98a8f88d2597dcd193c45d46.json` (~233 tok) - `3d0a807870ec7f6703eab25022ba70e6d12cca273ef820ea6e3bc8a31c97afa5.json` (~10333 tok) - `3d7e3a102cd4fc3bc27f599364f8f63ec75e6cb1ea3c47bcacc0e1261b485a15.json` (~3783 tok) - `3da9ee357a09d89a26df59bd0393606349c2d0849078091078924f60b5cdfd51.json` (~495 tok) @@ -579,35 +601,38 @@ - `3de5f25efab468345f714829408944025550d1056ba2b46fa3f3f29bcd5a9573.json` (~2258 tok) - `3e550bfbc558f9bcf5301570b291b375c3b2218478c6fee87d712bafb942dfe7.json` (~86150 tok) - `3ebb59da3bec8cfea2f9a426717e513a696eac7f6e08672e0a2d7f72ac811ba6.json` (~23800 tok) +- `3f262362589f503db1e19a46c668f044429f325be927582cda524ff546e3cd21.json` (~652 tok) +- `3f48feec3b4d3fb5184678ab68e9b1584d493ec8b175ad30122dc7058d8faaa5.json` (~81 tok) - `3f88bc2442422df4bf6036c3b2a1b3409779cbbeff5df7fb2c031bd14f973119.json` (~6809 tok) +- `3f8d83e0da73654fdad2d36d37d926e0fcf87f3fc645834c75938afed280aa20.json` (~81 tok) - `3f9cade10a0070913a41048bc2462ee7a4680399174c61c30c8d6e8a6e722cee.json` (~4780 tok) -- `4013563292e7090c61e46ca0c933fabaf9b9a07971cf0646b90a49f6b1275f15.json` (~81 tok) - `40138b45926e46347669dbe289bb2ea7597aedc0eb558bd0d39aacfef5df62e1.json` (~2333 tok) - `40a5802d6063d7dceabcd49010eac965e03377546e69b2839ce06e3bc19fb99b.json` (~3592 tok) -- `41604f3c06c391bc11c42a81865a06dd6258d153036bfb52660b25e466fa7a87.json` (~2943 tok) -- `4165831898bc7c34059f7c8c2f045ce3cfce69d38d58d2379acf6bebaa88c03f.json` (~8258 tok) +- `40a727896bbebc2f6e00afb878b8c666f7fdefa3f34fea7215de4ed042087b96.json` (~81 tok) +- `4109287f426902eeb3615a1912430eac8ca65558cc4ec26c781ea237d5460ef0.json` (~28018 tok) - `41bc353adb8853621ebd82fa735c038f98fe3a7e14d44a2eba6a50f01b4cc944.json` (~1511 tok) - `41e4d75d97d06c3f52e77585e5d114ccd5a9ac8e21767a2b7c74cc568dc7b5b0.json` (~298 tok) - `4244574967e1be57fe35ab1b68d8b31aa262d50a728d8877e7af61f9dec09cd5.json` (~526 tok) -- `429dfa6934ae3716422eadeec619fc76ba81797d0c569a2a218fd634eebb24e8.json` (~2824 tok) - `42fc26ddae39455e27588e5cd8b7a61961a4e5ce52a4e6c77fce30fd813ebe38.json` (~465 tok) -- `4300dce8311a1a28c47b63d12837f1dfa953f6f0f154d706705853a94d24865c.json` (~1201 tok) - `431735fb57e4e50e282f8cc57b09d553ef1b88f135996cfd253bfac5ff186bd3.json` (~2752 tok) - `43ba72a48acad7a07213454f6c1f78abccf9dd5ecf463d5863b83774dd99bf49.json` (~1010 tok) - `43cca59d292abf40819e07f2859c2dce727ca71a7334ce8384e9a1c50d922337.json` (~3604 tok) - `44245055f1f2aebb7db03f3ef508b06f9357cc6f5671308e8bfcaadf913972ad.json` (~2335 tok) +- `447da620ccec613360eb8b68fa18e1605d9265e6469f8ba3f9d05c36292db4be.json` (~652 tok) - `44d133b11103df5a5455f0e124056054065a43a4cf9bf6608a157d98a8247919.json` (~2414 tok) - `44eacc4abbec955dd4031ac82e902ced758e0fc319af2c17d2cc650d7c8701d3.json` (~648 tok) +- `4569060fc750bc7f993cb0eef888a341c4b9369a16cdfff680a749a2d8eb7679.json` (~14232 tok) - `45a3f01bceef855fff2282e1e2f5a96edaf6e49eb2dd6774de2c951190939476.json` (~876 tok) - `461865ad5d4eb698d9f8b0d0189052bd8733835f159fdec76ee474deb74fac89.json` (~466 tok) - `468a56306bfbb4b7f8f052404e6f4d239bde83c10b206520cb8e9fd6631647c4.json` (~1038 tok) -- `46ebcefa2f684f74b621bd247f64eb50458f8c96da528a7d72e77c0e28c31117.json` (~546 tok) -- `4794d6a5d735b81c534e62ac212d58a44382e110cd88d6a9fb354a2486b3adb7.json` (~2304 tok) +- `473aad72b667e57806070866e41e43c658d0c08d9422b2bab8641b027e29d2db.json` (~53573 tok) +- `4761c9bb00e54bf17b43a2ebb04ba21b531111c34e93a9dbc957756076e78bf8.json` (~25212 tok) - `480dbaa39314c68c3d4da3c1cb184846dc98ea3f672516afb1683ad674bc88cf.json` (~678 tok) - `4864f62b6a1943ef81d8e4f903830c54ca1451bf8656bbcacf0b603e9ca39af9.json` (~238 tok) - `48b009bc246cdc46da82cb6eede2d25cbfd574593e06dfcdfb4d29d67494094d.json` (~27795 tok) - `48e2a62af809b08d550c4c38d6312ef86dddbab71aa13f2b6058f83da4c364eb.json` (~1226 tok) - `48f612d06f7f9ac59bc69ee5cd647248f72809eb7cb81546a1d7fefc9a0c560d.json` (~16313 tok) +- `491112ef541e46590f478bd7be4a1558e96f76b393b4cdb60d70c4335aad80dd.json` (~15340 tok) - `495102d8b31e92493be8301b6d6af618f217d67f5d23eb73970ed03e4e6aae38.json` (~2679 tok) - `496de07d7e51cef1389ef84f25304ca54e6ed1d409b90c924d73cbf88230066d.json` (~49671 tok) - `498740b1cb5104685bf952a5bc3003a35ccb17d29fd01ab233424e7550a17bfe.json` (~41420 tok) @@ -617,13 +642,14 @@ - `4b1e8b3876352a607a2d7f8945f9770d648a45f3285f916ab81b29798e1038b9.json` (~5468 tok) - `4b648434f378dc77cabcf7ad726d40ab8a3ecd4bd96c25fe2b9b303713b85cfd.json` (~71366 tok) - `4bf3a6240c3116e762e6c911280d0bc5e14d68dbd7b82caf61f8695a11587e72.json` (~1277 tok) +- `4c776cf52b544a88c4c9a52d25dd1520c7a30cec47d7e0f08f0acb0eab7348cb.json` (~20902 tok) - `4c8845dd0c079a771cb7e6ff15708701de63a89516b79a1ec9a9cc9a2a5a4801.json` (~486 tok) - `4db3c1de6ac63ea0008c2a929d31af6a328b02f1f9ae4cca67aaf9e74d735e2b.json` (~2641 tok) -- `4dfeb0ee643bd08a965369c75b1b479464af4a6830ef2cd52a5ce4d91ac86ef7.json` (~15585 tok) - `4e034b866804145f6dc85a7dc7fefcd38219624f1b4ee747ee6a6ac1f40b2644.json` (~6371 tok) - `4e1c9fda5ab7f3f66527cd52ef0fcc20daea535347855acfe4086cd0f9dd10c8.json` (~1221 tok) - `4e81d1fe6f1cfc63de7e1a59695f9687deecf540b45fa7df24141de82335a8b6.json` (~3036 tok) - `4e90715ba4f6253b1220ccc8949da57080ace7973196ecb257522a26b6288952.json` (~626 tok) +- `4e96707af64de170915568f441d41a15fe7aded395b3024dd8d2c13d83fe38c8.json` (~10023 tok) - `4ee847c53c563a13117c4c6c9079895077ff7d18e951e8860530548e82f5f3e8.json` (~888 tok) - `4f1695f88fdc020f547c2cf509e08b8facfe69919b541b554c0aec76ec04537e.json` (~1432 tok) - `4f385d090c5ace09ef00e7883f4d965d6212218ea58df3f78088208fa47c7d56.json` (~8132 tok) @@ -632,7 +658,6 @@ - `51103bc49cc87487f9efa30dcffc5f7639f0cc6cbf8acc722f5da33fd0ce32e3.json` (~8308 tok) - `5175f1392d190fb958eb902f22aea50b3506648b739e0cb8a7bc42d389231e4b.json` (~4502 tok) - `51d646ce7e4c76a33d29d45d20149ff8825fe4339a842c2d000ed93d9e46f3de.json` (~12490 tok) -- `52329b7fe00b26b3d38673c5b43067199e0e951ca3d2aa9bc091c9372665f658.json` (~9914 tok) - `527377e036844617174eac5b08965176bb127edc5db3d5a77f362b034a4ec4fd.json` (~10319 tok) - `5286722a3fefdea557784ae42c65304cf227e92c44178fff83b9f2394c57b2e6.json` (~5453 tok) - `528bfbd499082064cc9b015a4092ffba50f77e09f2f90b5497e20aec9aba24fb.json` (~138 tok) @@ -643,77 +668,52 @@ - `53c97c6c796b513ab01170b56e6ace6f8725efa934165c6a84249fb0e278b687.json` (~4856 tok) - `53fb90c7ba54ed15ce868aec27ac9cf045a8b40311aa80dcd8c0c27619bb68c0.json` (~437 tok) - `541e0d5c8926ed331444c25f25649063cfd94a800e7a30c5ee42b98e5901e6cf.json` (~7036 tok) +- `546ac5f8e828ce834dbc0fd2e1af00b3970345404390f8e8de9d656977fb2761.json` (~2372 tok) - `547c8859d3083d2aa3df826d7d45b335b63abd89ecc10f67810628b642b26f28.json` (~32474 tok) - `54bf081b3ee033f99f334327084ec909aee9a0f93c01e9e7140a6da2e0e218b8.json` (~427 tok) - `5598204d609251195af1fd249f50dc13954de3767c7b1f6d3a5d7217f44ed180.json` (~1398 tok) +- `559977e3e74ccca3b7025f5db54827525173610f9ede460adf9f8bd7dd5f5d8f.json` (~60916 tok) - `55a658f23a6ae2ae434c1847a8ce4e67d6077bb1d86cfdae1854e86b332c214a.json` (~3305 tok) - `55e566c3da1711786075f53bd5b5cf55c8c67385305c4e056e5c536471387fae.json` (~3852 tok) -- `5613e6696da20b969a4f981c22bdb5d89934cd2430d8e17db0b0b7b2daaa3b9c.json` (~324 tok) +- `568a9d16b08a8d2a064b13e70b3a7b1759a7925428815a8b188d8d7258b92ed2.json` (~10763 tok) - `577e37e81e94ff718f63cc8d59fb255f51ad4090898b88e7996ad50eeceb3379.json` (~4531 tok) - `57d9e640d91abd2f583860f3d63b54c0da0d89622194e945122d238869af3579.json` (~2262 tok) - `581c504c4d3fe6ca908dcfdb5bd9507b73f9b821e96fb15d58356ab6f2d04ccd.json` (~2270 tok) +- `5939de23768f07fd1a2a227aecc4411f32191e003383cb526e4cafb702571cca.json` (~269 tok) - `594aedf253eb6263edd24ffc2d34c93a770f4bdc0b728515eb053324773d4ddb.json` (~8323 tok) -- `59b12ba54d87f01d227c6e5d5e22f9877adf011b33c33784ac448d13582e7ebc.json` (~1298 tok) +- `599bb763d7c10ec93e8b7401678e30afa6d94d11653c70c46f8c928191a9001e.json` (~592 tok) +- `59f075695e0135dde52ba377bdd55298ecb1357cb177b5438d56d4244ec167f1.json` (~2354 tok) - `59f7956e28ec4fc2d018f85746017fdcfa3b6a124bbe9e6b2eb4cfc7e77b9be2.json` (~11412 tok) - `5a1508eee3b01cfd7586af0f88973dfc8c51ef9bdb8d3d6fcf7e40a61d663b0f.json` (~362 tok) +- `5a4eca0415834ed8d9a9d84bdef3b95febd8011ac64b257757890b2d6246a1c4.json` (~81 tok) - `5a51f4134cc78b3e9b72124a8117a45e1d9a97d943172d29ba35c29fd844b12f.json` (~360 tok) -- `5a5414afbbb66fd1ffbab1337436f0a28b9a2ba43af5dd9a2093563f8e7ba524.json` (~31522 tok) -- `5a608396a5073fa3c87769b7fdf0905619e080337cf52300fa8e0c136cd3dd51.json` (~1122 tok) - `5a822398dceebb814bc41d8ffb801c51f89a129f9d090965877f7f67782a1b23.json` (~4032 tok) -- `5a8c04f9c75a98e2b3f33a9e4d45553abf2254fcb59e153a8e129abaf31779ac.json` (~8432 tok) - `5a92249e0d4f9dd34ff89497b4c540e6b84a672a455b0a5be5ca710b94e9eb79.json` (~4454 tok) - `5a974e6340597c0447c72b0d7bc977a668a8da2fac8c90c810e886c319e9bbd0.json` (~2118 tok) -- `5aa0816f3f76467d6c946a14d167de2deac14612a02702a7726c74c116f2d57a.json` (~81 tok) +- `5a99edc88f1f75983b12b9b8931c7ba5717424024e2ffb000e19564f2956175c.json` (~2393 tok) - `5b5887beb614d310bb85633443a1e29df0dec82a39650daecc39d529ffe5ce37.json` (~4806 tok) - `5b710141d6eb0f199d33b695898e32519aca05ea857921dd62bf508c7f954948.json` (~567 tok) - `5b791c1c235d96fe2e512c3d6fedd8f44c5ee256db33beb485935a3f6cc3d1c1.json` (~6930 tok) - `5c1cd225c770bed4fc03e72d8433eb17a2193588c47d5abeb26580098aab6506.json` (~77 tok) +- `5cc2f13594e5c0d36031a44dc0b88706f3e547d5e48720b55f02a68eb3b3149b.json` (~789 tok) - `5ce1db6b15faeeb658ef1fca18682532adca25278a2add781eb3c7caf4792b44.json` (~22459 tok) - `5cebb6a86cbc5a90d803973f1365c1673f3a11bd36cfc5178bdc985a03950378.json` (~8230 tok) - `5d7b2f244a9e7100f16139db782e80265d4724d5cb53b7c51fab1aeaaf1fb7b6.json` (~1313 tok) +- `5d996b732909e4428bb55522f4fdad491a98075c3aa256d7a816956bd42adac7.json` (~984 tok) - `5da97b26e48c2ea5c6df990bedce598cfd8052022102fa7a7e4c499c6f1374f5.json` (~1773 tok) -- `5db1ac6c80d157e99332fd95a25e232419940d0775d353068c2f832c2a895415.json` (~3371 tok) -- `5ddc7686f4df860858e56b0118a4363bdc97747240981c9477d50fae1e27a148.json` (~14128 tok) -- `5e38d7d54881ddb7dd7cb46a8b91ddf4ee555c899f5d08297ac4ee2f81f221f1.json` (~9256 tok) - `5e6d0ac84e99bdd6f6129fa521f73726ded1a47ff2e5d46a7c69026cec7522a5.json` (~591 tok) - `5eb698f53d93049c137820a52db3d1a7c451be085cea9d956a06b44671034787.json` (~1504 tok) -- `5ed29f8b6584e6bb033cd37fb6ccdf486254adda489f989641a3dfdf1c595cd4.json` (~726 tok) -- `5ede9a0001022535c762bd22ae452960e9664ea3487e3bcd04a9cc3368b9b819.json` (~2470 tok) -- `5f1dd49f629f4e9163fb4f2dc4ba3c810a01e6c6604835843560f1fab6303bd6.json` (~506 tok) -- `5f4bcb8abfd96052ce4500607dd3d9426acb458a5cba0a7a57f641f6b7d5a95f.json` (~801 tok) -- `5f522fbbd977c3b08acea207a56d99aed5069c1b882a186498c8f0cd0957493c.json` (~2972 tok) -- `6041d9e4749c93c1268d8ddce42d2eed66ab5b1dd739ef9fd28bee04f745bb48.json` (~3610 tok) -- `612fc1d644edb92da41984c14d161ee8e5d244f317abd3f2d305ab9a63884372.json` (~427 tok) -- `6138099a827202f5f984212ac5a92cdd0181489a14a0f341b27169d40695fa9b.json` (~12476 tok) -- `616ea854efce5a7e95c1739d6400bb48fd63c7cee591a710c66a2bcc9e70fd20.json` (~4511 tok) -- `6180aec3d86b1cb2980831e6c75a8ac6216fd14493f52a1539ad73a750bd10b7.json` (~5053 tok) -- `61f1333461274a206f965221fa31fd7ce681795ce2c4271c9455759288e83655.json` (~4360 tok) -- `62258c399f9068c677ede2af396f4c73d2fc148afdd992ba9ae0abcb0f9ecca2.json` (~16144 tok) -- `6240ba5b339e0ad010ff54e9a18d5176b6705d98b0efccbbdc10cc4efeccd398.json` (~754 tok) -- `631bad264e70cf18845630209d5314d2ef0628b282ed1517a7e68948daa0d5a7.json` (~12779 tok) -- `63df40cb2722f34ee3e80321f4e6409b15d990890509e6e22b87d7223e12eca1.json` (~269 tok) -- `643b384648c934f3476ad78c6585f49eaa86eda84b85db9b51cf91efcc38fadb.json` (~206 tok) -- `64438620e82af97eccf2a74fbf5787a3dffc3966d5fdd9cbeb1e4bbd7aba8581.json` (~722 tok) -- `648aeeeca2a5fcfc56d838e99feadcaca9949e3624281ae7409e63793a20f958.json` (~1991 tok) -- `649c5319469f296cf2b83dff09ac6a441f1c9249d31c411626adb3f1846adc73.json` (~7610 tok) -- `650b49cb494a9178f319380defc9821e6a644c1117916ce234b0de30704aaa38.json` (~17425 tok) -- `652242aa9502713f15cc5143b7729537a244823c44623cc85eec4cb511544721.json` (~3996 tok) -- `65239c90331dde539ddf43e8f9cfb61cbe0f704d0c49e6a90068d3fb7cb88285.json` (~7661 tok) -- `665f014bd63eba7c449d95b8bae93c518b84ca037127a4ee05f7097dc26777f2.json` (~12661 tok) -- `6664f5dc160a6acfc03af4d9881c53f8f6c650ac384b1ad9b3db47d896800af9.json` (~656 tok) -- `669931b38fdf0060e974d68dd28abc43e49a1907405848ce9d8b3b3901518313.json` (~376 tok) -- `66cc2d1ad85554725cecb1f215d375511474a58829464647a0d8c109818d9804.json` (~57 tok) ## src/controller/ -- `commit.c` — Commit engine core (ha-commit-engine 1.2): per-LSN majority evaluation (descending watermark median arr[n_peers/2-1], catch-up excluded), monotonic clamped committed_lsn advancement, RETAIN submit immediate; WAIT = -ENOSYS until task 2.2 (~2600 tok) -- `meson.build` (~1094 tok) - -## src/controller/include/ - -- `commit.h` — commit engine service interface (umbrella-verbatim): commit_engine_t, WAIT/RETAIN modes, init/fini/submit/on_ack/set_writable_lsn/on_membership_change (~771 tok) +- `meson.build` (~1109 tok) +- `persist.c` — define _GNU_SOURCE (~13786 tok) ## src/controller/testsuite/ -- `controller_test.c` — include (~1323 tok) -- `test_commit_engine.c` — Commit engine tests: tasks 1.x majority/commit-index (green) + task 2.1 WAIT blocking/admission-cap/own-term-gate RED cases (~13200 tok) +- `controller_test.c` — include (~1346 tok) +- `test_commit_barrier_rollout.c` — RED tests for ha-commit-barrier-rollout 1.1: wal_classify totality (verified table), unknown-type fail-fast via fork+SIGABRT, fact-hook RETAIN routing (~4884 tok) + +## src/include/ + +- `persist.h` — pragma once (~2129 tok) diff --git a/.wolf/cerebrum.md b/.wolf/cerebrum.md index 3f52cd8a..f813a2e4 100644 --- a/.wolf/cerebrum.md +++ b/.wolf/cerebrum.md @@ -78,4 +78,6 @@ - (2026-06-11) Scheduler-bound job egress has TWO seams, not one: full push `xcesched_push_jobs` (sched.c:169, on scheduler join) AND incremental JOB_NEW push `sched_job_new_fun`→`xcesched_push_joblist` (sched.c:236-248). Status reads (apiserver.c:83, opget.c:173) share `collect_jobs` (dataex.c:294) and must stay unfiltered per umbrella deviation. - (2026-06-11) No saved-argv/execv machinery exists anywhere in src/controller or src/lib — demotion self-restart is net-new; `job_t` is a thin wrapper `{ xce_job_t job; }` at src/include/xce/types.h:483-485 (controller-local fields go on the wrapper, wire type untouched). +- commit_submit(COMMIT_MODE_RETAIN) is REGISTRATION-FREE (commit.c:233-239, verified 2026-06-11): returns 0 immediately, records NOTHING per entry — commit state derives entirely from registry watermarks at ACK time. RETAIN routing at the hook seam therefore has NO engine-side observable distinct from the append itself; tests of RETAIN routing must assert classification + non-blocking return + lsn > committed_lsn pending + ACK-driven advancement, never a fake "registered" counter. (ha-commit-barrier-rollout 1.1) +- XCE_TESTING fail-fast (assert/abort) paths are testable via fork(): child silences stderr, calls the guarded fn, _exit(0); parent asserts WIFSIGNALED && WTERMSIG == SIGABRT. Precedent: lib testsuite test.c:1783; ASAN-compatible with detect_leaks=0. (ha-commit-barrier-rollout 1.1) - ha_has_write_quorum (ha.c:1507) is STATIC — testsuite reaches it only through ha_dispatch_gate(ha, opcode) (extern-declared in test files, pattern: test_ha_repl_ack.c). Gate fixtures: ha_init + n_peers + atomic role store + heap zarray standby_endpoints (test_ha_connection_reuse.c pattern). Catch-up exclusion at the gate is untestable until the ha_state_t.repl back-pointer lands (ha-commit-engine 4.2) — assert via repl_active_standby_count instead. (2026-06-11, task 4.1) diff --git a/.wolf/memory.md b/.wolf/memory.md index 172574cc..04313e91 100644 --- a/.wolf/memory.md +++ b/.wolf/memory.md @@ -964,3 +964,14 @@ | 03:45 | kiro-validate-impl ha-commit-engine: GO — 433+99 tests 0 FAIL (bug-411-aware count), cross-task wiring/lock-order/boundary clean, 1 LOW drift (commit.h includes persist.h vs fwd-decl) | .kiro/specs/ha-commit-engine/, src/controller/{commit,repl,ha,cluster,persist}.c | validated | ~45k | | 01:15 | /kiro-impl ha-commit-engine COMPLETE: 12/12 tasks, 10 commits (d5a0f9c..95a10ce), validate-impl GO, suite 467/0 | commit.c+wiring | sub-spec 1 of 4 done | ~1.5M | | 20:41 | Edited .kiro/steering/roadmap.md | inline fix | ~77 | +| 20:42 | Session end: 186 writes across 30 files (brief.md, project_ha_consistency_invariants.md, product.md, spec.json, requirements.md) | 92 reads | ~358650 tok | +| 20:51 | Created src/controller/testsuite/test_commit_barrier_rollout.c | — | ~4879 | +| 20:51 | Edited src/controller/testsuite/test_commit_barrier_rollout.c | 3→4 lines | ~21 | +| 20:52 | Edited src/include/persist.h | modified enum() | ~239 | +| 20:52 | Edited src/controller/persist.c | modified wal_classify() | ~187 | +| 20:52 | Edited src/controller/meson.build | 2→3 lines | ~26 | +| 20:53 | Edited src/controller/testsuite/controller_test.c | modified setUp() | ~31 | +| 20:53 | Edited src/controller/testsuite/controller_test.c | 3→4 lines | ~25 | +| 21:05 | Task 1.1 ha-commit-barrier-rollout RED complete: build green, 8/8 new cases FAIL on assertions, 0 pre-existing failures (901 PASS) | test_commit_barrier_rollout.c, persist.h, persist.c, meson.build, controller_test.c | RED verified | ~60k | +| 21:12 | Edited .kiro/specs/ha-commit-barrier-rollout/tasks.md | inline fix | ~21 | +| 21:13 | Edited .kiro/specs/ha-commit-barrier-rollout/tasks.md | added error handling | ~350 | diff --git a/src/controller/meson.build b/src/controller/meson.build index 6dc2b69f..8c6d9224 100644 --- a/src/controller/meson.build +++ b/src/controller/meson.build @@ -121,6 +121,7 @@ controller_unit_test_src = [ 'testsuite/test_ha_rdhup_integration.c', 'testsuite/test_ha_liveness.c', 'testsuite/test_commit_engine.c', + 'testsuite/test_commit_barrier_rollout.c', 'commit.c', 'startup_validation.c', 'worker.c', diff --git a/src/controller/persist.c b/src/controller/persist.c index e57c1bb5..6416a3ff 100644 --- a/src/controller/persist.c +++ b/src/controller/persist.c @@ -1118,6 +1118,18 @@ static uint32_t detail_to_wal_type(int detail) } } +/* Write-path commit classification — INERT RED-phase stub + * (ha-commit-barrier-rollout task 1.1; sanctioned scaffolding so the RED + * tests fail on assertions, not at link time). Returns an out-of-enum + * sentinel: classifies nothing yet. The verified classification table + * (design.md §Write-Path Classification) is implemented in task 1.2. */ +wal_commit_mode_t wal_classify(uint32_t wal_type, int detail) +{ + (void)wal_type; + (void)detail; + return (wal_commit_mode_t)-1; /* invalid sentinel — task 1.2 replaces */ +} + /* P0 payload: just the detail value (4 bytes). * Full Cap'n Proto serialization is wired in Task 15. */ diff --git a/src/controller/testsuite/controller_test.c b/src/controller/testsuite/controller_test.c index bf8bb1b6..64df06fd 100644 --- a/src/controller/testsuite/controller_test.c +++ b/src/controller/testsuite/controller_test.c @@ -51,6 +51,7 @@ extern void test_ha_connection_reuse(void); extern void test_ha_rdhup_integration(void); extern void test_ha_liveness(void); extern void test_commit_engine(void); +extern void test_commit_barrier_rollout(void); void setUp(void) { return; } @@ -171,6 +172,7 @@ int main(int argc, char* argv[]) TEST(test_ha_rdhup_integration); TEST(test_ha_liveness); TEST(test_commit_engine); + TEST(test_commit_barrier_rollout); return UNITY_END(); } diff --git a/src/controller/testsuite/test_commit_barrier_rollout.c b/src/controller/testsuite/test_commit_barrier_rollout.c new file mode 100644 index 00000000..9c6c4ebe --- /dev/null +++ b/src/controller/testsuite/test_commit_barrier_rollout.c @@ -0,0 +1,426 @@ +/* + * test_commit_barrier_rollout.c — RED-phase tests for write-path commit + * classification (ha-commit-barrier-rollout task 1.1). + * + * Covers Requirements 2.1 (every state-changing WAL type explicitly + * classified WAIT/RETAIN/LOCAL), 2.2 (unclassified type fails fast under + * XCE_TESTING), and 3.1 (fact-shaped appends are retained — appended, + * non-blocking, pending purely by lsn > committed_lsn, committed later + * by covering ACKs without redelivery). + * + * The classification table under test is the VERIFIED table from + * .kiro/specs/ha-commit-barrier-rollout/design.md §Write-Path + * Classification (it supersedes the umbrella draft table — research.md §2 + * records the call-site-verified deviations: WAL_JOB_STOP/RESUME are + * fact-origin only; WAL_JOB_UPDATE/WAL_JOB_FAIL are dual-origin and + * classify RETAIN at the seam, with the request-shaped guarantee realized + * by the handler barrier in do_jobcontrol_op — tasks 2.x/3.x): + * + * WAL_JOB_SUBMIT request WAIT + * WAL_QUEUE_CTRL request WAIT + * WAL_WORKER_CTRL + JOIN/UPDATE request WAIT + * WAL_WORKER_CTRL + DEAD fact RETAIN + * WAL_JOB_START/FINISH/FAIL/STOP/ + * RESUME/UPDATE/RUSAGE/CKPT/ + * PREASON fact RETAIN + * WAL_CHECKPOINT local LOCAL (never replicated) + * unknown — XCE_TESTING assert + * + * These cases FAIL (RED) against the task-1.1 inert wal_classify stub in + * persist.c (returns an invalid sentinel mode) and go green with the + * task-1.2 implementation. + * + * Honest-observable note for the RETAIN-registration case (task brief): + * commit_submit(COMMIT_MODE_RETAIN) is registration-free bookkeeping + * (commit.c — returns 0 immediately; commit state is derived entirely + * from registry watermarks at ACK time), so RETAIN routing adds NO + * engine-side observable distinct from the append itself. The fact-hook + * test therefore asserts the full observable retention contract — + * classified RETAIN (the RED carrier), hook returns only the local-append + * rc without blocking, entry in the WAL with lsn > committed_lsn (pending, + * never confirmed early), and committed_lsn advancing over the entry once + * a covering ACK arrives (no redelivery) — rather than inventing a fake + * registration observable. + * + * Fixture patterns from test_commit_engine.c (stub persist_t + replicator + * with dummy endpoints; real tmpdir persist_t for the hook-driven append; + * repl_broadcast_send_hook to drop broadcasts) and test_persist_hooks.c + * (variadic trampoline driving persist_job_hook_fun with a real va_list). + * + * Requirements: 2.1, 2.2, 3.1 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xce/cluster_def.h" /* HOOK_DETAIL_*, XCE_CONTROLLER_HOOK_JOB */ +#include "xce/types.h" /* job_t */ + +#include "commit.h" +#include "hook.h" /* HOOK_AFTER_EVENT */ +#include "persist.h" /* wal_commit_mode_t, wal_classify (task 1.1 decls) */ +#include "repl.h" +#include "typemap.h" /* wal_catchup_entry_t (send hook signature) */ +#include "wal_types.h" + +/* Test-injectable send hook (defined in repl.c under XCE_TESTING) — keeps + * repl_broadcast from dereferencing the dummy endpoints. */ +extern int (*repl_broadcast_send_hook)(void *ep, + wal_catchup_entry_t **entries, + int n_entries); + +static int cbr_drop_send_hook(void *ep, wal_catchup_entry_t **entries, + int n_entries) +{ + (void)ep; + (void)entries; + (void)n_entries; + return 0; /* pretend success, drop the data */ +} + +/* ── Shared helpers ──────────────────────────────────────────────────────── */ + +static uint64_t mono_ms(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u; +} + +/* Valid-mode predicate for the totality sweep (Req 2.1). */ +static int is_valid_mode(wal_commit_mode_t m) +{ + return m == WAL_MODE_WAIT || m == WAL_MODE_RETAIN || m == WAL_MODE_LOCAL; +} + +/* ── Classification table cases (Req 2.1) ───────────────────────────────── + * All RED against the inert stub: it returns an out-of-enum sentinel, so + * every equality below fails on assertion, not linking. */ + +static void test_classify_job_submit_is_wait(void) +{ + /* Arrange / Act */ + wal_commit_mode_t mode = wal_classify(WAL_JOB_SUBMIT, HOOK_DETAIL_JOB_NEW); + + /* Assert: client job submit is request-shaped — commit barrier before + * the requester reply (reference impl keeps the in-hook sync path) */ + TEST_ASSERT_EQUAL_INT_MESSAGE(WAL_MODE_WAIT, mode, + "WAL_JOB_SUBMIT must classify WAIT (request-shaped)"); + + printf("test_classify_job_submit_is_wait: PASS\n"); +} + +static void test_classify_queue_ctrl_is_wait(void) +{ + /* Arrange / Act */ + wal_commit_mode_t mode = wal_classify(WAL_QUEUE_CTRL, + HOOK_DETAIL_QUEUE_UPDATE); + + /* Assert: queue control is client-request-shaped */ + TEST_ASSERT_EQUAL_INT_MESSAGE(WAL_MODE_WAIT, mode, + "WAL_QUEUE_CTRL must classify WAIT (request-shaped)"); + + printf("test_classify_queue_ctrl_is_wait: PASS\n"); +} + +static void test_classify_worker_ctrl_detail_split(void) +{ + /* Arrange / Act: WAL_WORKER_CTRL is the only detail-disambiguated type */ + wal_commit_mode_t join_mode = + wal_classify(WAL_WORKER_CTRL, HOOK_DETAIL_WORKER_JOIN); + wal_commit_mode_t update_mode = + wal_classify(WAL_WORKER_CTRL, HOOK_DETAIL_WORKER_UPDATE); + wal_commit_mode_t dead_mode = + wal_classify(WAL_WORKER_CTRL, HOOK_DETAIL_WORKER_DEAD); + + /* Assert: join/update have a requester awaiting confirmation (WAIT); + * worker death is controller-detected — a reported fact (RETAIN) */ + TEST_ASSERT_EQUAL_INT_MESSAGE(WAL_MODE_WAIT, join_mode, + "WAL_WORKER_CTRL + WORKER_JOIN must classify WAIT"); + TEST_ASSERT_EQUAL_INT_MESSAGE(WAL_MODE_WAIT, update_mode, + "WAL_WORKER_CTRL + WORKER_UPDATE must classify WAIT"); + TEST_ASSERT_EQUAL_INT_MESSAGE(WAL_MODE_RETAIN, dead_mode, + "WAL_WORKER_CTRL + WORKER_DEAD must classify RETAIN (fact)"); + + printf("test_classify_worker_ctrl_detail_split: PASS\n"); +} + +static void test_classify_all_job_state_types_are_retain(void) +{ + /* Arrange: every WAL_JOB_* state-transition type with its natural hook + * detail (verified table: all fact-shaped — STOP/RESUME are fact-origin + * only; FAIL/UPDATE dual-origin, request side covered by the handler + * barrier in later tasks) */ + static const struct { + uint32_t wal_type; + int detail; + const char *name; + } facts[] = { + { WAL_JOB_START, HOOK_DETAIL_JOB_START, "WAL_JOB_START" }, + { WAL_JOB_FINISH, HOOK_DETAIL_JOB_EXIT, "WAL_JOB_FINISH" }, + { WAL_JOB_FAIL, HOOK_DETAIL_JOB_FAIL, "WAL_JOB_FAIL" }, + { WAL_JOB_STOP, HOOK_DETAIL_JOB_STOP, "WAL_JOB_STOP" }, + { WAL_JOB_RESUME, HOOK_DETAIL_JOB_RESUME, "WAL_JOB_RESUME" }, + { WAL_JOB_UPDATE, HOOK_DETAIL_JOB_UPDATE, "WAL_JOB_UPDATE" }, + { WAL_JOB_RUSAGE, HOOK_DETAIL_JOB_RUSAGE_UPDATE, "WAL_JOB_RUSAGE" }, + { WAL_JOB_CKPT, HOOK_DETAIL_JOB_CKPT, "WAL_JOB_CKPT" }, + { WAL_JOB_PREASON, HOOK_DETAIL_SETPREASON, "WAL_JOB_PREASON" }, + }; + size_t i; + + /* Act + Assert: each is a reported fact — retained until committed */ + for (i = 0; i < sizeof(facts) / sizeof(facts[0]); i++) { + char msg[96]; + + snprintf(msg, sizeof(msg), "%s must classify RETAIN (fact-shaped)", + facts[i].name); + TEST_ASSERT_EQUAL_INT_MESSAGE( + WAL_MODE_RETAIN, wal_classify(facts[i].wal_type, facts[i].detail), + msg); + } + + printf("test_classify_all_job_state_types_are_retain: PASS\n"); +} + +static void test_classify_checkpoint_is_local(void) +{ + /* Arrange / Act: WAL_CHECKPOINT is a local WAL marker, never replicated */ + wal_commit_mode_t mode = wal_classify(WAL_CHECKPOINT, 0); + + /* Assert */ + TEST_ASSERT_EQUAL_INT_MESSAGE(WAL_MODE_LOCAL, mode, + "WAL_CHECKPOINT must classify LOCAL (never replicated)"); + + printf("test_classify_checkpoint_is_local: PASS\n"); +} + +static void test_classify_is_total_over_wal_entry_type(void) +{ + /* Arrange: EVERY value of wal_entry_type (wal_types.h:44-58, verified + * 2026-06-11: 13 values, no WAL_NOOP yet). When a new type is added, + * this sweep plus the unknown-type guard force it to be classified. */ + static const struct { + uint32_t wal_type; + int detail; + const char *name; + } all_types[] = { + { WAL_JOB_SUBMIT, HOOK_DETAIL_JOB_NEW, "WAL_JOB_SUBMIT" }, + { WAL_JOB_START, HOOK_DETAIL_JOB_START, "WAL_JOB_START" }, + { WAL_JOB_FINISH, HOOK_DETAIL_JOB_EXIT, "WAL_JOB_FINISH" }, + { WAL_JOB_FAIL, HOOK_DETAIL_JOB_FAIL, "WAL_JOB_FAIL" }, + { WAL_JOB_STOP, HOOK_DETAIL_JOB_STOP, "WAL_JOB_STOP" }, + { WAL_JOB_RESUME, HOOK_DETAIL_JOB_RESUME, "WAL_JOB_RESUME" }, + { WAL_JOB_UPDATE, HOOK_DETAIL_JOB_UPDATE, "WAL_JOB_UPDATE" }, + { WAL_JOB_RUSAGE, HOOK_DETAIL_JOB_RUSAGE_UPDATE, "WAL_JOB_RUSAGE" }, + { WAL_JOB_CKPT, HOOK_DETAIL_JOB_CKPT, "WAL_JOB_CKPT" }, + { WAL_JOB_PREASON, HOOK_DETAIL_CLRPREASON, "WAL_JOB_PREASON" }, + { WAL_QUEUE_CTRL, HOOK_DETAIL_QUEUE_UPDATE, "WAL_QUEUE_CTRL" }, + { WAL_WORKER_CTRL, HOOK_DETAIL_WORKER_DEAD, "WAL_WORKER_CTRL" }, + { WAL_CHECKPOINT, 0, "WAL_CHECKPOINT" }, + }; + size_t i; + + /* Act + Assert: classification is total — every enum value maps to a + * valid mode (no silent barrier bypass for any state-changing type) */ + for (i = 0; i < sizeof(all_types) / sizeof(all_types[0]); i++) { + char msg[96]; + + snprintf(msg, sizeof(msg), + "%s must map to a valid commit mode (totality)", + all_types[i].name); + TEST_ASSERT_TRUE_MESSAGE( + is_valid_mode(wal_classify(all_types[i].wal_type, + all_types[i].detail)), + msg); + } + + printf("test_classify_is_total_over_wal_entry_type: PASS\n"); +} + +/* ── Unknown type fails fast under XCE_TESTING (Req 2.2) ────────────────── + * The guard contract is assert(0) in testing builds (abort), production + * ERROR + RETAIN fallback. The abort is observed from a forked child so + * the test runner survives (fork pattern precedent: lib testsuite test.c). + * RED: the inert stub returns its sentinel without asserting, so the child + * exits normally and the SIGABRT assertion fails. */ +static void test_classify_unknown_type_fails_fast_in_testing(void) +{ + pid_t pid; + int status = 0; + + /* Arrange: a WAL type far outside wal_entry_type */ + const uint32_t unknown_type = 777u; + + /* Act: evaluate the unknown type in a child process */ + pid = fork(); + TEST_ASSERT_TRUE_MESSAGE(pid >= 0, "fork must succeed"); + if (pid == 0) { + /* Child: silence the expected assert/abort noise, then classify. + * Reaching _exit(0) means the fail-fast path did NOT trigger. */ + int devnull = open("/dev/null", O_WRONLY); + if (devnull >= 0) { + dup2(devnull, STDERR_FILENO); + dup2(devnull, STDOUT_FILENO); + } + (void)wal_classify(unknown_type, 0); + _exit(0); + } + + /* Assert: the child must die on the XCE_TESTING fail-fast path */ + TEST_ASSERT_EQUAL_INT_MESSAGE(pid, waitpid(pid, &status, 0), + "waitpid must reap the classifier child"); + TEST_ASSERT_TRUE_MESSAGE( + WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT, + "unknown WAL type must trip the XCE_TESTING fail-fast (abort)"); + + printf("test_classify_unknown_type_fails_fast_in_testing: PASS\n"); +} + +/* ── Fact-type append through the hook seam is RETAIN-routed (Req 3.1) ──── + * Drives the production persist_job_hook_fun with a worker-reported job + * finish (fact) against a real tmpdir persist_t with a wired commit engine + * (n_peers=3) and ACKs withheld. Observable retention contract: + * - wal_classify routes the type RETAIN (RED carrier — fails against the + * inert stub; the behavioral assertions below pass on unmodified + * persist.c and pin the contract task 1.2 must preserve) + * - the hook returns the local-append rc (0) WITHOUT blocking on commit + * progress (no timeout applies to facts) + * - the entry is in the WAL and pending purely by lsn > committed_lsn + * (never confirmed early) + * - a later covering ACK advances committed_lsn over the entry with no + * redelivery of the fact + * commit_submit(RETAIN) itself is registration-free (commit.c) — see the + * file header note; there is deliberately no fake "registered" observable. */ + +/* Variadic trampoline so the test passes typed args through a real va_list + * (pattern from test_persist_hooks.c). */ +static int call_job_hook(int type, int sit, int detail, persist_t *p, ...) +{ + va_list ap; + int rc; + + va_start(ap, p); + rc = persist_job_hook_fun(type, sit, detail, p, ap); + va_end(ap); + return rc; +} + +/* Statics survive Unity's longjmp on assertion failure. */ +static persist_t *g_fp; +static commit_engine_t g_fce; +static job_t g_fact_job; +static int g_fep_a, g_fep_b; /* dummy endpoints — replicator compares pointers */ + +static void test_fact_hook_append_is_retained_and_nonblocking(void) +{ + persist_config_t cfg; + char tmpdir[256]; + uint64_t t0, elapsed_ms, fact_lsn; + int rc; + + /* Arrange: real persist over a private tmpdir; sync timeout large so a + * misrouted blocking wait is detectable as elapsed time */ + cfg = persist_config_defaults(); + cfg.wal_sync_ms = 5000; + cfg.sync_timeout_ms = 3000; + + snprintf(tmpdir, sizeof(tmpdir), "/tmp/xce_cbr11_fact_XXXXXX"); + TEST_ASSERT_NOT_NULL(mkdtemp(tmpdir)); + + g_fp = calloc(1, sizeof(persist_t)); + TEST_ASSERT_NOT_NULL(g_fp); + persist_set_workdir(g_fp, tmpdir, "ctrl"); + TEST_ASSERT_EQUAL_INT(0, persist_init(g_fp, &cfg, NULL)); + TEST_ASSERT_EQUAL_INT(0, persist_start(g_fp)); + + /* 3-member cluster: 2 standbys registered, engine wired, ACKs withheld */ + TEST_ASSERT_TRUE(repl_add_standby(&g_fp->repl, &g_fep_a, "sb_a", 7200) >= 0); + TEST_ASSERT_TRUE(repl_add_standby(&g_fp->repl, &g_fep_b, "sb_b", 7201) >= 0); + TEST_ASSERT_EQUAL_INT(0, commit_engine_init(&g_fce, g_fp, 3, 8)); + repl_set_commit_engine(&g_fp->repl, &g_fce); + repl_broadcast_send_hook = cbr_drop_send_hook; + + /* Priming async append so the fact entry lands at LSN 1 (the engine + * rejects lsn == 0 — accepted interim LSN-0 edge, ha-commit-engine) */ + TEST_ASSERT_EQUAL_INT(0, persist_wal_async(g_fp, WAL_JOB_UPDATE, "p", 1)); + fact_lsn = g_fp->current_lsn; /* the LSN the fact append will get */ + TEST_ASSERT_EQUAL_UINT64(1, fact_lsn); + + memset(&g_fact_job, 0, sizeof(g_fact_job)); + g_fact_job.job.jobid = 42; + g_fact_job.job.state = 0x10; + + /* Act: worker-reported job finish (fact) through the production hook. + * JOB_EXIT AFTER_EVENT args: cluster_t*, xce_jobfinish_t*, job_t* */ + t0 = mono_ms(); + rc = call_job_hook(XCE_CONTROLLER_HOOK_JOB, HOOK_AFTER_EVENT, + HOOK_DETAIL_JOB_EXIT, g_fp, + (void *)NULL, (void *)NULL, &g_fact_job); + elapsed_ms = mono_ms() - t0; + + /* Assert: the seam classifies the fact RETAIN (RED carrier — the routing + * contract the behavioral assertions below depend on) */ + TEST_ASSERT_EQUAL_INT_MESSAGE( + WAL_MODE_RETAIN, wal_classify(WAL_JOB_FINISH, HOOK_DETAIL_JOB_EXIT), + "fact-type WAL_JOB_FINISH must be RETAIN-routed at the hook seam"); + + /* ...the hook propagated only the local-append rc, without blocking on + * commit progress (facts have no commit timeout) */ + TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, + "fact hook must return the local-append rc only (no commit error)"); + TEST_ASSERT_TRUE_MESSAGE(elapsed_ms < 1000, + "fact append must not block on commit progress (no timeout applies)"); + + /* ...the entry is in the WAL, pending purely by lsn > committed_lsn */ + TEST_ASSERT_EQUAL_UINT64_MESSAGE(fact_lsn + 1, g_fp->current_lsn, + "the fact entry must be appended to the WAL"); + TEST_ASSERT_TRUE_MESSAGE( + atomic_load(&g_fp->committed_lsn) < fact_lsn, + "the uncommitted fact must be pending (lsn > committed_lsn), " + "never confirmed early"); + + /* ...and a covering ACK later commits the retained entry — engine + * accounting advances committed_lsn over it, no redelivery needed */ + repl_on_ack(&g_fp->repl, &g_fep_a, fact_lsn); + TEST_ASSERT_EQUAL_UINT64_MESSAGE( + fact_lsn, atomic_load(&g_fp->committed_lsn), + "a covering ACK must advance committed_lsn over the retained fact"); + + /* Teardown */ + repl_broadcast_send_hook = NULL; + commit_engine_fini(&g_fce); + persist_stop(g_fp); + persist_fini(g_fp); + free(g_fp); + g_fp = NULL; + + printf("test_fact_hook_append_is_retained_and_nonblocking: PASS\n"); +} + +/* ── Test group entry point ──────────────────────────────────────────────── */ + +void test_commit_barrier_rollout(void) +{ + UNITY_BEGIN(); + /* Task 1.1 — classification totality + RETAIN routing (Req 2.1, 2.2, 3.1) */ + RUN_TEST(test_classify_job_submit_is_wait); + RUN_TEST(test_classify_queue_ctrl_is_wait); + RUN_TEST(test_classify_worker_ctrl_detail_split); + RUN_TEST(test_classify_all_job_state_types_are_retain); + RUN_TEST(test_classify_checkpoint_is_local); + RUN_TEST(test_classify_is_total_over_wal_entry_type); + RUN_TEST(test_classify_unknown_type_fails_fast_in_testing); + RUN_TEST(test_fact_hook_append_is_retained_and_nonblocking); + UNITY_END(); +} diff --git a/src/include/persist.h b/src/include/persist.h index d23d0ec6..14206230 100644 --- a/src/include/persist.h +++ b/src/include/persist.h @@ -116,6 +116,21 @@ int persist_wal_sync_append(persist_t* p, uint32_t type, const void* payload, uint32_t len, int* sync_rc_out); +/* ============================================================ + * Write-path commit classification (ha-commit-barrier-rollout) + * ============================================================ */ +typedef enum { + WAL_MODE_WAIT, /* request-shaped: barrier before requester reply */ + WAL_MODE_RETAIN, /* fact-shaped: retained until committed, no timeout */ + WAL_MODE_LOCAL, /* not replicated (WAL_CHECKPOINT) */ +} wal_commit_mode_t; + +/* Pure classification lookup. detail disambiguates WAL_WORKER_CTRL only + * (HOOK_DETAIL_WORKER_JOIN/UPDATE -> WAIT, HOOK_DETAIL_WORKER_DEAD -> + * RETAIN). Unknown type: XCE_TESTING assert (fail fast); production logs + * ERROR and returns WAL_MODE_RETAIN (never dropped, never confirmed early). */ +wal_commit_mode_t wal_classify(uint32_t wal_type, int detail); + /* Force WAL fdatasync (for tests and clean shutdown) */ int persist_flush(persist_t* p);