connexion.menotes

How a run is produced, what the raw files are, and how you may re-use them

Everything on this site is one procedure run over and over. This page is that procedure, the files it leaves behind, and what you are allowed to do with them.

What one board is

A board is a single buying question asked 44 different ways. One board is the exception and says so on its own page: /c/aeo/ was asked 24 ways.

The two kinds of question

The 44 phrasings of a board are not all the same kind of question, and the product pages at /check/ print the split. The rule is mechanical, it is the same on every board, and it uses nothing at all about any product: a question is first-person if it opens with “We”, “I”, “Our” or “My” — the buyer describing their own company and asking what to buy — and every other question is counted as a category or feature question. That is the whole classifier. It is applied to the phrasing field of the board's own published mentions-runA.jsonl, so you can rerun it yourself:

# the split on one board, for one product, from the file that board links
curl -s https://connexion.me/c/lpm/mentions-runA.jsonl \
  | python3 -c 'import sys,json,re,collections
n=collections.Counter(); c=collections.Counter()
for l in sys.stdin:
    r=json.loads(l)
    fam="first-person" if re.match(r"\s*(we|i|our|my)\b",r["phrasing"],re.I) else "category"
    n[fam]+=1
    for name in set(r["names"]): c[fam,name]+=1
for fam in ("first-person","category"):
    print(fam, n[fam], "answers,", c[fam,"PracticePanther"], "named PracticePanther")'

first-person 14 answers, 2 named PracticePanther
category 74 answers, 39 named PracticePanther

That counts one spelling. A product page counts the same way and then applies the board's own name fold on top — the one described above, which merges Workable ATS into Workable — so for a product the engines spell more than one way, the page's figure is the higher one and the difference is the spellings this snippet did not add up.

A product page prints both figures whatever they say. It points out a difference between the two only when three conditions all hold, and they are floors rather than thresholds we tuned: each family holds at least 10 answers, the product's rate in the weaker family is at most half its rate in the stronger one, and the weaker family was large enough to have named that product at least 5 times at its own overall rate. A product whose two rates are close gets the numbers and no sentence about them. The rule is symmetric — it is not searching for the reading least flattering to whoever is on the page — and the two counts always add back to the mention count printed above them, which is checked at build time against ranking.json before any page is written.

The line at the top of a product page

Each product page at /check/ opens with one sentence about that product, and one comparison decides which sentence it is: the answers that named the product, against the answers that did not — whichever count is larger is what the line says. Equal counts get their own sentence and are not broken either way. That is the whole rule. It uses two integers from the board's own published ranking.json, mentions and answers, it is the same on every page, and there is no floor, no weighting and nothing tuned per product or per board:

# the line every product page on one board would open with
curl -s https://connexion.me/c/inventory/ranking.json \
  | python3 -c 'import sys,json
r=json.load(sys.stdin); a=r["answers"]
for v in r["vendors"]:
    m=v["mentions"]; n=a-m
    say=("named in %d of %d" % (m,a)) if m>n else ("absent from %d of %d" % (n,a)) if n>m \
        else ("named in %d of %d and absent from the other %d" % (m,a,n))
    print(v["vendor"], "--", say)' | head -4

Zoho Inventory -- named in 61 of 88
Katana -- absent from 53 of 88
Cin7 -- absent from 55 of 88
Sortly -- absent from 59 of 88

The sentence states a count and never a label. A product named in 41 of 88 answers is absent from 47 of them and the page says that; it does not say invisible, or losing, or at risk, because none of those is something this measurement produced. Rank, share and the per-question split are printed under the line and are not part of the comparison.

The second run

Every category is measured twice, from scratch — run A and run B, the same 44 questions asked again. Both runs are published, not only the one the table is drawn from, and every board prints its own run A/run B top-ten overlap.

A category built from a request is only published behind four checks that are commands rather than judgement calls: at least 12 distinct products named, top-ten overlap of at least 8 out of 10 between the two runs, no product above 95% share, and no top-five product that was named in only one phrasing. The last two are the ones that catch a wrong board rather than a thin one — a single name in nearly every answer is usually an extractor that has collapsed a category onto one string, and a top-five product carried by one phrasing is usually a question that leaked a brand name.

The files beside every board

A board lives at https://connexion.me/c/<slug>/. Five files sit at that same path, and they are the entire measurement — the page is only a rendering of them.

fileone line is
answers-runA.jsonl
answers-runB.jsonl
one engine answer, verbatimengine, model, index, phrasing, text, error
mentions-runA.jsonl
mentions-runB.jsonl
the products extracted from one answer — engine, index, phrasing, names
ranking.json the board itself — category, answers, phrasings, engines, engine_labels, answers_by_engine, partial, and vendors, each carrying vendor, key, mentions, share, by_engine, phrasings_named_in and rank

The .jsonl files are NDJSON: one JSON object per line, no enclosing array, so they stream and you never need to hold one in memory. index is the phrasing number, 0 to 43, and it is the join key — the same index and engine in the answers file and the mentions file are the same question put to the same engine.

Reading them

Nothing here needs a client, a key or an account.

curl -s https://connexion.me/c/hotel/ranking.json | python3 -m json.tool | head

# every product the extractor took out of run A, most-named first
curl -s https://connexion.me/c/hotel/mentions-runA.jsonl \
  | python3 -c 'import sys,json,collections; c=collections.Counter()
[c.update(set(json.loads(l)["names"])) for l in sys.stdin]
[print(n,v) for n,v in c.most_common(15)]'

# the verbatim answer behind one cell: phrasing 0, as ChatGPT returned it
curl -s https://connexion.me/c/hotel/answers-runA.jsonl \
  | python3 -c 'import sys,json
[print(json.loads(l)["text"]) for l in sys.stdin
 if json.loads(l)["index"]==0 and json.loads(l)["engine"]=="chatgpt"]'

If a count on a board disagrees with what you get out of these files, the files are right and we want to hear about it: lana@connexion.me.

Re-use and attribution

You may re-use the raw answers. In the words it was granted in, on 2026-08-08:

…you have the right to re-use the raw answers in later work with attribution, including inside your open-source tooling, with no restriction on scope, medium or duration.

That is the standing grant, and it is not specific to the person it was first written to. Concretely, and with nothing held back for later:

Two things this grant does not cover, because they are not ours to give: the engines' own terms for their outputs, which are between you and them, and any file we have handed someone privately at an unlisted URL — that stays under whatever was agreed when it was handed over. Everything published beside a board is covered.

What this is not

It is a measurement of what answer engines said, on a stated date, in response to stated questions. It is not a quality ranking, not a review, and not advice on what to buy. A product can be excellent and rank low here; that gap is the interesting part, not a defect of the product.