{"id":"2dd8a269-d886-41b1-a7a6-0b806435d37b","shortId":"xE6HXq","kind":"skill","title":"write-tests","tagline":">-","description":"!../principles/SKILL_BODY.md\n\n!../tool-gitnexus/SKILL_BODY.md\n\n---\n\n# Test Writing Workflow\n\n## Core Principle\n\nThe value of tests is not \"line coverage,\" but **accurately reporting regressions when code changes**. Good tests are a safety net for future refactoring — they go red only when behavior is truly broken, not when unrelated changes are made.\n\n---\n\n## Entering from a Bug Fix\n\nIf this test session was triggered by a bug fix, apply the following adjustments before entering the standard workflow:\n\n**1. Write the reproduction test first — before anything else.**\n\nThe root cause and trigger conditions are already known from the bug fix. Use that analysis directly:\n\n```python\ndef test_<bug_description>():\n    # This test MUST FAIL before the fix is applied.\n    # It documents the exact condition that triggered the bug.\n    result = <call using the trigger conditions from the bug fix>\n    assert <the correct behavior that was previously broken>\n```\n\n**2. Skip Step 1 (code analysis) for the fixed function.** Jump directly to Step 2, using the impact assessment from the bug fix as input for \"what callers depend on.\"\n\n**3. Prioritize boundary tests adjacent to the bug.** Focus on the boundary conditions the bug exposed — null, zero, extreme values, race conditions.\n\n**4. Name the reproduction test clearly:**\n\n```python\ndef test_calculate_discount_zero_rate_does_not_divide_by_zero():\n    # fix: rate=0 caused ZeroDivisionError (see bug fix root cause)\n    assert calculate_discount(price=100, rate=0) == 100\n```\n\nOnce the reproduction test is in place and passing, skip to Step 2 for any additional coverage.\n\n**If the reproduction test still fails after the fix was applied:** stop the test-writing workflow. Report to the user: \"The reproduction test is still failing — the fix may be incomplete. Please re-trigger with `sextant:fix-bug` to re-evaluate the root cause, or describe the remaining failure and I'll investigate with baseline rules.\" Do not proceed to Step 2.\n\n---\n\n## Before You Start\n\n### Five Characteristics of Good Tests (F.I.R.S.T)\n\n| Characteristic | Description |\n|---------------|-------------|\n| **Fast** | Single test < 100ms, full test suite < 30s |\n| **Independent** | No dependencies between tests, no dependency on execution order |\n| **Repeatable** | Results consistent across any environment; no dependency on external services, system time, or random numbers |\n| **Self-validating** | Results are pass/fail; no manual log inspection required |\n| **Prioritized by Impact** | Write tests for high-impact functions first: core business logic, functions with many callers, code that is hard to debug manually |\n\n### Choosing the Test Level\n\n```\n─── Test Pyramid (bottom to top: decreasing quantity, increasing cost) ─\n        ╱ ╲           E2E Tests — few, verify key user paths\n       ╱   ╲\n      ╱─────╲         Integration Tests — moderate, verify inter-module collaboration\n     ╱       ╲\n    ╱─────────╲       Unit Tests — many, verify behavior of individual functions/classes\n─────────────────────────────────────────────────────\n```\n\n**Selection criteria:**\n- Pure functions, computation logic, data transformation → **Unit tests**\n- Inter-module interaction, database reads/writes, API calls → **Integration tests**\n- Complete user operation flows → **E2E tests** (cover core paths only)\n\n---\n\n## Complete Execution Workflow\n\n> **Progress tracking:** At the start of each step, output an updated progress block.\n>\n> ```\n> Write Tests Progress\n> ✓ Step 1: Analyze Code Under Test   — <one-line finding>\n> → Step 2: Design Test Boundaries    — in progress\n> ○ Step 3: Determine Test Structure\n> ○ Step 4: Handle Dependencies\n> ○ Step 5: Implement Tests\n> ○ Step 6: Validate Quality\n> ```\n>\n> Replace `○` with `→` for the current step, and `✓` once complete.\n\n---\n\n### Step 1: Analyze the Code Under Test\n\nBefore writing tests, thoroughly understand the behavioral contract of the code under test.\n\n**Questions to answer:**\n- What is the **responsibility** of the function/class under test?\n- What are its **inputs**? What is the **valid range** and **boundary values** for each input?\n- What are its **outputs** (return values + side effects)?\n- What **preconditions** does it have?\n- Which **exceptions** might it throw? Under what conditions?\n- Which **external collaborators** does it depend on? Do these need to be mocked?\n\n🔗 When GitNexus is available, use `context` MCP tool to get callees (external dependencies) automatically.\n\n### Step 2: Design Test Boundaries\n\nDivide the behavioral space into discrete test regions.\n\n**Three required test scenarios:**\n\n**① Happy Path** — typical input → expected output; each meaningful parameter combination\n\n**② Boundary Conditions:**\n- Null/zero/empty collection/empty string\n- Maximum/minimum/just beyond range\n- Single-element collection (off-by-one happens most here)\n- Type boundaries (integer overflow, float precision)\n\n**③ Error Path:**\n- Invalid input → expected exception/error code\n- External dependency failure → expected degradation/error handling\n- Concurrency/race conditions (if applicable)\n- Timeout scenarios (if network/IO is involved)\n\n```\n─── Test Boundary Matrix ────────────────────────────\nTarget under test: <function/class name>\n\nHappy path:\n  case_1: <input description> → <expected output>\n\nBoundary conditions:\n  boundary_1: <boundary input> → <expected behavior>\n\nError path:\n  error_1: <invalid input> → <expected exception/error>\n─────────────────────────────────────────────────────\n```\n\n### Confirmation Gate (between Step 2 and Step 3)\n\nFor **Medium and Large tasks** (new test module or full coverage pass), after presenting the Test Boundary Matrix, use the confirmation gate with:\n\n- **question**: The Test Boundary Matrix above, plus: `\"Does this boundary coverage match your expectations before I write the tests?\"`\n- **options**:\n  - `\"Yes, proceed with implementation\"`\n  - `\"No — adjust the scope or boundaries\"`\n\nFor **Lightweight tasks** (1–3 tests for a single function) or **bug-fix reproduction tests**: skip — proceed directly to Step 3.\n\n**If user selects \"No\":** ask *\"Which scenarios should be added, removed, or changed?\"*, update the matrix, and show it again before proceeding.\n\n---\n\n### Step 3: Determine Test Structure\n\n**Naming conventions:** Test names must describe behavior, not repeat the function name.\n\n```python\n# ✅ Good test naming\ndef test_discount_returns_zero_when_rate_exceeds_price():\ndef test_login_fails_with_expired_token():\ndef test_parse_handles_empty_input_gracefully():\n\n# ❌ Poor test naming\ndef test_discount():\ndef test_login():\n```\n\n**Naming pattern:** `test_<behavior>_<condition>` or `test_<behavior>_when_<condition>_then_<expectation>`\n\n**AAA Structure (Arrange → Act → Assert):**\n\n```python\ndef test_order_total_applies_discount_for_vip_user():\n    # Arrange\n    user = create_vip_user()\n    items = [Item(\"book\", price=100), Item(\"pen\", price=20)]\n    order = Order(user=user, items=items)\n\n    # Act\n    total = order.calculate_total()\n\n    # Assert\n    assert total == 96.0  # VIP 20% discount: (100 + 20) * 0.8\n```\n\n**Each test verifies only one behavior.** Multiple asserts checking different aspects of the same behavior are acceptable.\n\n### Step 4: Handle External Dependencies\n\n| Dependency Type | Isolation Method | Use Case |\n|----------------|-----------------|----------|\n| Pure interface dependency (Repository, Client) | Mock objects | Unit tests |\n| Database | In-memory database / test containers | Integration tests |\n| External API | HTTP mock (e.g., responses, wiremock) | Integration tests |\n| File system | Temp directory + teardown cleanup | File read/write logic |\n| Time/random | Inject controllable clock / fixed seed | Deterministic results needed |\n\n**Mock discipline:**\n- Mocked behavior must be consistent with the real implementation's contract\n- Don't mock internal implementations of code you don't own — mock interfaces\n- If mock setup is more complex than the code under test, the code has too heavy dependencies\n\n🔗 When GitNexus is available, use `context` callees to distinguish internal vs external dependencies.\n\n### Step 5: Implement Tests\n\n**Recommended implementation order:**\n1. Write the most typical happy path case first — verify AAA structure and mock setup are correct\n2. Add boundary conditions — cover boundary items in the Step 2 matrix one by one\n3. Write error paths — verify error handling meets expectations\n\n**Reference existing test style:** New tests' file organization, naming style, assertion library, and fixture patterns must be consistent with existing tests in the project.\n\n🔗 When GitNexus is available, use `query \"<function name> test\"` to find reference tests.\n\n### Step 6: Validate Test Quality\n\n```\n─── Test Quality Checklist ──────────────────────────\n[ ] Independence: No dependencies between tests? Can run in any order? Can run individually?\n[ ] Determinism: No dependency on system time, random numbers, external services?\n[ ] Readability: Does reading the test name tell you what's being tested? Is AAA structure clear?\n[ ] Validity: If a line of logic in the code under test is deleted, will the test fail?\n[ ] Non-brittleness: Does the test depend on implementation details rather than behavior?\n[ ] Boundary coverage: Does each case in the Step 2 matrix have a corresponding test?\n[ ] Error messages: Is the error message on assertion failure sufficient to locate the problem?\n[ ] Speed: Is a single test < 100ms?\n─────────────────────────────────────────────────────\n```\n\n**\"Validity\" check technique (mutation testing mindset):**\nMake these changes to the code under test; check if tests fail:\n- Change `>` to `>=`\n- Change return value to `None` / `null`\n- Comment out a key conditional branch\n\nIf tests still pass, they cover lines but don't verify behavior.\n\n---\n\n## Forbidden Actions\n\n- **Write business logic in tests**: Directly assert expected concrete values; don't replicate computation logic\n- **Assert `assertTrue(result is not None)`**: Doesn't verify any behavior\n- **Depend on execution order**: Each test must be self-contained\n- **Test private methods**: Test behavior of public interfaces, not implementation details\n- **Ignore flaky tests**: Fix or delete them — flaky tests cause teams to ignore all failures\n\n---\n\n## Sprint State Integration\n\nIf `.sextant/state.json` exists in the project root and the current task matches a sprint task:\n\n- **On start:** offer to update the task's `status` from `pending` → `in_progress`. Ask: *\"Update sprint state to mark Task N as in_progress?\"*\n- **On completion** (acceptance condition met): offer to update `status` to `done`. Ask: *\"Update sprint state to mark Task N as done?\"*\n- **On blocker** (test failure, missing dependency, unresolvable ambiguity that halts progress): surface the issue, then ask: *\"Mark Task N as blocked and record the reason in flags?\"* If confirmed, set `status: \"blocked\"` and append `{\"task\": N, \"reason\": \"<one-sentence blocker description>\"}` to the top-level `flags` array. Do not proceed to the next task while a task is blocked.\n\nDo not write the file without explicit user confirmation. If the user declines, continue without state updates.\n\n---\n\n## Reply Format\n\n**Lightweight task** (1–3 tests for a single function): one sentence only.\n```\n✅ Added <N> tests for `<function>` covering <scenarios> (<test_file>:<line>).\n```\n\n**Medium/large task** (new test module or full coverage pass): full block.\n\nTest Summary:\n\n| # | Item | Detail |\n|---|------|--------|\n| [1] | Conclusion | <one sentence: N tests written for [target], covering [happy/boundary/error paths]> |\n| [2] | Changes | <test files / functions added or modified, with scenario count per category> |\n| [3] | Risks / Assumptions | <mock contracts assumed; known coverage gaps; scenarios deferred> |\n| [4] | Verification | <Step 6 quality checklist result; all tests passing / not yet run> |\n| [5] | Needs your input | <behavioral contracts that are ambiguous; tests the user should execute to confirm> |","tags":["write","tests","sextant","hellotern","agent-skills","claude-code","skill-md"],"capabilities":["skill","source-hellotern","skill-write-tests","topic-agent-skills","topic-claude-code","topic-skill-md"],"categories":["Sextant"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/hellotern/Sextant/write-tests","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add hellotern/Sextant","source_repo":"https://github.com/hellotern/Sextant","install_from":"skills.sh"}},"qualityScore":"0.457","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 14 github stars · SKILL.md body (12,036 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T13:03:26.858Z","embedding":null,"createdAt":"2026-04-19T00:40:28.485Z","updatedAt":"2026-04-22T13:03:26.858Z","lastSeenAt":"2026-04-22T13:03:26.858Z","tsv":"'/principles/skill_body.md':4 '/tool-gitnexus/skill_body.md':5 '0':195,209 '0.8':902 '1':74,126,461,500,680,684,688,753,1039,1475,1504 '100':207,210,878,900 '100ms':308,1225 '2':123,137,223,293,467,595,693,1056,1066,1200,1516 '20':882,898,901 '3':153,474,696,754,771,795,1071,1476,1529 '30s':312 '4':175,479,921,1540 '5':483,1033,1553 '6':487,1116,1543 '96.0':896 'aaa':854,1049,1159 'accept':919,1379 'accur':20 'across':326 'act':857,889 'action':1271 'ad':781,1485,1521 'add':1057 'addit':226 'adjac':157 'adjust':68,745 'alreadi':90 'ambigu':1405,1561 'analysi':98,128 'analyz':462,501 'answer':521 'anyth':81 'api':427,950 'append':1431 'appli':65,111,238,864 'applic':662 'arrang':856,869 'array':1441 'ask':776,1366,1388,1413 'aspect':913 'assert':122,203,858,893,894,910,1090,1213,1278,1287 'asserttru':1288 'assess':141 'assum':1534 'assumpt':1531 'automat':593 'avail':583,1022,1107 'baselin':286 'behavior':40,407,512,601,805,908,917,979,1191,1269,1297,1313,1557 'beyond':627 'block':456,1418,1429,1453,1499 'blocker':1399 'book':876 'bottom':381 'boundari':155,164,470,541,598,621,641,670,681,683,713,723,729,749,1058,1061,1192 'branch':1257 'brittl':1181 'broken':43 'bug':53,63,94,120,144,160,167,199,268,762 'bug-fix':761 'busi':362,1273 'calcul':184,204 'call':428 'calle':590,1025 'caller':150,367 'case':679,930,1046,1196 'categori':1528 'caus':85,196,202,275,1329 'chang':25,47,784,1234,1244,1246,1517 'characterist':298,303 'check':911,1227,1240 'checklist':1122,1545 'choos':375 'cleanup':963 'clear':180,1161 'client':935 'clock':970 'code':24,127,368,463,503,516,652,995,1010,1014,1170,1237 'collabor':402,569 'collect':632 'collection/empty':624 'combin':620 'comment':1252 'complet':431,441,498,1378 'complex':1007 'comput':415,1285 'conclus':1505 'concret':1280 'concurrency/race':659 'condit':88,116,165,174,566,622,660,682,1059,1256,1380 'confirm':689,717,1426,1462,1568 'consist':325,982,1097 'contain':946,1308 'context':585,1024 'continu':1467 'contract':513,988,1533,1558 'control':969 'convent':800 'core':9,361,438 'correct':1055 'correspond':1204 'cost':387 'count':1526 'cover':437,1060,1263,1488,1513 'coverag':18,227,707,730,1193,1496,1536 'creat':871 'criteria':412 'current':494,1347 'data':417 'databas':425,940,944 'debug':373 'declin':1466 'decreas':384 'def':101,182,815,824,831,841,844,860 'defer':1539 'degradation/error':657 'delet':1174,1325 'depend':151,315,319,330,481,572,592,654,924,925,933,1018,1031,1125,1138,1185,1298,1403 'describ':277,804 'descript':304 'design':468,596 'detail':1188,1319,1503 'determin':475,796,1136 'determinist':973 'differ':912 'direct':99,134,768,1277 'directori':961 'disciplin':977 'discount':185,205,817,843,865,899 'discret':604 'distinguish':1027 'divid':190,599 'document':113 'doesn':1293 'done':1387,1397 'e.g':953 'e2e':388,435 'effect':553 'element':631 'els':82 'empti':835 'enter':50,70 'environ':328 'error':646,685,687,1073,1076,1206,1210 'evalu':272 'exact':115 'exceed':822 'except':560 'exception/error':651 'execut':321,442,1300,1566 'exist':1081,1099,1340 'expect':615,650,656,733,1079,1279 'expir':829 'explicit':1460 'expos':168 'extern':332,568,591,653,923,949,1030,1144 'extrem':171 'f.i.r.s.t':302 'fail':106,233,254,827,1178,1243 'failur':280,655,1214,1334,1401 'fast':305 'file':958,964,1086,1458,1519 'find':1112 'first':79,360,1047 'five':297 'fix':54,64,95,109,131,145,193,200,236,256,267,763,971,1323 'fix-bug':266 'fixtur':1093 'flag':1424,1440 'flaki':1321,1327 'float':644 'flow':434 'focus':161 'follow':67 'forbidden':1270 'format':1472 'full':309,706,1495,1498 'function':132,359,364,414,759,809,1481,1520 'function/class':528,675 'functions/classes':410 'futur':33 'gap':1537 'gate':690,718 'get':589 'gitnexus':581,1020,1105 'go':36 'good':26,300,812 'grace':837 'halt':1407 'handl':480,658,834,922,1077 'happen':637 'happi':611,677,1044 'happy/boundary/error':1514 'hard':371 'heavi':1017 'high':357 'high-impact':356 'http':951 'ignor':1320,1332 'impact':140,352,358 'implement':484,743,986,993,1034,1037,1187,1318 'in-memori':941 'incomplet':259 'increas':386 'independ':313,1123 'individu':409,1135 'inject':968 'input':147,534,545,614,649,836,1556 'inspect':348 'integ':642 'integr':395,429,947,956,1337 'inter':400,422 'inter-modul':399,421 'interact':424 'interfac':932,1001,1316 'intern':992,1028 'invalid':648 'investig':284 'involv':668 'isol':927 'issu':1411 'item':874,875,879,887,888,1062,1502 'jump':133 'key':392,1255 'known':91,1535 'larg':700 'level':378,1439 'librari':1091 'lightweight':751,1473 'line':17,1165,1264 'll':283 'locat':1217 'log':347 'logic':363,416,966,1167,1274,1286 'login':826,846 'made':49 'make':1232 'mani':366,405 'manual':346,374 'mark':1371,1393,1414 'match':731,1349 'matrix':671,714,724,787,1067,1201 'maximum/minimum/just':626 'may':257 'mcp':586 'meaning':618 'medium':698 'medium/large':1489 'meet':1078 'memori':943 'messag':1207,1211 'met':1381 'method':928,1311 'might':561 'mindset':1231 'miss':1402 'mock':579,936,952,976,978,991,1000,1003,1052,1532 'moder':397 'modifi':1523 'modul':401,423,704,1493 'multipl':909 'must':105,803,980,1095,1304 'mutat':1229 'n':1373,1395,1416,1433,1508 'name':176,676,799,802,810,814,840,847,1088,1151 'need':576,975,1554 'net':31 'network/io':666 'new':702,1084,1491 'next':1447 'non':1180 'non-brittl':1179 'none':1250,1292 'null':169,1251 'null/zero/empty':623 'number':338,1143 'object':937 'off-by-on':633 'offer':1355,1382 'one':636,907,1068,1070,1482,1506 'oper':433 'option':739 'order':322,862,883,884,1038,1132,1301 'order.calculate':891 'organ':1087 'output':452,549,616 'overflow':643 'paramet':619 'pars':833 'pass':219,708,1261,1497,1549 'pass/fail':344 'path':394,439,612,647,678,686,1045,1074,1515 'pattern':848,1094 'pen':880 'pend':1363 'per':1527 'place':217 'pleas':260 'plus':726 'poor':838 'precis':645 'precondit':555 'present':710 'price':206,823,877,881 'principl':10 'priorit':154,350 'privat':1310 'problem':1219 'proceed':290,741,767,793,1444 'progress':444,455,459,472,1365,1376,1408 'project':1103,1343 'public':1315 'pure':413,931 'pyramid':380 'python':100,181,811,859 'qualiti':489,1119,1121,1544 'quantiti':385 'queri':1109 'question':519,720 'race':173 'random':337,1142 'rang':539,628 'rate':187,194,208,821 'rather':1189 're':262,271 're-evalu':270 're-trigg':261 'read':1148 'read/write':965 'readabl':1146 'reads/writes':426 'real':985 'reason':1422,1434 'recommend':1036 'record':1420 'red':37 'refactor':34 'refer':1080,1113 'region':606 'regress':22 'remain':279 'remov':782 'repeat':323,807 'replac':490 'repli':1471 'replic':1284 'report':21,245 'repositori':934 'reproduct':77,178,213,230,250,764 'requir':349,608 'respons':525,954 'result':121,324,342,974,1289,1546 'return':550,818,1247 'risk':1530 'root':84,201,274,1344 'rule':287 'run':1129,1134,1552 'safeti':30 'scenario':610,664,778,1525,1538 'scope':747 'see':198 'seed':972 'select':411,774 'self':340,1307 'self-contain':1306 'self-valid':339 'sentenc':1483,1507 'servic':333,1145 'session':58 'set':1427 'setup':1004,1053 'sextant':265 'sextant/state.json':1339 'show':789 'side':552 'singl':306,630,758,1223,1480 'single-el':629 'skill' 'skill-write-tests' 'skip':124,220,766 'source-hellotern' 'space':602 'speed':1220 'sprint':1335,1351,1368,1390 'standard':72 'start':296,448,1354 'state':1336,1369,1391,1469 'status':1361,1385,1428 'step':125,136,222,292,451,460,466,473,478,482,486,495,499,594,692,695,770,794,920,1032,1065,1115,1199,1542 'still':232,253,1260 'stop':239 'string':625 'structur':477,798,855,1050,1160 'style':1083,1089 'suffici':1215 'suit':311 'summari':1501 'surfac':1409 'system':334,959,1140 'target':672,1512 'task':701,752,1348,1352,1359,1372,1394,1415,1432,1448,1451,1474,1490 'team':1330 'teardown':962 'techniqu':1228 'tell':1152 'temp':960 'test':3,6,14,27,57,78,102,104,156,179,183,214,231,242,251,301,307,310,317,354,377,379,389,396,404,420,430,436,458,465,469,476,485,505,508,518,530,597,605,609,669,674,703,712,722,738,755,765,797,801,813,816,825,832,839,842,845,849,851,861,904,939,945,948,957,1012,1035,1082,1085,1100,1110,1114,1118,1120,1127,1150,1157,1172,1177,1184,1205,1224,1230,1239,1242,1259,1276,1303,1309,1312,1322,1328,1400,1477,1486,1492,1500,1509,1518,1548,1562 'test-writ':241 'thorough':509 'three':607 'throw':563 'time':335,1141 'time/random':967 'timeout':663 'token':830 'tool':587 'top':383,1438 'top-level':1437 'topic-agent-skills' 'topic-claude-code' 'topic-skill-md' 'total':863,890,892,895 'track':445 'transform':418 'trigger':60,87,118,263 'truli':42 'type':640,926 'typic':613,1043 'understand':510 'unit':403,419,938 'unrel':46 'unresolv':1404 'updat':454,785,1357,1367,1384,1389,1470 'use':96,138,584,715,929,1023,1108 'user':248,393,432,773,868,870,873,885,886,1461,1465,1564 'valid':341,488,538,1117,1162,1226 'valu':12,172,542,551,1248,1281 'verif':1541 'verifi':391,398,406,905,1048,1075,1268,1295 'vip':867,872,897 'vs':1029 'wiremock':955 'without':1459,1468 'workflow':8,73,244,443 'write':2,7,75,243,353,457,507,736,1040,1072,1272,1456 'write-test':1 'written':1510 'yes':740 'yet':1551 'zero':170,186,192,819 'zerodivisionerror':197","prices":[{"id":"8bc83a02-df66-45c7-870c-475190052c8d","listingId":"2dd8a269-d886-41b1-a7a6-0b806435d37b","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"hellotern","category":"Sextant","install_from":"skills.sh"},"createdAt":"2026-04-19T00:40:28.485Z"}],"sources":[{"listingId":"2dd8a269-d886-41b1-a7a6-0b806435d37b","source":"github","sourceId":"hellotern/Sextant/write-tests","sourceUrl":"https://github.com/hellotern/Sextant/tree/main/skills/write-tests","isPrimary":false,"firstSeenAt":"2026-04-19T00:40:28.485Z","lastSeenAt":"2026-04-22T13:03:26.858Z"}],"details":{"listingId":"2dd8a269-d886-41b1-a7a6-0b806435d37b","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"hellotern","slug":"write-tests","github":{"repo":"hellotern/Sextant","stars":14,"topics":["agent-skills","claude-code","skill-md"],"license":"mit","html_url":"https://github.com/hellotern/Sextant","pushed_at":"2026-04-07T00:57:27Z","description":"init","skill_md_sha":"63275d17eb2ea660da149b2256cf55e26ec28cae","skill_md_path":"skills/write-tests/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/hellotern/Sextant/tree/main/skills/write-tests"},"layout":"multi","source":"github","category":"Sextant","frontmatter":{"description":">-"},"skills_sh_url":"https://skills.sh/hellotern/Sextant/write-tests"},"updatedAt":"2026-04-22T13:03:26.858Z"}}