Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: Tooling

Tooling

Sharing with Jadpo HQ

On this page

Sharing is how Jadpo learns which signals are right: which findings people take, which they ignore and why, and what the scan missed. Jadpo HQ is the service that collects them.

Sharing is on unless it is turned off, and nothing goes unless someone runs share. Only hashes go without a person's approval; a snippet's text never does.

text
$ jadpo suggest share✓ Sent 3 hashes to Jadpo HQ    kinds  1 accepted, 1 found, 1 ignored    asked  nothing more    token  new, in app.jadpo: commit it so every copy counts as one projectSharing is on  Only hashes went. To stop sharing, add `share: false` to `suggestions { }`.

The project token

app.jadpo holds suggestions { project_token: "…" }: 32 random hex digits tied to nothing, no repository, person or machine. It is committed, so a team counts as one project, and HQ counts distinct projects without knowing which they are.

Whenever it is missing, share writes it before sending, made from the operating system's random generator. With nothing pending it writes none. share --new-token replaces it and sends nothing. A fork shares its parent's token until it does. Only 32 lower-case hex digits read as a token: any other value is type.suggestions_value_invalid, so a repository's name can't be used as one.

Turning it off

suggestions { share: false } turns sharing off for the project. JADPO_SUGGEST_SHARE=off in the environment turns it off for a whole machine or organisation; any value but on or empty turns it off, so a misspelling never sends. share: false counts in whichever file the suggestions { } is in. Neither can turn sharing back on over the other.

Either way, every form of share, --approve and --new-token included, stops at once. It sends nothing, writes nothing, keeps the pending entries and says where sharing was turned off. The scan's report drops its instruction to share. The scan, ignore and found still record entries. A suggestions { } block with a problem stops share too.

What share sends

share sends every pending entry's text hash with its kind (found, accepted or ignored), its signal and its suggestion, and the token. It does so at once, with no prompt: the agent runs it itself at the end of a suggest session, as the report's instruction says. The signal and suggestion go because HQ reassesses a signal by how often it is ignored, and most entries never send their text. Nothing else goes, not even Jadpo's version.

HQ replies with the hashes whose text it wants: those seen from enough distinct projects. The command writes the request's address and body to .jadpo/suggest_wanted, shows exactly what share --approve would send, and stops.

text
$ jadpo suggest share✓ Sent 3 hashes to Jadpo HQ    kinds  1 accepted, 1 found, 1 ignored    asked  1 snippetWhat `jadpo suggest share --approve` sends  POST https://hq.example.test/v1/suggestions/snippets  {    "snippets": [      {        "kind": "ignored",        "reason": "Basis points, not money",        "signal": "conversion",        "suggestion": "Money",        "text": "total_price * 100"      }    ]  }◆ decision  Jadpo HQ asks for 1 snippet of this project's code  next      Ask the person whether to send it, showing them the payload above.            Run `jadpo suggest share --approve` only after they say yes;            otherwise run `jadpo suggest share --decline`. (human)

Here (human) means the choice is yours: whether a snippet of your code leaves your machine.

Approving or declining

A person approves, directly or through the agent asking them in its conversation. Only then does share --approve send those bytes to that address, whatever JADPO_SUGGEST_HQ says by then. It sends the snippets, each with its kind, signal and suggestion and, for an ignored one, its reason; a found entry's reason stays local. HQ recomputes their hashes.

share --decline drops them and sends nothing. The next share replaces what waits, unsent.

text
$ jadpo suggest share --approve✓ Sent 1 snippet to Jadpo HQ$ JADPO_SUGGEST_SHARE=off jadpo suggest share✓ Sent nothing: sharing is turned off    where  JADPO_SUGGEST_SHARE=off in the environment

--json gives the counts, the hashes HQ asked for, the payload, the address it goes to, and an approval object an agent relays: the question, the payload, the two commands, and the rule that --approve is only valid after the person said yes. The command can't check that they did, so asking is the agent's job, written into the decision's next.

Unattended runs

An unattended run (--unattended, or CI set to anything) sends only hashes and skips HQ's request for text. HQ asks the next project that sends that hash, and the next share drops a request left waiting. An unattended run can't --approve (cli.suggest_arguments), and share's flags given without share are refused rather than ignored.

Afterwards

The pending file is wiped, whether HQ wanted anything or not; a send that fails keeps it. The first time a checkout sends hashes, share says so in one line, with how to turn sharing off. That first time is noted in .jadpo/suggest_shared, so nothing is written outside the project.

While HQ isn't running

No HQ address is configured by default, so a suggest session never ends in a failure while HQ doesn't exist. share says Sharing isn't available yet: Jadpo HQ isn't running. Nothing was sent; your entries are kept., sends nothing, keeps the entries and succeeds. The scan's report leaves out its instruction to share, as it does when sharing is off (The protocol).

The protocol

This is the contract the real HQ implements unchanged: HTTPS and JSON, UTF-8, under a versioned path.

  • Address. JADPO_SUGGEST_HQ in the environment. There is no default yet: the release that launches HQ, with the registry, sets the real address as the default. http:// is accepted only for a loopback host (localhost, 127.0.0.1, [::1]), which is how the tests reach the fake. Anything else must be https://, through OpenSSL, with the platform's certificate store and the host name checked. Only the jadpo tool links OpenSSL; no deployable does.

  • Headers. Content-Type: application/json, Accept: application/json, User-Agent: jadpo, without a version, and Connection: close: the client is a small HTTP/1.1 one of jadpo's own. The token in the body is the only identifier the client sends. The client logs nothing about a request: no repository name, user, machine or address.

  • POST /v1/suggestions/hashes sends at most 500 entries; share sends more in several requests.

    json
    {  "token": "5f0c2a9e7b1d4c38a6e2f9b0d7c4a1e3",  "entries": [    { "hash": "8c1d0e5a4f3b2a19", "kind": "ignored", "signal": "conversion", "suggestion": "Money" }  ]}

    hash is the text hash (16 hex digits of BLAKE3 of the snippet's text after jadpo fmt, comments removed). kind is found, accepted or ignored. signal is a signal's word (taught for a found entry). suggestion is a standard type or operation. HQ answers 200 with the entries whose text it wants, each a hash and kind it was sent, so an ignored entry's reason can be asked for without the accepted ones' texts. It names at most 20, so an approval stays readable:

    json
    { "wanted": [ { "hash": "8c1d0e5a4f3b2a19", "kind": "ignored" } ] }

    For each hash, kind, signal and suggestion, HQ counts the distinct tokens it has seen it from, so sending the same entry twice changes nothing and a retry is safe. It wants a hash's text once that count reaches its threshold and it doesn't hold the text yet; for an ignored entry, until it holds a few reasons. A hash below the threshold is never asked for.

  • POST /v1/suggestions/snippets, only after approval, carries no token: a snippet is never tied to a project.

    json
    { "snippets": [ { "kind": "ignored", "reason": "Basis points, not money", "signal": "conversion", "suggestion": "Money", "text": "total_price * 100" } ] }

    text is the snippet's text without comments, whose hash is the one HQ asked for. reason is sent only for an ignored snippet. HQ recomputes each hash, keeps the snippets it asked for and refuses the rest, answering 200 with { "received": 1, "refused": 0 }.

  • Errors. Any other status has the body { "error": { "code": "…", "message": "…" } }: 400 invalid_request (with the field), 413 too_large (a body over 1 MiB), 410 version_retired (this Jadpo is too old to share; update it), 429 rate_limited with Retry-After in seconds, and 500, 502, 503 or 504 for HQ's own trouble.

  • Timeouts and retries. 5 seconds to connect and 15 for each answer. A failed connection, a timeout, 429, and 500, 502, 503 and 504 are tried again up to three times: after half a second, 1 and 2 seconds (at most three and a half seconds of waiting), or after Retry-After when HQ gives one. A Retry-After over 60 seconds ends the attempt. Nothing else is retried. When the last try fails, share fails with suggest.share_failed, sends nothing more and keeps the pending entries for the next run.

  • What HQ keeps. For each hash, the set of tokens it came from and its kinds, signals and suggestions; and the texts and reasons it asked for, without a token. Nothing else: addresses are used only to rate-limit, in memory, and never written down.

HQ's agent

HQ runs an agent over what is shared. It turns shared patterns into signal data for the next release: new constants, words and validation texts on a type's entry, a threshold, the tests each new signal is generated with, and the release notes saying what changed.

It reassesses a signal that is ignored often across distinct projects. It reads the ignored snippets and their reasons beside the accepted ones, then narrows the signal or removes it, tested against the accepted snippets so it keeps catching what it got right. Where a pattern points at something the language lacks, it proposes a language feature for a person to decide on, and never builds one.

Not built yet: Jadpo HQ itself, which comes later, with the registry. Sharing is built against a local fake of HQ that speaks the protocol above. jadpo new writes the project token; for a project without one, share writes it.

Markdown for agents: /docs/sharing-with-jadpo-hq.md.Built from Jadpo main at a707efb.