THE HARNESS - operating guide ============================= You are reading this because you have been asked to do a large piece of work through the harness. Read it once and you can run the whole thing yourself: there is nothing to install and nobody else to involve. Everything is plain HTTP - fetch a URL or POST JSON, whichever you have - and the only thing a human had to do was register an account and paste you a token. WHAT THIS IS FOR ---------------- Work too long to hold in one context: a migration, a refactor across many files, a build-out with dozens of steps. You write a plan once, then execute one step at a time. Each step arrives with only its own text and the named outputs of the steps it depends on, so step 400 costs the same context as step 4. The harness does not trust your summary. It decides pass or fail from the evidence you submit. That is the whole point of it. STARTING A SESSION ------------------ You were given a token. Which kind it is decides your first call, and the registration page hands out the first kind, so start by assuming that: A SESSION TOKEN - the usual case. The human's registration page minted it for you. There is nothing to set up: BASE = https://eghardt.co.za/harness/api/ AN ACCOUNT KEY - the human's own credential. Trade it for a session of your own and then stop using the key: https://eghardt.co.za/harness/api//session?label=what+this+is+about The reply has a `session_token` line; that becomes your BASE as above. If your tooling refuses that one call - it happens, and it is not something you can fix by rephrasing - ask the human to mint a session on the registration page and paste it to you. Do not invent an endpoint to work around it. If you are unsure which you hold, fetch BASE/whoami: it says so. Every endpoint here answers a plain GET as well as a POST, so if the only thing you can do is fetch a URL, you can run all of this. Now find out where you stand: BASE/resume If it returns a run, continue it - do not start another. Runs belong to the ACCOUNT, not to the session, so unfinished work from an earlier conversation is yours to pick up. If there is nothing to resume, start a run when you have something worth planning. That is the whole setup. If you can POST, the same thing with a JSON body: curl -sX POST https://eghardt.co.za/harness/api//session \ -H 'Content-Type: application/json' -d '{"label":"what this is about"}' Use whichever you actually have. Neither is preferred; the URL form is listed first only because every caller can use it. If you have no token at all You cannot make one. A person has to register at https://eghardt.co.za/harness/register.php, which takes a moment and hands them a block to paste to you. Ask for it; do not try to guess a token. If a call returns 401 The token is wrong, revoked or expired. Mint a new session with the account key. If that fails too, ask the human to sign in and rotate. Other identity calls, none of which you normally need: BASE/whoami which account this token belongs to, and its limits BASE/sessions live sessions on the account BASE/revoke?session_id=N or BASE/revoke?all=1 You may also send the token as `Authorization: Bearer ` against `https://eghardt.co.za/harness/api`. Treat both the account key and the session token as secrets: anyone holding one can read and write this account's runs. THE LOOP -------- 1. BASE/resume - always start here. If a run comes back, continue it. Do not start a second one. 2. BASE/start - only if there is nothing to resume. 3. BASE/plan - submit the whole plan. It is validated. 4. BASE/step - get the next step whose dependencies have passed. 5. do exactly that step. 6. BASE/submit - with evidence. Get the next step back. 7. repeat 5-6 until the response says complete. Every response has this shape: {"ok": true, "data": { ... }, "instruction": "what to do next"} Read `instruction` every time. It changes depending on what just happened, and it tells you what the harness expects next. On failure you get `{"ok": false, "error": "..."}` with an HTTP 4xx and a message written to be acted on, not just logged. IF YOU CAN ONLY FETCH URLS -------------------------- If your only tool is web browsing or fetching - you cannot POST a JSON body - you can still run the whole harness. Every action also answers GET, with its arguments as query parameters. Build the URL, fetch it, read the reply. BASE/start?objective=Refactor+the+billing+module&environment=chat BASE/step?run_id=1 BASE/submit?run_id=1&seq=1&summary=Did+the+thing&type=exit_code&command=make+test&exit_code=0 BASE/status?run_id=1 BASE/resume You do not have to encode JSON anywhere. Evidence is assembled from flat parameters: `type`, `command`, `exit_code`, `excerpt`, `diff`, `files` (comma-separated). Plan steps are numbered prefixes: BASE/plan?run_id=1 &s1_title=Outline &s1_spec= &s1_acceptance= &s1_produces=outline &s2_title=Expand &s2_spec=... &s2_acceptance=... &s2_deps=1 &s2_produces=draft (all on one line, no spaces - shown wrapped only to be readable here) Over GET you get plain text back instead of JSON, because fetch tools tend to reflow and summarise JSON until it is unusable. Add `&format=json` if you would rather have JSON. THE ONE REAL LIMIT: URL LENGTH. A web server rejects a request line over about 8000 characters, and URL encoding roughly doubles the size of prose. **Keep each URL under ~2000 characters.** In practice that means two or three plan steps per call: 1. BASE/plan?run_id=1&s1_...&s2_...&s3_... 2. BASE/plan?run_id=1&append=1&s4_...&s5_...&s6_... 3. ... and so on Every call after the plan is small and fits easily. If a fetch returns nothing, an error page, or a 414, your URL was too long - split it and retry. Refetching a `start` or `submit` URL does not repeat the action. The server derives a replay key from the URL itself and returns the stored answer, so a link preview, a prefetch or a retry cannot burn an attempt. Changing any value makes it a genuinely new request. 1. START -------- curl -sX POST BASE/start -H 'Content-Type: application/json' -d '{ "objective": "what the whole run must achieve, in a sentence or two", "environment": "cli" }' `environment` is "cli" if you can run shell commands, "chat" if you cannot. Be honest about this. It decides how strict the gate is, and claiming cli when you have no shell only means every step fails. Returns a `run_id`. Keep it; every later call needs it. 2. PLAN ------- curl -sX POST BASE/plan -H 'Content-Type: application/json' -d '{ "run_id": 1, "steps": [ { "seq": 1, "title": "short name", "spec": "what to do, precise enough for someone with no other context to execute it. Name files, functions, exact changes. 100 characters minimum.", "acceptance": "one objectively checkable condition", "depends_on": [], "produces": "name_of_what_this_leaves_behind" } ] }' Rules the validator enforces, so save yourself a round trip: - Every step needs an `acceptance`: one condition, objectively checkable. In a cli run it should be settled by a command that exits 0. - `spec` under 100 characters is rejected as a lazy plan. - `depends_on` must name steps that exist. Cycles are rejected. - `produces` names what the step leaves behind. Steps that depend on it receive it under that name in their `inputs`. - Prefer 40 small steps over 8 large ones. One outcome, one check, each. - Never write "improve the code" or "test everything". If it is rejected you get a list of reasons. Fix all of them and resubmit. Do not delete steps to make the validation pass. Max 1000 steps per call. For a longer plan, send the first chunk, then send the rest with `"append": true`. Chunking is also the workaround if a firewall between you and the server rejects a large body. 3. GET A STEP ------------- curl -sX POST BASE/step -H 'Content-Type: application/json' -d '{"run_id":1}' curl -s BASE/step?run_id=1 # GET works too, for reads You get one step: its spec, its acceptance, and `inputs` - the outputs of the steps it depends on, under the names they declared. You do not get the rest of the plan, and you do not need it. 4. DO THE STEP -------------- Execute ONLY that step. Do not do work belonging to other steps, even if it is obvious and small and you are already in the file. The plan is a contract; doing step 7's work inside step 5 is how a run stops being trackable. 5. SUBMIT --------- curl -sX POST BASE/submit -H 'Content-Type: application/json' -d '{ "run_id": 1, "seq": 1, "summary": "what you actually did", "output": "the value named in the step\'s produces field", "evidence": { "type": "exit_code", "command": "pytest -q tests/test_auth.py", "exit_code": 0, "excerpt": "12 passed in 0.4s" }, "idempotency_key": "run1-step1-attempt1" }' THE GATE - what counts as evidence ---------------------------------- exit_code, exit_code == 0 -> pass exit_code, anything else -> fail, with your excerpt as the reason diff or file -> needs a review first: see below assertion, or no type -> in cli: FAIL. in chat: passes but flagged nothing at all -> rejected, and it does not cost an attempt The only evidence the server can settle by itself is a command exit code. Run the check. Report the real exit status. A claim that it worked is not evidence, and in a cli run it is recorded as a failure. `idempotency_key` is optional but use it: send the same key twice and you get the stored response instead of a second submission. Retries are common. 6. REVIEWED EVIDENCE (diff / file) ---------------------------------- When there is genuinely no command to run, you review your own work as a stranger would. Two calls: curl -sX POST BASE/verify -H 'Content-Type: application/json' -d '{ "run_id": 1, "seq": 4, "evidence": {"type":"diff","diff":"...","files":["src/auth.php"]} }' You get the acceptance criterion back with an instruction to judge it as an independent reviewer. Then submit with the verdict: ... "evidence": , "verdict": "pass" | "fail", "verdict_reason": "why" The evidence is fingerprinted at verify time. If what you submit is not byte-identical to what you reviewed, the verdict is discarded and the submission rejected. Review one thing and submit another and you get nothing. WHEN A STEP FAILS ----------------- 1st and 2nd failure -> the same step comes back with the reason. Change the approach; do not repeat what failed. 3rd failure -> you are offered a replan. after 2 replans -> the step is blocked, and so is everything downstream of it. The run stops for a human. curl -sX POST BASE/replan -H 'Content-Type: application/json' -d '{ "run_id": 1, "seq": 4, "spec": "a different approach to the same end" }' A replan may change the spec. It may NOT change the acceptance criterion. That is compared on arrival and a changed one is refused. If the criterion genuinely cannot be met, say so: {"run_id":1, "seq":4, "escalate":true, "note":"what is actually in the way"} Escalating is a legitimate outcome. Quietly weakening a criterion until it passes is not, and it is the one thing this system exists to prevent. FIXING A STEP YOU HAVE NOT STARTED ---------------------------------- A plan is written before you know what you will find. When step 20 teaches you that step 35 is wrong, fix it rather than letting it fail: curl -sX POST BASE/amend -H 'Content-Type: application/json' -d '{ "run_id": 1, "seq": 35, "spec": "the revised instruction", "acceptance": "a revised criterion" }' Unlike a replan this MAY change the acceptance criterion, because nothing has been judged against it yet. You can also change title, produces and depends_on; a rewiring that would close a dependency loop is refused. The line is whether the step has been attempted, not whether it has been handed to you. A step you were just given, with nothing submitted against it, is still amendable. Submit once and it is not: from then on it is a replan, and the criterion is frozen. OTHER CALLS ----------- POST BASE/status {"run_id":1} counts, blocked steps, last failure POST BASE/resume {} the carry-on button POST BASE/amend {...} revise a step you have not started POST BASE/abandon {"run_id":1} give up on a run, free the slot `status` also reports how many results passed on your word alone rather than on machine evidence. In a chat run that number is the honest measure of how much of the run is actually confirmed. THE ONLY ENDPOINTS THAT EXIST ----------------------------- resume start plan step submit verify replan amend status abandon session sessions whoami revoke That is the complete list. There is no /claim, no /task, no /next, no /complete. If a call 404s, you invented it: re-read this list rather than trying variations. An unknown action returns the list too, so you are never guessing. THE FIVE RULES -------------- 1. Start with resume. Never run two runs at once. 2. Execute only the step you were given. 3. Evidence, not assurances. Run the check; report the real exit code. 4. Submit failures honestly. A submitted failure costs one attempt; a fake pass costs the whole run. 5. Never move the goalpost. The acceptance criterion is frozen. Machine-readable schema for every endpoint: https://eghardt.co.za/harness/openapi.json