{"slug": "catch-ai-code-hallucinations-without-asking-a-model", "title": "Catch AI code hallucinations without asking a model", "summary": "Hedgemony, a new open-source tool, detects AI code hallucinations by checking code against the Python interpreter and package registries without querying any language model, reporting precise verdicts such as fabrication, invention, misattribution, malformation, and contradiction. The tool, which avoids the terms 'lying' and 'hallucination' in favor of decidable claims, found 2 fabrications in 21 lines of a sample dashboard.py file, and it flags files with no stated examples as NO_CONTRACT to signal that confabulations cannot be detected.", "body_md": "\n\n```\n██╗  ██╗███████╗██████╗  ██████╗ ███████╗███╗   ███╗ ██████╗ ███╗   ██╗██╗   ██╗\n██║  ██║██╔════╝██╔══██╗██╔════╝ ██╔════╝████╗ ████║██╔═══██╗████╗  ██║╚██╗ ██╔╝\n███████║█████╗  ██║  ██║██║  ███╗█████╗  ██╔████╔██║██║   ██║██╔██╗ ██║ ╚████╔╝\n██╔══██║██╔══╝  ██║  ██║██║   ██║██╔══╝  ██║╚██╔╝██║██║   ██║██║╚██╗██║  ╚██╔╝\n██║  ██║███████╗██████╔╝╚██████╔╝███████╗██║ ╚═╝ ██║╚██████╔╝██║ ╚████║   ██║\n╚═╝  ╚═╝╚══════╝╚═════╝  ╚═════╝ ╚══════╝╚═╝     ╚═╝ ╚═════╝ ╚═╝  ╚═══╝   ╚═╝\n\n────────────────────────────────────────────────────────────────────────────────\n  F I N D   W H A T   W A S   M A D E   U P\n────────────────────────────────────────────────────────────────────────────────\n```\n\n**hedgemony** finds the things that do not exist in code an AI wrote — packages that were never\npublished, methods that were never written, arguments no function accepts — and the code that\ncontradicts its own stated examples.\n\nEvery verdict comes from the Python interpreter or a package registry. **No language model is\nasked anything.** That is the point: a finding is a fact about the world, not a second opinion\nfrom the same kind of system that produced the mistake.\n\n``` bash\n$ hedgemony dashboard.py\n\n  dashboard.py\n    2 fabrication(s) in 21 lines = 9.5 per 100 lines\n    no stated examples in this file, so its behaviour was not checked at all\n\n    line   12  ATTR      `console` has no attribute `table`\n    line   19  ATTR      `console` has no attribute `progress`\n\n    rewrite -- that attribute does not exist\n```\n\n\"Hallucination\" is vague and \"lying\" is wrong, so hedgemony does not use either as a verdict. Every finding is named precisely, and each name is a claim you can check:\n\n| word | what it means | example | decidable? |\n|---|---|---|---|\nfabrication |\nthe umbrella: a claim about the world that is false |\n— | yes |\ninvention |\nthe name exists nowhere |\n`import ghostlib` |\nyes |\nmisattribution |\na real name on the wrong owner |\n`json.serialise` |\nyes |\nmalformation |\nthe target is real, the call is impossible |\n`math.sqrt(2, 3)` |\nyes |\ncontradiction |\nthe code disagrees with its own stated behaviour |\na docstring example that fails | yes |\nconfabulation |\nplausible wrong logic, with nothing stated to check it against |\n— | no — not detected |\n\nTwo words this tool deliberately avoids:\n\n**\"Lying\" is the wrong word.** Lying requires intent to deceive. A model has no intent, so\nnothing here takes a position on motive. hedgemony reports **truth value only**: this name does\nnot exist, this example did not hold. Whether anything meant to mislead is not a question the\ninterpreter can answer, and not one this tool pretends to.\n\n**\"Hallucination\" is a popular umbrella** covering both the decidable and the undecidable.\nhedgemony measures **only the decidable part** — the first five rows above. That is why a clean\nresult is reported as *no fabricated names*, never as *correct*.\n\nTo say code is wrong, you need something to compare it against. hedgemony has two such\nstandards: the **interpreter**, for whether a name exists, and **an example you wrote**, for\nwhether the code does what you said. Confabulation has neither.\n\n``` python\ndef average(values):\n    return sum(values) / len(values) - 1        # the -1 is wrong\n```\n\nEvery name exists. Nothing states what the answer should be. The only description of what this\nfunction does *is the function*, and it agrees with itself perfectly. There is nothing to check\nit against — not by this tool, and not by any tool.\n\n**It is not permanently invisible.** One line changes it:\n\n``` python\ndef average(values):\n    \"\"\"\n    >>> average([2, 4])\n    3.0\n    \"\"\"\n    return sum(values) / len(values) - 1\nline 3  CONTRACT  `average([2, 4])` was stated to give `3.0` but gave `2.0`\n```\n\nThe confabulation became a **contradiction**, and contradictions are caught. This is exactly\nwhy hedgemony reports `NO_CONTRACT`\n\nloudly instead of passing such files quietly — it is\ntelling you the one thing that would make the file checkable.\n\n**Why no better tool fixes this.** \"Is this what you meant?\" is not a property of the code. It\nexists only in your head until you write it down, and no amount of analysis can read an\nintention that was never recorded. So the honest goal is not to detect intent — it is to make\n*stating* intent cost one line, then execute it without mercy. That is what the contract layer\ndoes, and it is why this gap is named here rather than left out.\n\nTwo passes over each file. The first never runs anything; the second runs only what you wrote down.\n\n**Pass one — does this name exist?** The file is parsed into a syntax tree, and every name it\nrefers to becomes a question put to a live interpreter. Does `math`\n\nhave `median`\n\n? Does `json`\n\nexport `serialise`\n\n? Does `re.sub`\n\ntake a `greedy`\n\nkeyword? These are answered with\n`hasattr`\n\nand `inspect.signature`\n\n— the same machinery Python itself uses — so an answer is a\nfact about the machine that will run your code, not an inference. Nothing is executed: asking\nwhether a name exists never requires calling it.\n\n**Pass two — does the code do what it says?** If a docstring contains a `>>>`\n\nexample, that\nexample is a claim the author wrote down, and running it settles whether the code agrees with\nit. This is the only part that executes anything, it happens in a bounded separate process, and\n**a file with no stated examples is never run at all** — that is decided by parsing, before\nanything starts.\n\n**When it cannot decide, it says nothing.** An uninstalled package, a variable whose type is\nambiguous, a C builtin with no readable signature — all produce silence rather than a guess.\nThat asymmetry is deliberate: a false alarm sends you to rewrite correct code and you have no\nway to discover the tool was wrong, while a miss still meets every test and review downstream.\n\n```\n        your file\n            │\n            ├── parse ──► every name it claims exists\n            │                     │\n            │                     ▼\n            │            ask the interpreter  ──►  exists / does not / cannot tell\n            │                                          │        │           │\n            │                                       silent   FINDING     NOT CHECKED\n            │\n            └── any \">>>\" examples? ──no──► NO_CONTRACT (behaviour unknown)\n                        │\n                       yes\n                        ▼\n                run them, sandboxed  ──►  held / did not hold\n```\n\nThat is the whole design. There is no model in it, no scoring, and no threshold to tune.\n\n```\npip install hedgemony\n```\n\nOr just clone it and run — **there are no dependencies.** Python 3.9 or newer, standard library\nonly, nothing to configure.\n\n```\ngit clone https://github.com/lovettsendit/hedgemony\ncd hedgemony\npython3 -m hedgemony yourfile.py\nhedgemony app.py                  # one file\nhedgemony src/                    # a directory, recursively\nhedgemony src/ --quiet            # only show files with problems\nhedgemony app.py --report         # write an annotated copy beside the file\nhedgemony app.py --report html    # ...as a self-contained page instead\nhedgemony src/ --json             # machine-readable, for a pipeline\nhedgemony app.py --report both    # one of each\nhedgemony app.py --no-run         # never run the checked file\nhedgemony app.py --online         # also ask registries about uninstalled packages\nhedgemony --board a/ b/ c/        # rank directories against each other\n```\n\nExit codes: **0** nothing found · **1** findings · **2** the tool could not run. Drop it into\nCI as-is.\n\nSix kinds of invented name, each decided by asking the interpreter directly:\n\n``` python\nPACKAGE   import ghostlib               no such package was ever published\nMODPATH   from json.fast import load    json is real, json.fast is not\nIMPORT    from json import serialise    json exists and does not export that\nATTR      console.table(...)            the object has no such attribute\nKWARG     re.sub(..., greedy=True)      the function accepts no such keyword\nARITY     math.sqrt(2, 3)               the function cannot take that many arguments\n```\n\nThey split into exactly two actions, which is the part that saves time:\n\n| meaning | what to do | |\n|---|---|---|\n`PACKAGE` `MODPATH` `IMPORT` `ATTR` |\nthe thing does not exist | rewrite |\n`KWARG` `ARITY` |\nthe function is real, the call is wrong | fix the call |\n\nA type checker gives the same error for both of these:\n\n``` python\nfrom humanize import naturalsize   # a real package — just not installed here\nimport ghostlib                     # never existed anywhere\n```\n\n`Cannot find implementation or library stub for module named \"humanize\"`\n\n`Cannot find implementation or library stub for module named \"ghostlib\"`\n\nSame message, completely different problem. One is `pip install`\n\n. The other means the code can\nnever work and needs rewriting. `hedgemony --online`\n\ntells them apart:\n\n```\nline 2  PACKAGE  no package `ghostlib` was ever published\n```\n\n`humanize`\n\nis not flagged. It exists.\n\nNames existing is not the same as code being right:\n\n``` python\ndef pages_needed(items, per_page):\n    \"\"\"How many pages are needed to show every item.\n\n    >>> pages_needed(10, 3)\n    4\n    \"\"\"\n    return math.floor(items / per_page)\n```\n\n`math.floor`\n\nexists. The call is well formed. Every static checker passes this file — and it is\nwrong. Ten items at three per page needs four pages; this returns three.\n\n```\nline   11  CONTRACT  `pages_needed(10, 3)` was stated to give `4` but gave `3`\n```\n\nThe authority is not this tool's opinion about what the function should do. It is a claim the\nauthor wrote into the file, in a standard executable format. hedgemony reports the contradiction\nbetween two things already in the file — and does **not** guess which side is wrong.\n\n**Making a file checkable costs one line.** If a file states no examples, hedgemony says so plainly\nrather than passing it:\n\n```\nno stated examples in this file, so its behaviour was not checked at all\n```\n\nThis matters more than the feature list.\n\n**A clean result is not a proof of correctness.** It means no fabricated*name*was found. Code that calls the wrong real function is invisible to name checking, by construction. Every report says so in those words.**It does not judge style, performance, or design.****It does not guess.** Anything that cannot be decided — an uninstalled package's internals, a variable of ambiguous type, a C builtin with no introspectable signature — is left unreported. A false alarm sends someone to rewrite correct code with no way to discover the tool was wrong; a miss still meets every test downstream. The costs are not symmetric, so ambiguity always resolves to silence.\n\n```\nhedgemony app.py --report        # app.py.hedgemony.md\nhedgemony app.py --report html   # app.py.hedgemony.html\n```\n\nOne report per source file, at one fixed name, **overwritten every run** — a hundred runs leave\none file, not a hundred.\n\nThe markdown carries the whole file with every line marked and each finding keyed by number. It reads correctly as plain text, needs no renderer, and compresses well, which matters when the reader is an agent paying for every line:\n\n``` python\n+  8 | def pages_needed(items, per_page):\n+  9 |     \"\"\"How many pages are needed to show every item.\n+ 10 |\n! 11 |     >>> pages_needed(10, 3)  #1\n+ 12 |     4\n+ 13 |     \"\"\"\n+ 14 |     return math.floor(items / per_page)\n```\n\nThe HTML is one self-contained page — black ground, code coloured green where it is clean and red where it is not, nothing loaded from anywhere.\n\n```\nhedgemony app.py --report        # markdown  (default)\nhedgemony app.py --report html   # a page\nhedgemony app.py --report both   # one of each\n```\n\nNothing is written unless you ask. Without `--report`\n\nit prints to the terminal and leaves no\nfiles behind.\n\n**hedgemony never talks to your model.** There is no endpoint to configure, no API key, no\nintegration with any runner. It works on the code your model produced, which is already a file\non disk. That is deliberate: a checker that asked the model whether the model was wrong would\nbe asking the thing that made the mistake, and its answer would be worth nothing. The\ninterpreter has no such conflict of interest.\n\nSo the flow is three steps, and the first two are what you already do:\n\n```\n  1.  your model writes code       (any runner, any IDE, any agent — it does not matter)\n  2.  it lands in a file           (this already happens)\n  3.  hedgemony thatfile.py        ← the only new step\n# whatever you normally do to get code out of your model, then:\nhedgemony generated.py\n```\n\nThat works for **any** model — local, hosted, one you have no API access to, or a snippet\nsomeone sent you. If it produced code you can save, hedgemony can check it.\n\nGive each model its own folder and rank them:\n\n```\n  out/\n    qwen/       ← one model's output\n    llama/      ← another's\n    handwritten/\nhedgemony --board out/qwen out/llama out/handwritten\nRANKED BY DEFECTS PER 100 LINES  (lower is better)\n\n     source                  per 100  names     contracts   lines  files\n  1  handwritten                 0.0      0        4 held      34      1\n  2  llama                       3.3      0      2 broken      61      1\n  3  qwen                        9.5      2   none stated      21      1\n```\n\nSame prompts into each folder makes it a fair comparison. Read the line counts alongside the rate — a model that wrote less scores better for it.\n\nAsk your model to include a `>>>`\n\nexample in each docstring. It costs one line, models produce\nthem readily, and it turns behaviour from unknown into checkable:\n\n\"…and give every function a docstring with a\n\n`>>>`\n\nexample showing the expected output.\"\n\nWithout one, hedgemony can only tell you the names exist. With one, it can tell you the code\ndisagrees with what the model itself said it would do — which is how the logic bug in\n`examples/generated_with_contracts.py`\n\nwas caught.\n\nOne measurement is an anecdote. A rate over a body of code is comparable.\n\n```\nhedgemony --board out/model_a out/model_b out/handwritten\nRANKED BY DEFECTS PER 100 LINES  (lower is better)\n\n     source                  per 100  names     contracts   lines  files\n  1  handwritten                 0.0      0        4 held      34      1\n  2  model_b                     3.3      0      2 broken      61      1\n  3  model_a                     9.5      2   none stated      21      1\n      1x  `console` has no attribute `table`\n      1x  `console` has no attribute `progress`\n```\n\nA source is just a directory, so this compares whatever you put in them — two models, two\nprompting strategies, last month against this month, your team against a vendor. **Nothing is\ngenerated and no model is contacted**; it reads code that already exists, which is why it can\nscore anything you can save to disk.\n\nThe rank is over **every** defect found — invented names *and* stated examples that failed.\nRanking on names alone once put a source with two broken contracts above one where four held,\nbecause neither had invented anything. Both columns stay visible so a position is never a\nsingle opaque number.\n\n```\nhedgemony --board a/ b/ --out board.html   # the extension picks the format\nhedgemony --board a/ b/ --json             # for a pipeline\n```\n\n**Read the line counts.** A rate measures defects, not capability, and a source that attempted\nless will score better for it. That caveat is printed with every ranking rather than left in\nthe documentation.\n\nEvery verdict comes from asking an interpreter whether a name exists — so **which** interpreter\nis asked decides the answer. Install hedgemony on its own and it cannot see your project's\nlibraries:\n\n``` bash\n$ hedgemony app.py\n    0 fabrication(s) in 21 lines = 0.0 per 100 lines\n    NOT CHECKED: rich — not installed in the interpreter used, so names from it\n                 were not examined\n```\n\nZero findings, because nothing could be looked at. Silence that reads like a pass is the worst failure a checker can have, so hedgemony does two things about it.\n\n**It says so.** Any package it could not resolve is listed as `NOT CHECKED`\n\n, by name.\n\n**It goes and asks your interpreter instead.** hedgemony depends on nothing outside the\nstandard library, so it can run inside your project's environment without being installed\nthere. A virtual environment beside your code is found automatically:\n\n``` bash\n$ hedgemony app.py\n  using the interpreter that owns this code: Python 3.12 at /path/to/project/.venv\n\n    2 fabrication(s) in 21 lines = 9.5 per 100 lines\n    line 12  ATTR  `console` has no attribute `table`\n```\n\nSame tool, same file, one install — it just asked the right interpreter. Point it anywhere:\n\n```\nhedgemony src/ --python /path/to/project/.venv/bin/python\nhedgemony src/ --python self      # force the interpreter running hedgemony\n```\n\nThe order it chooses: `--python`\n\nif given, then a `.venv`\n\n/`venv`\n\n/`.env`\n\nfound by walking up\nfrom your code, then an activated `VIRTUAL_ENV`\n\n, then the interpreter running hedgemony. When\nit hands over, it prints which interpreter it used — that is never silent.\n\nIf the interpreter you point it at cannot run the scan — too old, missing a module, not really\nan interpreter — hedgemony says so and **falls back to its own**, listing whatever it cannot\nresolve as `NOT CHECKED`\n\n. A partial answer with the gaps named beats no answer at all.\n\nOnly hedgemony's own package is copied across, never the environment it was installed into. If\nit shared its own `site-packages`\n\n, your project would appear to have libraries it does not\nhave, and the tool would go quiet for the wrong reason.\n\nIf you keep code on an exFAT or FAT32 drive, an SD card, or most network shares, macOS writes a\nhidden metadata companion for every file — `app.py`\n\ngets a `._app.py`\n\nalongside it. They match\nevery source glob and are not source.\n\n**hedgemony skips them by name, everywhere**, so you do not need to do anything. If you want\nthem gone from a checkout:\n\n```\ndot_clean .                      # merge and remove them\nfind . -name '._*' -delete       # or just delete them\n```\n\nThe bundled `.gitignore`\n\nalready excludes `._*`\n\nand `.DS_Store`\n\nso they never reach a commit.\n\nContract checking has to run the file, and the file was written by a machine. So:\n\n- Every execution happens in\n**a separate interpreter**, bounded on CPU, memory, process count, file size and wall time. **Network is refused** inside that interpreter.**The environment is stripped**— your tokens and keys are not visible to executed code.- It runs in a\n**temporary directory that is deleted afterwards**. **A file with no stated examples is never executed at all.** That is decided by parsing.never runs the checked file.`--no-run`\n\n**Its dependencies are still imported**, under every mode. Deciding whether a name exists means importing the module that would answer, and importing anything runs that module's top-level code. A dependency with import-time side effects will perform them. This is the price of asking the interpreter rather than guessing from a stub, and it is why the answers are facts — but it is not \"nothing executes\", and saying so would be exactly the kind of overstatement this tool exists to catch.- Registry lookups are\n**off by default**, because the package names being looked up come from generated code.\n\nMemory is not left to the kernel. On macOS, `RLIMIT_AS`\n\n, `RLIMIT_DATA`\n\nand `RLIMIT_RSS`\n\nwere\nall measured taking a 200 MB allocation under a 64 MB cap without complaint, and a guard whose\nbehaviour depends on which machine it runs on is not a guard. So the ceiling is enforced from\nboth sides: the parent samples the whole process group (the only view that sees child processes\nor a wedged run), and the child checks its own usage far more often than the parent can afford\nto. Together they stop a 200 MB block allocated and dropped six times over in under a third of\na second.\n\n**This bounds accidents, not attackers.** Real isolation against code deliberately trying to\nescape needs a container, and this tool does not claim otherwise. If what you are checking may\nbe hostile rather than merely wrong, run hedgemony inside one.\n\nRun it yourself:\n\n```\npython3 tests/run_all.py\n```\n\nNine suites, no test framework required, no network needed. `tests/test_proof.py`\n\nargues the\ncentral claim four independent ways, and prints its working:\n\n**Ground truth comes from the interpreter, not from hedgemony.** Ten names, labelled real or\ninvented before anything runs:\n\n```\n                              flagged   not flagged\n  invented (should flag)        4             0\n  real     (should not)         0             6\n\n  recall     1.00   (4 of 4 invented names caught)\n  precision  1.00   (4 of 4 flags were genuinely invented)\n  false alarms on real names: 0 of 6\n```\n\n**The world confirms it by failing.** The flagged call is executed:\n\n```\n  hedgemony said : `console` has no attribute `table`\n  running it : AttributeError -- 'Console' object has no attribute 'table'\n```\n\n**Nothing was asked.** The same scan is repeated with the network unavailable and produces\nidentical findings — no service, endpoint or model contributed to the verdict. This is what\nseparates hedgemony from confidence scores and self-consistency checks, which ask the model that\nmade the mistake whether it made a mistake.\n\n**The samples are real.** Every file in `examples/`\n\nbeginning `generated_`\n\nis unmodified output\nfrom a small local code model, saved exactly as produced. Nothing about them was arranged to be\ncatchable.\n\n**And the two layers catch different things.** This is the result worth reading twice —\ngenerated code with **zero** invented names, which every name checker and every type checker\npasses clean:\n\n```\n  generated_with_contracts.py\n    0 fabrication(s) in 61 lines = 0.0 per 100 lines\n    2 of 5 stated example(s) did not hold\n\n    line 61  CONTRACT  `format_byte_count(1024)` was stated to give `'1.0 KB'` but gave `'0.0 MB'`\n    line 63  CONTRACT  `format_byte_count(1536)` was stated to give `'1.5 KB'` but gave `'0.0 MB'`\n```\n\nEvery name in that file exists. The function is real, the call is well formed, and it is\nwrong — it always returns megabytes and divides by the wrong constant, so one megabyte reports\nas `'1024.0 MB'`\n\n. Name checking alone exits 0 on this file. The model's own stated example is\nwhat proves the bug.\n\n`examples/generated_contracts_hold.py`\n\nis the negative control: also generated, also unmodified,\nand completely fine — four stated examples, all of which hold. A suite where every fixture\nfails cannot tell a working detector from one that flags everything.\n\n`tests/test_sandbox.py`\n\ntests every containment guard with code written to defeat that specific\nguard — infinite loops, runaway allocation, forking, oversized writes, outbound connections,\nenvironment leakage. One rule governs those payloads: each is harmless if the guard it tests\nfails.\n\n`hedgemony`\n\nis built to be called by an agent inside its own loop, on its own output, before code\nreaches a person.\n\n— how to read every class, what action each one implies, when to escalate.`AGENT.md`\n\n— a drop-in skill definition.`SKILL.md`\n\nThe short version: parse `--json`\n\n, treat `PACKAGE`\n\n/`IMPORT`\n\n/`ATTR`\n\nas *rewrite* and\n`KWARG`\n\n/`ARITY`\n\nas *fix the call*, never report a clean scan as \"correct\", and above roughly\n**3 fabrications per 100 lines** stop patching findings one at a time and go read the real API.\n\n**THIS SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\nINCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\nPURPOSE AND NONINFRINGEMENT.** See sections 15, 16 and 17 of the [LICENSE](/lovettsendit/hedgemony/blob/main/LICENSE) for the\nbinding text. The following is a plain-language summary of what that means here; the licence\ngoverns.\n\n**The sandbox is a blast-radius limiter, not a security boundary.** Checking contracts means\nrunning code. hedgemony bounds that run — separate process, CPU and memory ceilings, a process\ncap, a file-size cap, refused sockets, a stripped environment, a temporary directory that is\ndeleted — and every one of those guards is tested against code written to defeat it. None of\nthat makes it safe against code that is *deliberately* trying to escape. It is designed to keep\naccidents small, and it says so rather than claiming more.\n\n**Specifically not warranted or defended against:**\n\n- code that deliberately attempts to escape process isolation\n- code that reads or exfiltrates files your user account can already read\n- code that calls out to the operating system to do what the in-process guards refuse\n- resource exhaustion faster than the guards can observe it\n- platform behaviour outside this project's control — on macOS, for instance,\n**no kernel memory limit is enforced at all**, which is why memory is bounded in software instead\n\n**No liability is accepted for any security breach, data loss, resource exhaustion, or failure\nof the sandbox to contain anything**, whether arising from use of this software, from its\nguards behaving other than described, or from any defect in it. You run it at your own risk.\n\n**If the code you are checking may be hostile rather than merely wrong**, do not rely on these\nguards. Run hedgemony inside a container, a virtual machine, or a throwaway account with no\naccess to anything you care about. For that case use `--no-run`\n\n, which never runs the file\nand reduces hedgemony to pure static analysis:\n\n```\nhedgemony suspicious/ --no-run\n```\n\n`--no-run`\n\nis the lowest-risk mode: the checked file never runs. It is not zero execution,\nbecause the file's dependencies are still imported in order to answer questions about them.\nIf even that is too much, do not point the tool at the code.\n\n[Server Side Public License v1](/lovettsendit/hedgemony/blob/main/LICENSE) (SSPL-1.0).\n\nFree to use, modify and self-host. If you offer hedgemony to third parties as a service, the SSPL requires you to release the source of the service under the same terms.\n\nSSPL is not OSI-approved, and some organisations disallow it by policy. That is a deliberate trade for the service clause.", "url": "https://wpnews.pro/news/catch-ai-code-hallucinations-without-asking-a-model", "canonical_source": "https://github.com/lovettsendit/hedgemony", "published_at": "2026-08-31 08:47:14+00:00", "updated_at": "2026-08-31 08:52:16.659637+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-safety"], "entities": ["Hedgemony"], "alternates": {"html": "https://wpnews.pro/news/catch-ai-code-hallucinations-without-asking-a-model", "markdown": "https://wpnews.pro/news/catch-ai-code-hallucinations-without-asking-a-model.md", "text": "https://wpnews.pro/news/catch-ai-code-hallucinations-without-asking-a-model.txt", "jsonld": "https://wpnews.pro/news/catch-ai-code-hallucinations-without-asking-a-model.jsonld"}}