From cdb2c1756cbffcdf30e4edbe45294f4b9848c052 Mon Sep 17 00:00:00 2001 From: Mario Tarosso Date: Mon, 26 Oct 2026 09:49:02 +0000 Subject: [PATCH] Tell the agent how to finish the map-link lines setup leaves Setup can end with "Some attacks are only reported, not blocked yet" and "Could not confirm whether rules for this app are ready". Both mean the map setup uploaded could not be linked to the guard, and nothing told the agent how to finish that, so it passed the lines on to the person. AGENT-INSTALL.md now says to run setup --verbose, read the reason on the "could not bind this map" line, and fix it, with the fix for each reason, then rerun setup until the line is gone. It also says how to answer "is my app protected?": installed, blocking only once the site is connected to an account that includes protection, and live only after publishing again. The setup hints point at --verbose, and the readiness line says it follows the link when there is none. Co-Authored-By: Claude Opus 5.5 (1M context) --- AGENT-INSTALL.md | 10 ++++++++++ src/cli.ts | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/AGENT-INSTALL.md b/AGENT-INSTALL.md index e79ad26..ddf202b 100644 --- a/AGENT-INSTALL.md +++ b/AGENT-INSTALL.md @@ -137,6 +137,7 @@ The person who asked for this usually did not write the project and has never in - **Say what changes and what does not.** Nothing visitors see changes. A couple of small config files are added, and a line that lets Patchstack recognise the site. - **Hand over the link like a next step, not a log line.** When `setup` prints the dashboard link, say what it is for: "Open this link and sign in — that's what connects the site to your Patchstack account so you can see the reports." - **Avoid these words with the user**: lockfile, dependency, manifest, npm, registry, UUID, environment variable, build artifact, package manager. Use them in commands, not in sentences addressed to the person. +- **Say plainly whether the app is protected.** If they ask, the answer has three parts, and all three matter: the protection is installed in the project; until the site is connected to a Patchstack account that includes protection, Patchstack's rules report attacks without blocking them; and the live app has none of it until they publish again. Do not call the app protected before all three are true. - **Report a failure in terms of what they can do**, not what errored. If a step is genuinely unavailable on this platform, say which one and what it means for them, rather than reporting success. This is a request, not a mechanism: nothing in the install depends on it. Do it anyway — the wait is the only moment where the person is paying full attention. @@ -270,6 +271,15 @@ Only when the person asks you to publish the site. Start with `npx @patchstack/c **A `✘` line under `To do` is yours to finish, not a result to report.** `setup` applies what it can apply safely and prints the exact edit (or the command that prints it) for anything it would have had to overwrite user code to do. They are: moving `@patchstack/connect` out of `devDependencies`, the widget tag in a root layout `setup` could not edit, the production marker on a server-rendered root, and wiring a generic guard into the server entry. The last three are steps 3 and 4 of "Manual setup" below; after the guard one, `npx @patchstack/connect protect --check` must exit 0. + **Two more `To do` lines are about linking the app's map to the guard. Finish them too; do not pass them on.** + + - `Some attacks are only reported, not blocked yet` (`Map is not linked to runtime protection` with `--verbose`): setup uploaded the map of the app's inputs but could not record its identity in the guard's rules file, so rules Patchstack writes for those inputs can only report. Run `npx @patchstack/connect setup --verbose` and find the line `patchstack: could not bind this map to the runtime guard — `. Fix that reason, then run `setup` again until the line is gone: + - `no guard rules file in this project`: the guard is not installed. Run `npx @patchstack/connect protect`; `npx @patchstack/connect protect --check` must then exit 0. + - `more than one candidate guard rules file (…)`: two Patchstack guards are installed, each with its own rules file. Keep the one `protect --check` reports as wired. Remove the other guard file and its rules file only after checking that nothing in the app imports that guard; if something does, tell the person instead of removing it. + - `… is not valid JSON` or `… is not a guard rules bundle`: the rules file was edited by hand. Restore it from version control, or delete it and run `npx @patchstack/connect protect`, which writes a fresh one. + - `… could not be read` or `… could not be written`: a file permission problem in the project; fix it and rerun. + - `Could not confirm whether rules for this app are ready` (`Rules readiness for this map is not confirmed`): follows from the line above, and goes away when you rerun `setup` after fixing it. `Rules for this app are still being prepared` needs nothing from you: the running app picks the rules up when they are ready. + **Read the report.** `setup` and `scan` print, in order: `To do` (what is left, each with the one thing to do); the four steps, each marked `✔` (done) or `✘` (not yet) — `Patchstack installed`, `Connect your Patchstack account`, `Packages checked` on this computer, in your preview or in your live build, and `Deploy to protect your live app`; the one next step in a box, with its link or command; what Patchstack found, as vulnerabilities by severity and outdated packages, once Patchstack has counted them; and a `More` list of follow-up commands. `guide` prints the same without the findings. A line marked `ℹ` is something to know, not something to do — for example that blocking attacks needs a server and none was found. What a run did is printed only with `--verbose`. A part the project cannot carry (no build script, no request path for runtime protection) is simply not listed. The CLI does not store whether the site has an owner, so the connect step stays `✘` until a scan hears otherwise from Patchstack; do not report the site as connected from that line alone, and do not report it as unconnected once the person has signed in. **The widget.** `setup --widget` adds it and saves `"widget": true` in `.patchstackrc.json`. Without `--widget`, `setup` leaves it out of a project it sets up for the first time, saves `"widget": false`, and lists `npx @patchstack/connect widget` under `More`. A project that already has a site keeps what it has. When the request you were given asks for the widget, run `setup --widget` (or `widget` after setup) without asking the person again; otherwise offer it in the message you end on. Leave a `"widget"` value you did not write as it is. diff --git a/src/cli.ts b/src/cli.ts index 9bd02a3..ce21405 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1581,7 +1581,7 @@ async function runSetup(args: ParsedArgs): Promise { const uploaded = synced.map.upload; if (uploaded?.result === 'stored' || uploaded?.result === 'unchanged') { report.done.push(`Attack-surface map uploaded (revision ${uploaded.revision}; ${synced.map.endpoints ?? 0} detected entry points)`); - if (!synced.map.buildId && !noServer) report.missing.push({ key: 'map-binding', text: 'Map is not linked to runtime protection', hint: ['Finish the reported protection integration, then rerun setup. Rules for specific inputs can only detect, not block, until linked.'], plain: { text: 'Some attacks are only reported, not blocked yet', hint: ['Finish adding protection, then run setup again.'] } }); + if (!synced.map.buildId && !noServer) report.missing.push({ key: 'map-binding', text: 'Map is not linked to runtime protection', hint: ['Run setup --verbose for the reason the map could not be linked, fix it, then rerun setup. Rules for specific inputs can only detect, not block, until linked.'], plain: { text: 'Some attacks are only reported, not blocked yet', hint: ['Run `npx @patchstack/connect setup --verbose`, fix the reason it gives, then run setup again.'] } }); } else { report.missing.push({ key: 'map-upload', text: 'Attack-surface map was not uploaded', hint: [synced.map.error ?? uploaded?.message ?? 'Check connectivity and rerun setup.'], plain: { text: 'Could not send your app\'s layout to Patchstack', hint: ['Check your connection and run setup again.'] } }); } @@ -1594,7 +1594,9 @@ async function runSetup(args: ParsedArgs): Promise { report.done.push(`Rules for this map are ready${synced.rules.count === 0 ? ' (no applicable rules)' : ''}`); report.confirmed = [`Rules for this app are ready${synced.rules.count === 0 ? ' (none currently apply)' : ''}. This does not confirm active blocking.`]; } else { - report.missing.push({ key: 'map-rules', text: synced.rules.mapRules === 'pending' ? 'Rules for this map are still being prepared' : 'Rules readiness for this map is not confirmed', hint: ['The running guard will refresh automatically. Broad and hardening rules do not depend on map generation.'], plain: { text: synced.rules.mapRules === 'pending' ? 'Rules for this app are still being prepared' : 'Could not confirm whether rules for this app are ready', hint: ['Your running app will check again automatically.'] } }); + // Readiness is judged for the linked map, so without a link it cannot be confirmed: fixing the link is the step. + const unlinked = synced.rules.mapRules !== 'pending' && !synced.map.buildId; + report.missing.push({ key: 'map-rules', text: synced.rules.mapRules === 'pending' ? 'Rules for this map are still being prepared' : 'Rules readiness for this map is not confirmed', hint: [unlinked ? 'Readiness follows the map link: fix the link above, then rerun setup.' : 'The running guard will refresh automatically. Broad and hardening rules do not depend on map generation.'], plain: { text: synced.rules.mapRules === 'pending' ? 'Rules for this app are still being prepared' : 'Could not confirm whether rules for this app are ready', hint: [unlinked ? 'This clears once the line above is fixed and setup runs again.' : 'Your running app will check again automatically.'] } }); } } } else {