What decides what, and how often it refuses
Benchless uses a language model. It also tells students whether a publication venue is real, which is a claim that can end a career if it is wrong. Those two facts need to be reconciled in public rather than glossed over, so this page says exactly which component decides which thing.
The rule: code decides where correctness matters; the model decides where judgement is required. A model is never permitted to rule on whether a venue is legitimate. Where the deterministic answer cannot be computed, the system refuses and says so, and the refusal rate is published below.
The map
| Function | Decided by | On failure | Record |
|---|---|---|---|
| Is this venue legitimate? | CODE | Returns UNKNOWN. Never guesses. |
public/js/venue-core.mjs |
| Which record did it match? | CODE | Shows the record and source URL, or nothing. | data/index/ — ~88k records from DOAJ, Scopus source list, IEEE, published predatory lists |
| Submission dates and timeline arithmetic | CODE | Deterministic. No model call. | public/js/ |
| What study could this student finish? | MODEL | Constrained to a forced response schema; must state why each proposal fits the stated hardware, and the most likely reason it fails. | api/scope.js · gemini-3.6-flash |
| How long will it take? | MODEL | An estimate, labelled as one. A study that cannot finish in the stated window is treated as a failed proposal, not an ambitious one. | api/scope.js |
| Whether to publish outreach | MODEL + HUMAN | Agent drafts and may decline; a person sends. See the refusal log below. | OUTREACH.md |
| Whether a payment happened | CODE | Asks Polar about the order on every page load. Never trusts the browser. | api/kit.mjs, api/polar-webhook.mjs |
| Whether to write a student's paper | NEVER | Refused by policy, not by configuration. This is the line the business does not cross. | PLAN.md, standing rule 6 |
The model is consulted in exactly one file. It proposes and explains. It does not rule on facts, and it does not touch money.
The refusal rate
A tool that declines is worse than one that works and better than one that lies. Declining is only honest if the rate is public, so here it is.
Venue verdicts
| Input class | Refused (UNKNOWN) | Rate |
|---|---|---|
| Journals, hand-checked sample | 0 of 10 | 0% |
| Flagship conference series | 16 of 20 | 80% |
| Invented spam-shaped conference names | 9 of 10 | 90% |
| Conferences, all inputs | 25 of 30 | 83% |
The system is close to useless on conference names, and it says so instead of guessing. That is not a tuning problem — there is no machine-readable registry of legitimate conference series for it to check against. I wrote up the measurement and the reproduction command in the conference gap.
node scripts/conference_gap.mjs # reproduces the table above
The benchmark carries a self-check that fails if the numbers stop being true, so this page cannot quietly go stale without the test breaking.
The outreach agent's refusals
A scheduled agent drafts outreach. Under its first rule set it ran three times and declined to publish anything on all three, because the topics it was permitted to engage with were drawn too narrowly to match any live discussion it found. I widened the permitted range and it produced one comment, which I reviewed and sent from my own account.
I am recording that rather than deleting it. Three refusals out of three runs is the system behaving correctly under a bad configuration I wrote, and the fix was a rule change made deliberately, not a retry until it complied.
Decision logging
Every agent decision writes one row. The schema was fixed before launch, because logs only accrue forward and a schema added later cannot recover what already happened.
timestamp, decision_type, model, input_hash, output, human_intervened
As of 15 August 2026 the decision_logs collection holds 5 rows, all of them self-tests written on 13 August. There has been no real user traffic to log. Publishing a governance page whose logs are empty is slightly absurd, and stating the count is the only version of it that is honest.
Revenue and related-party disclosure
Stated now, while the numbers are zero and there is no incentive to shade them.
| As of 15 August 2026 | Amount |
|---|---|
| Total revenue | $0.00 |
| — from arms-length third parties | $0.00 |
| — related-party (family, friends, agency clients) | $0.00 |
| Marketing and customer-acquisition spend | $0.00 |
| Infrastructure cost | ~$0 — free tiers, public data, a few dollars of inference |
Standing rule: I do not sell to family, friends, or clients of my agency. If that ever changes, the sale is disclosed here as related-party and excluded from any headline number I quote anywhere.
Channel of origin
Recorded at the moment of sale rather than reconstructed afterwards, because it is unrecoverable later. Every checkout carries source, referrer and product into the order metadata, and every paid order logs one structured line.
SALE {"orderId":…,"productId":…,"amount":…,"currency":…,
"customerEmail":…,"country":…,"source":…,"referrer":…}
See api/checkout.mjs and api/polar-webhook.mjs. Polar is the system of record for money; nothing here is trusted without a valid webhook signature.
How you would catch me being wrong
- Run
node scripts/conference_gap.mjs. If the refusal rates differ from this page, this page is wrong. - Read
api/scope.js. It is the only file that calls a model. If a model call appears in the venue path, the rule above is broken. - Paste a venue you know is predatory into the checker. If it returns anything other than a matched record or
UNKNOWN, tell me. - The whole thing is MIT licensed. Read it.
If I find that the honest version of this is not a business — that what people will pay for is to be published rather than to learn how — I will publish that finding here and say what I am doing instead. I have set the threshold in advance: eight people paying $200 or more by 12 November 2026.