Skip to content
megapromotingLet's talk

Expertise · Automated content

A flow that looks for the day’s topics on its own, writes the article, translates it into nineteen languages and publishes it — but only after passing a verification gate which, when it cannot verify, publishes nothing.

The motor is written, complete, and sits in the repository of this site itself: eight stages, 1,706 lines, a system timer that starts daily at 00:30 UTC. The part that matters is not generation, but the gate: five local checks, ten forbidden expressions and an extra check that looks for client names without consent, invented figures and awards we do not have. If the gate cannot run, it is not published.

Already built“delivered”, because the work exists as files you can open, not as a description: `scripts/content-engine/` in the megapromoting.com repository, 17 files, 1,706 lines of TypeScript, systemd unit with timer, eight linked stages and run logs. With a reservation we state ourselves, not one you discover: **the only complete run in the logs did not publish any article.** The gate stopped all three — two because the check could not run at all, the third because of a missing field in the header. `publish` remained empty, IndexNow received zero addresses. The run was local, on the workstation, and lasted two and a half hours, meaning over the 60-minute limit set in the systemd unit — so on the server it would have been stopped. We cannot confirm from the outside that the engine runs today on the server, and we do not claim it.

The automation that writes and publishes on its own is where it is easiest to promise what cannot be done. A model writes a thousand convincing words about anything, including things that did not happen, and it writes them just as fluently. If nothing stands between the model and the publish button, you have built a machine for putting mistakes on the internet, in twenty languages, every night. The whole work is in what stands between.

Our flow has eight stages. It gathers topics from open sources, gives them a score from 0 to 100 for how suitable they are, discards everything below 50, selects the first N with a diversity rule so that ten articles about the same thing do not come out, writes the article in Romanian, translates it into nineteen languages, sends it through the gate, and only then publishes. Each stage writes its cost and result in a log, so a run can be reconstructed afterwards.

The gate has two layers. The first is local and costs nothing: the header must exist and have a title, description and keywords; the text must pass 1,200 words; it must have at least three subheadings, at least three internal links and a questions section with at least three pairs; and it must not contain any of the ten marketing expressions we put on the blacklist. The second layer is a model that looks for something else: a client name without proof of consent, invented statistics, false statements about what we can do, and awards we do not have — the list of the real ones is written in the verification, and anything else is flagged.

There is human approval in the chain, and we say it as an argument, not as an excuse. Articles that fail the hard checks are discarded, not published. Those that pass the hard checks but receive flags from the second check are kept marked, for a person to look at. And if the check cannot run — because the model service does not respond, for example — the article is treated as failed. Fail-closed, not fail-open. That is what happened in the only complete run in the logs, and that is why nothing was published.

What it covers

The work, by component

Collect topics from open sources, in parallel, without breaking if one goes silent

Eight collectors start at once: Hacker News, Reddit, Product Hunt, GitHub trending on three cuts, a set of fourteen RSS feeds and Indie Hackers. Each has its own error handling, so a down source returns an empty list and does not stop the run. The results are unified, duplicates are removed by identifier, then a cap of eight topics per source is applied, sorted by circulation speed — so a noisy source does not flood the next stage.

Note and discard, before spending on writing

Each topic gets a score from 0 to 100 for how suitable it is for what we do, plus the proposed angle, target industry and target service. Anything below 50 is discarded. The stage runs on a cheap model, with four threads in parallel, precisely because it is the stage that decides what gets spent on next. From the final choice, one more layer is taken out: the first pass takes one topic per industry, so that ten variations on the same thread do not come out.

Write the article with a fixed structure, not a free one

The generator receives a strict structure: 1,500–2,200 words in Romanian, YAML header with a title of no more than 60 characters and a 155-character description, an H1 and four to six H2s, between three and five internal links chosen from a list of addresses that exist on the site, a highlighted quote, a question section compatible with the FAQPage schema and a final call to action. It runs with three threads in parallel, with a five-minute budget per article and an output limit of 5,000 tokens.

Translate into nineteen languages, with a glossary that is not translated

The source is Romanian; the targets are nineteen, from English, Russian and Ukrainian to Arabic, Hebrew, Hindi, Japanese and simplified Chinese — including two right-to-left scripts. The rules are explicit in the prompt: the header keeps its structure, internal link addresses are not rewritten and are not localised, numbers, units and dates are preserved, and brand terms from the glossary remain untouched. Translation runs with only two threads in parallel, deliberately — the model gate limits throughput under higher load.

Hard gate: five conditions and ten forbidden expressions

Local, with no paid call: the header must exist and have a title, description and keywords; the text must exceed 1,200 words, counted after removing code blocks and tags; at least three level-two subheadings; at least three internal links to sections that exist on the site; a question section with at least three pairs. Plus the blacklist: “game-changing”, “cutting-edge”, “world-class”, “revolutionize”, “synergy” and five more. A single occurrence fails the article.

The second gate looks for exactly what a counter cannot see

A second model, with low temperature and JSON response, receives the article and looks for five things: client names without proof that consent was given, hallucinated statistics such as a percentage availability without measurement, inaccurate claims about what we can do, brochure language, and claims about third-party tools that would give the impression we own them, when we only integrate them. It also has the list of awards we truly have — any award outside the list is flagged.

When the check cannot run, the article is treated as failed

The second check has a 90-second budget, explicitly so as not to block the run. If it times out or if the model service does not respond, the error is caught and turned into a negative verdict, not ignored. The consequence is important and is the one we like most: an article does not go public because the guard was absent. It has already happened, in the only complete run in the logs — two fully written articles were not published because the check could not be done.

Publishing has one condition and three steps

Publishing is not even called if zero articles have passed the gate. When it is called: it adds only the content directory, stops if there is nothing to commit, makes a commit with the date, the number of articles and the number of languages, and tries to push. If the push fails, the commit stays local and the run continues — work is not lost and it is not claimed that it was published. Search engine notification is done only if the push succeeded.

Each run leaves its account

At the end, a daily report is written: how many topics were gathered, how many passed the note, how many were selected, how many were written, how many translations came out, how many passed the gate, what was committed and what was pushed, what the search engines answered, how long it took, and the cost broken down by model, with the number of calls and the input and output tokens. The report is a file, not a dashboard — it can be read, archived, and compared.

What it looks like

The path, step by step.

01

We establish what it is allowed to write and what it is not

Before any code: the list of topics we cover, the list of those we do not touch, the tone, and the blacklist of expressions. Here, the list of statements the engine is not allowed to make about you is also written — clients, figures, awards, comparisons. We deliver: the written rules, in the form that goes directly into verification, not as a separate document that is ignored.

02

We start only gathering and noting, without writing anything

It runs in dry mode: topics are gathered, noted, the first ones are selected, the report is written, and it exits before generation. It is cheap — in such a run from our logs there were 24 calls, all on the small model, with a measured cost of approximately one cent in total. It shows exactly what the engine would have chosen, day by day, without any text existing. We deliver: the dry-run reports and the agreement on what it selects.

03

We write and verify, but do not publish

Generation and the gate are started, with publication turned off. Here the thresholds are calibrated: if all articles fail on length, that is something else to adjust than if they fail on internal links. Here you also see what the second verification signals, which is the most instructive part. We deliver: the generated articles, the complete verdicts with the reason for each rejection, and the adjusted thresholds.

04

We open publication, with the gate in place

The system timer and publication are started. The time is chosen outside the peak, with a random delay so the runs do not overlap; in our case, 00:30 UTC with up to 30 minutes delay, and with recovery if the server was stopped at that time. The time limit per run is set. We deliver: the service unit and the timer, the logs, and the shutdown procedure.

05

We add the languages, one by one

Translation is the stage with the highest consumption and the easiest to get out of control. Languages are added on priority lanes, not all at once, and what comes out in each is checked — especially non-Latin scripts, where the rule is that brand terms must remain in the Latin alphabet. We deliver: the glossary, the languages started, and the measured cost per language from the run log.

Opt etape1adunare din optparallel collectors,fiecare cu prindereproprie de eroare2score 0–100, discardsub 503alegere cudiversitate peindustrie4writing in Romanian,1.500–2.200 decuvinte5translation into 19 languagescu glosar neatins6the hard gate, fiveconditions and tenexpresii interzise7poarta a doua, caresearches for clients, numbersand invented prizes8publishing, only ifat least one has passedWhen the gate cannot run, nothing is published.
Opt etape

The data

What we touch, where it lives, and how long it stays

The questions anyone with a data protection officer would ask — asked here before they do.

What enters
Titles, addresses and short fragments from public sources — open APIs and RSS feeds. No personal data is collected, private accounts are not read and nothing is accessed after authentication. Each topic keeps the source, channel and moment of discovery, so it can be tracked where an article started from.
Where the texts are kept
As MDX files in the site code repository, under `content/daily/{language}/{date}/{slug}.mdx`. Not in a separate database. The consequence is that the complete history is the git history: it can be seen who wrote what, when, and it can be reverted with a command. Each article has its own front matter, from which the public page composes its structured data.
Where the keys are kept
In an environment file read by the systemd unit at startup, outside the code repository. The environment loader does not overwrite a variable already defined in the process, so the runtime configuration can be replaced without touching the file. Calls to models go through our internal gate, addressed locally on the server.
Logs and how long they are kept
One JSON file per day in the engine's logs directory, plus standard output and standard error written separately in `/var/log`, through the systemd unit. The daily log contains the gate verdicts per article, with the exact reason for rejection and the number of words counted. It does not contain the article text — that is in the repository.
What it does not touch
It does not touch your social media accounts, does not send emails, does not write in the CRM. Publishing means a commit in the site repository and a notification to two search engines. If you want publishing on other channels, that is discussed separately, with the same rule: gate first, not after.

A case

The night the engine wrote three articles and published none

The situation

A full run, with all stages started: gathering from open sources, scoring, selection, writing in Romanian, translation, gate, publishing. The target was intentionally small, three articles, to show the full chain without costing much.

What we built

The gathering brought 24 unique topics after removing duplicates and capping per source. The scoring kept 18 and discarded 6 below the threshold of 50. The diversity rule chose 3, from different industries. The generator wrote 3 articles in Romanian. The translator produced 57 files. Then the gate came in.

What came out

No article passed. Two failed because the second check could not be done at all — the model service did not respond within the 90-second budget, and the error was treated as a negative verdict, not ignored. The third failed on a single condition: it was missing the keywords field from the header, although it had 2,478 words, i.e. twice the minimum threshold. Since no article passed, publishing was not even called: the commit stayed empty, the push was not done, the search engines received zero addresses. The 57 translations stayed on disk, unused.

What the case does not say

The run was on the workstation, not on the server — this can be seen from the paths in the log — and lasted two and a half hours, over the 60-minute limit set in the systemd unit. So it shows two things at once: that the gate holds, and that the daily batch is too large for the configured time limit. Both are real and both are worth saying.

Questions

What people ask us before they call

Is it published without a human seeing the text?

Yes, if you choose it that way — and no, if you do not. The engine can run fully automatically, and in this mode the article that passes both layers of the gate is published without anyone having read it. But the gate is not decorative: the article fails for a missing header field, for 1,199 words instead of 1,200, or for a single phrase from the blacklist. And if you prefer each article to pass through a pair of eyes, automatic publishing is stopped and everything remains up to the review stage, with the flags visible. Our recommendation at the start is the second option, until the flags become boring.

What happens when the model gets a fact wrong?

It depends on what kind of fact, and it is worth saying honestly that not all of them are caught. Caught: a client name used without proof of consent, an invented statistic such as an availability percentage without measurement, a claim that would suggest we own a tool that we only integrate, an award that is not on the list of real ones. Not caught: a wrong calendar date from an external news item, or a technical claim about a third-party product that sounds plausible. For those there is no other guard than a human, and that is why each article keeps the sources it started from in the header — verification remains possible after publication, and the correction is a commit.

How is the flow stopped?

Three ways, in order of speed. Stopping the system timer — nothing starts tomorrow. Emptying the variable that holds the publishing key — the engine writes and verifies, but the search engine notification is skipped with a warning in the log. And a dry run, which stops after topic selection and generates nothing. In addition, an individual run has a time limit in the service unit, with a soft signal first and forced stop after.

Is the engine running now, on the server?

We cannot confirm this from the outside and we will not claim it. What we can show is the code, the service unit, the timer, the installation procedure and the run logs we have. The only complete run in the logs was on a workstation, not on a server — this is visible from the file paths — and it lasted two and a half hours, that is, over the 60-minute limit in the systemd unit. If you are interested in the current state of our installation, ask and we will check together. We prefer that question over a “yes” that turns out to be false.

How many articles has it published so far?

From the logs we have: zero. The full run gathered 24 topics, kept 18 after scoring, chose 3, wrote 3 articles and 57 translations — and the gate stopped all three. Two because the second check could not run at all, one because of a missing field in the header, even though it had 2,478 words, so it had long passed the length threshold. Publishing was not called, search engines received no address. We could choose not to tell you this. We are saying it because this is exactly the demonstration the service needs: the gate really stops.

Isn’t this “AI content” that harms search ranking?

The right question is not who wrote it, but whether the text answers something someone is actually looking for and whether it is verifiable. That is why the thresholds in the gate are set on things that correlate with usefulness, not style: minimum length, heading structure, internal links to pages that exist, a questions section. And that is why the blacklist contains precisely the expressions that signal text written to fill space. What we do not promise is a specific position in results — that depends on many things that are not up to us.

How much does a run cost?

The cost is measured, not estimated: each call to the model counts its input and output tokens, and the daily report gives the total broken down by model, with the number of calls. In the dry run from our logs: 24 calls on the cheap model. In the full run with three articles and 57 translations: 32 calls, of which 3 were on the expensive model — which made up most of the cost. The model used at each stage is an environment variable, so a stage can be moved down to a cheaper model without touching the code. The cash figure depends on the models chosen and their rates at the time of the run.

In which languages does it write and what happens with brand terms?

The source is Romanian, the targets are nineteen — English, Russian, Ukrainian, Polish, German, Italian, French, Spanish, Dutch, Arabic, Turkish, Hebrew, Hindi, Japanese, Korean, Indonesian, Simplified Chinese, Brazilian Portuguese and Czech. Each language has the reason it is there written in the configuration. Brand terms and technical terms are kept in a glossary and remain untranslated, including in Arabic, Hebrew and Asian scripts, where the rule is explicit: they are kept in the Latin alphabet. The addresses of internal links are never localised.

What is not ready from what you described?

Three things, said now so you do not discover them on your own. The in-house documentation has got ahead of the code in places — the comment at the head of the scoring stage mentions a model that is no longer the one being called, and the source register describes a larger set of RSS feeds than the one in the list. Second: two of the sources entered in the register are marked active, but their collector is not called in the function that gathers everything, so they contribute nothing today. Third: the full run in the logs exceeded the service unit time limit, which means that on the server, with the current settings, it would have been stopped halfway — the limit must be raised or the daily batch reduced before automatic launch.

What the statements above rest on (26 sources)

26 of them are code and files from our repositories. We don't publish their name or line: together, on a single page, they would describe too precisely how systems that aren't only ours are built. We go through them with you, in the repository, on request — verification stays possible, it just happens in a conversation.

What would you want to work better?

Tell us about your process. Together we decide what is worth building, what we can connect, and how we check the result.

Let's talk