[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-96739":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":13,"contributorsCount":13,"subscribersCount":13,"size":13,"stars1d":13,"stars7d":13,"stars30d":13,"stars90d":13,"forks30d":13,"starsTrendScore":13,"compositeScore":14,"rankGlobal":8,"rankLanguage":8,"license":15,"archived":16,"fork":16,"defaultBranch":17,"hasWiki":18,"hasPages":16,"topics":19,"createdAt":8,"pushedAt":8,"updatedAt":20,"readmeContent":21,"aiSummary":8,"trendingCount":13,"starSnapshotCount":13,"syncStatus":12,"lastSyncTime":22,"discoverSource":23},96739,"Fibo","0xkuch\u002FFibo","0xkuch",null,"Python",282,1,2,0,37.9,"MIT License",false,"main",true,[],"2026-09-25 04:01:33","\u003Cp align=\"center\">\n  \u003Cimg src=\"docs\u002Fbanner.svg\" alt=\"Fibo — git blame for your promises\" width=\"780\">\n\u003C\u002Fp>\n\n\u003Ch1 align=\"center\">Fibo\u003C\u002Fh1>\n\n\u003Cp align=\"center\">\u003Cb>git blame for your promises.\u003C\u002Fb>\u003Cbr>\nYou have made a thousand estimates. You have checked none of them.\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cimg alt=\"python 3.10+\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fpython-3.10%2B-b5e853?style=flat-square&labelColor=0b0d0b\">\n  \u003Cimg alt=\"zero dependencies\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fdeps-0-b5e853?style=flat-square&labelColor=0b0d0b\">\n  \u003Cimg alt=\"227 tests\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Ftests-227%20%E2%9C%93-b5e853?style=flat-square&labelColor=0b0d0b\">\n  \u003Cimg alt=\"one subject\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fsubjects-one-f0c419?style=flat-square&labelColor=0b0d0b\">\n  \u003Cimg alt=\"license MIT\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-MIT-e8e8e0?style=flat-square&labelColor=0b0d0b\">\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\u003Csub>GIT HISTORY · TERMINAL · ZERO DEPENDENCIES · NO DATABASE · NO ACCOUNT · NOTHING LEAVES THE MACHINE\u003C\u002Fsub>\u003C\u002Fp>\n\n\u003C!-- token -->\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fwww.ponsfamily.com\u002Flaunchpad\u002F0xfc7e0cddb9d7a1676edefbb4d87dda29efe2053f\">\u003Cimg alt=\"$FIB · live on robinhood chain\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002F%24FIB-live%20on%20robinhood%20chain-f0c419?style=flat-square&labelColor=0b0d0b\">\u003C\u002Fa>\u003Cbr>\n  \u003Csub>\u003Ccode>0xfc7e0cddb9d7a1676edefbb4d87dda29efe2053f\u003C\u002Fcode>\u003Cbr>\n  \u003Ca href=\"https:\u002F\u002Fwww.ponsfamily.com\u002Flaunchpad\u002F0xfc7e0cddb9d7a1676edefbb4d87dda29efe2053f\">swap\u003C\u002Fa> · \u003Ca href=\"https:\u002F\u002Fwww.geckoterminal.com\u002Frobinhood\u002Fpools\u002F0xd508f4f03cc73775f46c1102753cd4dba74d10124ecf3db969d2608e9b63c6b5\">chart\u003C\u002Fa> · \u003Ca href=\"https:\u002F\u002Frobinhoodchain.blockscout.com\u002Ftoken\u002F0xfc7e0cddb9d7a1676edefbb4d87dda29efe2053f\">explorer\u003C\u002Fa> · \u003Ca href=\"https:\u002F\u002Ffibo.fund\u002F#token\">fibo.fund\u003C\u002Fa> · burns every 9 hours\u003C\u002Fsub>\n\u003C\u002Fp>\n\u003C!-- \u002Ftoken -->\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"#thirty-seconds\">start\u003C\u002Fa> ·\n  \u003Ca href=\"#a-promise-with-a-number-on-it\">said \u002F took\u003C\u002Fa> ·\n  \u003Ca href=\"#the-multiplier\">the multiplier\u003C\u002Fa> ·\n  \u003Ca href=\"#what-it-refuses\">refusals\u003C\u002Fa> ·\n  \u003Ca href=\"#on-disk\">on disk\u003C\u002Fa> ·\n  \u003Ca href=\"#the-token\">the token\u003C\u002Fa> ·\n  \u003Ca href=\"#honest-limits\">honest limits\u003C\u002Fa>\n\u003C\u002Fp>\n\n---\n\n## Film\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"docs\u002Ffilm\u002Frun.svg\" alt=\"the full run: fibo on the demo repository\" width=\"780\">\n\u003C\u002Fp>\n\n\u003Ctable align=\"center\">\u003Ctr>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Ffilm\u002Fsay.svg\" width=\"250\" alt=\"fibo say\">\u003Cbr>\u003Csub>\u003Cb>say\u003C\u002Fb> · writing a promise down\u003C\u002Fsub>\u003C\u002Ftd>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Ffilm\u002Ftask.svg\" width=\"250\" alt=\"one task, taken apart\">\u003Cbr>\u003Csub>\u003Cb>one task\u003C\u002Fb> · taken apart\u003C\u002Fsub>\u003C\u002Ftd>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Ffilm\u002Fdoctor.svg\" width=\"250\" alt=\"fibo doctor\">\u003Cbr>\u003Csub>\u003Cb>doctor\u003C\u002Fb> · through the monocle\u003C\u002Fsub>\u003C\u002Ftd>\n\u003C\u002Ftr>\u003C\u002Ftable>\n\n## Screens\n\n\u003Cp align=\"center\">\u003Cimg src=\"docs\u002Fscreens\u002Freport.svg\" alt=\"the report\" width=\"780\">\u003C\u002Fp>\n\n\u003Ctable align=\"center\">\u003Ctr>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Fscreens\u002Ftask.svg\" width=\"380\" alt=\"one task\">\u003Cbr>\u003Csub>\u003Cb>one task\u003C\u002Fb> · said, first commit, merge, span, active, the longest pause\u003C\u002Fsub>\u003C\u002Ftd>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Fscreens\u002Fdrift.svg\" width=\"380\" alt=\"fibo drift\">\u003Cbr>\u003Csub>\u003Cb>drift\u003C\u002Fb> · quarter by quarter\u003C\u002Fsub>\u003C\u002Ftd>\n\u003C\u002Ftr>\u003Ctr>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Fscreens\u002Fdoctor.svg\" width=\"380\" alt=\"fibo doctor\">\u003Cbr>\u003Csub>\u003Cb>doctor\u003C\u002Fb> · four estimates that disagree, fourteen written late, the chain intact\u003C\u002Fsub>\u003C\u002Ftd>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Fscreens\u002Frefusals.svg\" width=\"380\" alt=\"what it refuses\">\u003Cbr>\u003Csub>\u003Cb>refusals\u003C\u002Fb> · five ways to be told no\u003C\u002Fsub>\u003C\u002Ftd>\n\u003C\u002Ftr>\u003Ctr>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Fscreens\u002Fempty.svg\" width=\"380\" alt=\"an empty repository\">\u003Cbr>\u003Csub>\u003Cb>day one\u003C\u002Fb> · yours starts empty\u003C\u002Fsub>\u003C\u002Ftd>\n\u003Ctd align=\"center\">\u003Cimg src=\"docs\u002Fscreens\u002Fru.svg\" width=\"380\" alt=\"--lang ru\">\u003Cbr>\u003Csub>\u003Cb>--lang ru\u003C\u002Fb> · it speaks Russian too\u003C\u002Fsub>\u003C\u002Ftd>\n\u003C\u002Ftr>\u003C\u002Ftable>\n\n\u003Csub>Every picture above is a real transcript: the demo repository, the real commands, drawn by \u003Ccode>docs\u002Fshots.py\u003C\u002Fcode>.\u003C\u002Fsub>\n\n## Thirty seconds\n\n```sh\npipx install git+https:\u002F\u002Fgithub.com\u002F0xkuch\u002FFibo\nfibo demo          # two years of made-up history, same numbers on every machine\nfibo               # inside your own repository\n```\n\n```\n  (o,o)   fibo · \u002Ftmp\u002Ffibo-demo\n  \u002F)_)\n   \"\"\n\n230 tasks · 225 closed · 194 estimated · 169 counted\n\n  said \"2 days\"   — took 9.6 days  ×4.8\n  said \"a day\"    — took 2.7 days  ×2.7\n  said \"4 hours\"  — took 14 hours  ×3.4\n\n  your multiplier    3.7×   calendar 5.2×\n  worst case (p90)   8.6×\n  underestimated     91% of tasks\n\n  by kind of work\n    bug fixes      ×1.3    n=42\n    features       ×3.7    n=66\n    refactoring    ×5.7    n=39\n    migrations     ×7.1    n=20\n\n  drift   3.7× ▄▃▃▃▄▃█▆▁▁▁▆ 4.5×\n          two years ago      now\n\nexcluded: 25 tasks (estimate after start: 14 · not a duration: 5 · no branch: 4 · points, not time: 2)\none subject: you — you@fibo.demo\nall of it computed from your history, none of it written here\n```\n\nYours starts empty. It speaks Russian too (`--lang ru`).\n\n## A promise with a number on it\n\n| | |\n|---|---|\n| **said** | an estimate written down before the first commit: in your tracker, in the commit, or in the ledger |\n| **took** | first commit → merge, in working hours, in the time zone your own commits carry |\n| **×** | took ÷ said for every task you finished, and the median of those, taken in log space |\n\nYour tracker already holds years of estimates (Jira, Linear, GitHub, GitLab), so the first run has history to work with. Without a tracker, put `Estimate: 2d` in the first commit of a branch. Without either, `fibo say PROJ-412 2d` writes it down before you start. [Where estimates come from →](docs\u002Fsources.md)\n\n> An estimate nobody checks is not an estimate. It is a mood.\n\n## The multiplier\n\nThis number is the whole product. It does not tell you to estimate better. It tells you what to multiply by.\n\n- **Median in log space.** ×4 and ×¼ are the same distance from ×1, and the median does not care about the one ×40 disaster. The geometric mean is in `-v`.\n- **Working hours.** Mon–Fri, 9–18, in the offset your own commits carried. A day is 8h, a week is 40h. `--calendar` takes both sides literally, shown small.\n- **span and active.** span: first commit → merge. active clips any pause over three working days. The gap is how long tasks sat waiting.\n- **p90** is the bad day: \"in the worst case, ×8.6\".\n- **By kind of work:** tracker labels, then where the diff went (`migrations\u002F`, `tests\u002F`), then the words in the title. Five tasks to be shown.\n- **Drift:** a 90-day rolling median, indexed by when the work landed — indexed by when it began, the last window would fake an improvement.\n\n`fibo PROJ-412` takes one task apart. `fibo drift` shows it quarter by quarter. `fibo doctor` checks that nobody nudged anything.\n\n## What it refuses\n\n```\nfibo say PROJ-412 soon          ✗  not a duration\nfibo say PROJ-412 2d            ✗  written after the work began\na task with no estimate         ✗  not counted, and not guessed\n--whose someone@else.com        ✗  this tool has one subject\nstory points, no rate given     ✗  points are not time\n```\n\n**Not a duration.** \"Soon\", \"by the end of the sprint\", \"quick\" are not estimates. Say a number and a unit.\n\n**Written after the work began.** The program knows when the field changed and when the first commit was made. The value that stood at the first commit counts. `doctor` lists the rest.\n\n**Not guessed.** A task without an estimate gets nothing by analogy, by average or by diff size. It is left out, and the output says so.\n\n**One subject.** Yours. It will not print another person's multiplier, aggregate a team, or export. `--whose` with anyone else's address fails loudly. Trackers are asked for your issues only. There is no JSON, no repository-wide mode, no all-authors mode.\n\n**Points are not time.** `--points-as-hours=N` exists, and the output then says the number rests on your assumption.\n\n> A mirror pointed at someone else is a different object with a different name.\n\n## On disk\n\n```\n.fibo.json            optional, yours: who you are, which tracker to ask\n.fibo\u002Fledger.jsonl    only if you use `fibo say`; every line hashes the one before\n.fibo\u002Fledger.head     the hash of the last line\n.fibo\u002Fcache\u002F          tracker responses with a TTL; delete it any time\n```\n\nThat is all of it. No database, no index, no daemon, no server, no account, no telemetry. Every run recomputes from history. With a tracker it only reads — GET and GraphQL queries; a mutation is refused before it is sent. The config loader reads one file and never follows a path to a key.\n\n> A multiplier you can quietly edit after a bad quarter is not a multiplier.\n\n## The token\n\n**$FIB** on Robinhood Chain — a community token. The code does not know it exists: there is no import, no flag, no line in the ledger. `fibo` will never require it and will never print its price.\n\n| | |\n|---|---|\n| contract | `0xfc7e0cddb9d7a1676edefbb4d87dda29efe2053f` |\n| swap | https:\u002F\u002Fwww.ponsfamily.com\u002Flaunchpad\u002F0xfc7e0cddb9d7a1676edefbb4d87dda29efe2053f |\n| chart | https:\u002F\u002Fwww.geckoterminal.com\u002Frobinhood\u002Fpools\u002F0xd508f4f03cc73775f46c1102753cd4dba74d10124ecf3db969d2608e9b63c6b5 |\n| explorer | https:\u002F\u002Frobinhoodchain.blockscout.com\u002Ftoken\u002F0xfc7e0cddb9d7a1676edefbb4d87dda29efe2053f |\n\n```\nfibo say $FIB 100x              ✗  not a duration\n--whose the-community           ✗  one subject\nfibo predict $FIB               ✗  it predicts nothing\n```\n\n\u003Csub>not financial advice · the owl is not a financial advisor · it is not even a real owl\u003C\u002Fsub>\n\n## Roadmap\n\nNo dates. This program exists because durations written in advance are wrong by a known factor, and it would have to multiply its own. So: order, not calendar.\n\n```\n00  the tool                         ✓\n01  the multiplier                   ✓\n02  the breakdown by kind of work    ✓\n03  the drift                        ✓\n04  more sources                     YouTrack, Azure Boards, Shortcut, …\n```\n\n## Honest limits\n\n**IT CANNOT SEE THINKING.** The clock starts at the first commit, not at the first thought. Your real multiplier is higher than the one printed.\n\n**IT CANNOT SEE INTERRUPTION.** A branch that sat for a week looks like a week of work. `active` is there for that, and it is still an approximation.\n\n**THE DEMO IS A DEMO.** Reproducible, and nobody's record of promises. Yours starts empty.\n\n**IT DOES NOT KNOW THE RIGHT ANSWER.** There is no correct estimate. It knows what you said and how long it took.\n\n**IT PREDICTS NOTHING.** No model, no opinion. What was written down, what happened, one division.\n\nA squash merge erases the start of the work: the tracker's \"In Progress\" or a leftover branch fills it in, and without either the task is excluded as \"no branch\". Time zones come from your commits' offsets, so a laptop set to the wrong zone gets a wrong day.\n\n## Development\n\n```sh\npython -m unittest        # 227 tests, stdlib only\npython docs\u002Fshots.py      # redraws every picture above, from the real output\n```\n\n---\n\n\u003Cp align=\"center\">\u003Csub>MIT · built for the fun of it · \u003Ccode>(-,-)\u003C\u002Fcode> the owl has seen enough\u003C\u002Fsub>\u003C\u002Fp>\n","2026-09-24 02:30:12","CREATED_QUERY"]