If you understood it, it was badly explained
Part 1 — the robot gives its real name
A robot dressed up as a browser ends up blocked, and it deserves to be. Ours announces who it is and where to find out more. One line, sent to every site it visits.
code-nomi-nomi-veille/1.0 (+https://matourdecontrole.fr ; news-watching robot)A name, and an address to look it up. Nothing else. No e-mail shouted at every site, no fake “Mozilla”.
No e-mail address shouted at every site, no fake “Mozilla”. An identified robot is accepted; a masked one gets banned. It really is that simple.
Part 2 — the subject gatekeeper
An article's title is free: we already have it from the news feed. So we decide before fetching the full text. Two word lists kept inside, and one rule.
The rule fits in one sentence: keep a title that touches a followed subject, drop one that touches none, and always write down the reason in the logbook. The word lists stay inside: publishing them would hand out the recipe for crafting a title that fools the sorting.
The detail that matters: the decision always returns a reason. Never a silent refusal. The robot's logbook keeps the trace: “IGNORED — no subject of interest in the title — TITLE: …”. So a decision can be challenged, and the list corrected.
Part 3 — the queue that does not forget
A site that answers badly once may answer well tomorrow. The first version struck failed articles off the list: they never came back. That is marking a lost parcel as delivered. Now a failure is a record: three attempts, then “abandoned”, and that abandonment is written down in plain sight.
Part 4 — the private page
The result lives on an internal page. In Odoo, the door holds with one word in the route declaration:
@http.route("/tour/cockpit/veille", type="http", auth="user")
def page(self, **kw):
...
auth="user" means: nobody but a signed-in user. Better still,
our tower does not say “please log in”, it answers “this page does not
exist”. A stranger does not even learn that the page is there. The test that
proves it compares against a page already in service: both must answer the
same way.
Part 5 — the page lets an agent read it
This is the new part. Since 2026, a browser lets a page put its own functions on the table as named tools. The agent no longer guesses where to click: it asks.
modelContext.registerTool({
name: "nouvelles_ia",
description: "Returns the articles kept by the news-watching robot…",
inputSchema: {
type: "object",
properties: {
sujet: { type: "string" },
source: { type: "string" },
combien: { type: "integer" },
},
},
execute: async ({ sujet, source, combien }) => ({ … }),
});
Three things make it useful. Every article returned carries its own address, so the agent can check. A word with no match returns an empty list — never a patched-together answer. And the logic is pure: it works on a list handed to it, touching neither disk nor screen, so it can be tested outside the browser.
The trap that cost half an hour
After going live, the page worked one time in eight. The stylesheet answered “never heard of it”, then “here you go”, with no visible logic. The cause: the server runs four workers — four copies sharing the visitors. Each keeps in memory the list of files it knew at start-up. A module added later only exists for those that reloaded.
The diagnosis did not come from a hunch: we asked for the same file eight times in a row. Seven refusals, one success. A number settles what an opinion only debates.