How it works
The whole path from an event in the store to a change someone approved.
The home page shows this path in six stations. Here each of them stands on its own: where facts come from, what a single number means, how a proposal takes shape, what stops it, and what stays in the system after execution.
Store model
Astarmo builds a model of the store.
Preparing a decision takes more than knowing the numbers. It takes knowing what they refer to and how they connect. A 19.8% margin means nothing until it’s clear which product it belongs to, what cost it comes from, how many orders it appeared in, and which campaign promotes it.
The model is built from data the store already has. Nothing to replace, no new system to roll out, nothing to rewrite.
Relations
On their own, concepts say little. The value is in the edges between them — and the edges are what lead from a single number to a situation that calls for a decision.
product --- has --------> price
--- has --------> cost
--- has --------> stock
--- sold in ----> order
<-- promotes ---- campaign
--- subject to -> rule
order --- placed by --> customer
session --- precedes ---> order
supplier --- supplies ---> product
This is the Astarmo ontology — a shared language in which the system describes every store. Each edge carries a confidence level and a pointer to the event that justified it, so the graph can be traced back to the raw fact. The language model moves across this graph: not across the store’s database, but across a model of its business.
Every object has its own page.
The store model isn’t a drawing in the documentation. The operator opens a product and sees everything Astarmo knows about it: price, cost, stock, margin, the campaigns that promote it, the orders it appeared in — and the history of decisions that concerned it, rejected ones included.
The same kind of page exists for a campaign, a category, a supplier and a rule. Each one leads along an edge to its neighbors, and every number leads down to the event it came from.
Sources
There is one model. The data comes from the systems the store actually uses.
The foundation of the context is the store’s data and its ad channels. Depending on how the store works, more sources join in — as many as specific decisions need, and no more.
Foundation
e-commerce platform Google Ads Meta Ads first-party tag
Four sources that make up the money axis: what sold, at what price, what it cost to buy, and where the traffic came from.
Depending on the store
warehouse system ERP PIM marketplace in-house systems
If the store keeps costs in an ERP or stock in a warehouse system, Astarmo takes them from there. If it doesn’t, there is nothing to connect — and nobody requires it.
Astarmo doesn’t require a particular stack. It maps the reality of a given store onto a shared e-commerce model.
Astarmo stays silent without data — and says why.
An unconnected source doesn’t produce an empty screen or a cautious platitude. It produces a sentence: “I won’t comment on ads, because the Meta connector isn’t connected.” The gap is named, located, and visible exactly where the operator would look for an answer.
Model-based systems tend to fake omniscience — they always answer, even when they have nothing to stand on. Astarmo shows where its sight ends, because the operator decides not only about a change in the store, but first about whether to trust the answer at all.
Source vs. meaning
The source says where a fact comes from. The model says what it means.
Decision logic can’t rest on the field names of a particular API or the structure of a particular platform. Switching providers must not invalidate the store’s rules.
Cost PrestaShop wholesale_price ERP PURCHASE_COST_NET spreadsheet net cost Stock PrestaShop quantity warehouse qty_available Campaign Google Ads campaign Meta Ads campaign
Cost remains cost, whichever system it comes from. Stock remains stock. A campaign remains a campaign. The decision layer works on commercial meaning, not on the structure of a particular system.
History
Astarmo remembers what happened.
Events go into the history first: an order, a price change, a cost update, a click, a stock change, an executed action. The record is appended, never overwritten. Everything above it is rebuilt from it.
Two clocks
Every event carries, separately, when it happened and when the system learned about it. A late correction appends a row instead of rewriting the past.
That is how Astarmo reconstructs not only the store’s state today, but also what it knew at the moment of a specific decision — and that is the only fair way to judge that decision.
What grows out of it
events -> store model -> metrics -> signals -> decisions
Each step stands on the one before. A metric points to the events it came from; a signal points to metrics; a proposal points to a signal.
Context
The model doesn’t get a summary.
It gets the keys to the store model.
The full context is products, orders, prices, costs, stock, campaigns, customers, metrics, relations, decision history, and the operator’s rules. It can’t be summarized up front, because up front nobody knows what will turn out to matter. So Astarmo doesn’t summarize — it gives the model tools and lets it ask.
language model
|
| asks for what it needs
v
question answer
margin on this SKU? 19.8% · 41 units
who promotes it? ad group 7719241
when did cost rise? Jul 11 +38.00 PLN
what was rejected? category markdown
worked before? margin +2.1 pp
which rule? margin min. 22.0%
|
v
proposal from action set
There is no fixed list of questions here and no limit on rounds. The model keeps asking until it knows enough — and it doesn’t roam the store’s systems to do so, only the model Astarmo has already built. Freedom to ask is total. The vocabulary of action is closed.
The cost of a decision doesn’t grow with the catalog.
The model doesn’t browse the catalog. It asks a question and gets a result computed on the data side — “coverage: 63 days”, not a thousand observations it would have to work that out from. Every answer has a hard ceiling on the number of rows, and hitting the ceiling is reported outright, not silently truncated.
That’s why a store with 900 products and a store with 10,000 products cost the same per decision. Scale lives in the data, not in the model’s context — and that is pinned by a test, not by a promise.
Data
The model doesn’t know a single customer.
It doesn’t even get the store’s identifier.
A decision about price, margin, stock or campaign budget doesn’t depend on who bought — it depends on how many and for how much. In the store model, a customer is a number of orders, not a name. This work needs no personal data, so the model gets none.
What the model gets
SKU 4471 margin 19.8% · 41 units cost 682.40 PLN orders 142 · 28 days campaign 7719241 rule: margin min. 22.0%
What never leaves
first and last name email, phone, address tax and national IDs IBAN, payment card IP, cookie, session visitor identifier ad click identifiers geographic coordinates tokens and system keys store identifier
One pass, not good intentions
Everything the model receives goes through a single scrubbing function in a single place — there is no second path to the prompt. It cuts by field name and by the shape of the value, so an email address hidden in free text drops out just like a field that carries that name.
The filter deliberately cuts wider than necessary. The cost of cutting too wide is one redacted field in an explanation; the cost of cutting too narrow is a data-protection breach. That asymmetry is settled in code, not in a privacy policy.
Code does the joining, not the model
The visitor identifier and ad click identifiers exist to tie a click to a session, and a session to an order. That join is performed by deterministic code in the data layer, and it stays there. The model only gets the result: campaign 7719241, spend 4,820 PLN, revenue 29,500 PLN, 41 orders.
This isn’t only safer, it’s more accurate. Matching identifiers is arithmetic, not judgment — the same data gives the same result, today and a year from now. The model wouldn’t do it any better; it would just have to do it on data it doesn’t need.
One store’s data isn’t visible from another: the data layer refuses any query that doesn’t name a store, and database policy is the second belt. Nor does Astarmo train any model on store data — models are hired, and the only thing the system learns is the rules the operator has approved. Those stay in the operator’s store.
A customer can be erased from the data.
When a store’s customer exercises the right to erasure, Astarmo deletes the key their data is encrypted with. From that moment on, the data can no longer be read — on Astarmo’s side too.
The audit chain is immutable by design, so the decision trail stays intact. The record documenting the decision survives; the person in it does not.
Security
Astarmo assumes the model can be wrong.
The model’s freedom grows on the side of knowing. On the side of acting, nothing moves. Between a question and a change in the store stand eight independent layers — each one works even when the others fail, and none depends on the model turning out to be well-behaved.
- Action vocabulary
- Closed and enumerable. The model composes freely within it and has no way to extend it.
- Parameter bounds
- Computed from the store’s data. A value outside the bounds rejects the whole proposal — never a silent correction.
- Memory veto
- A rule accepted by the operator blocks a conflicting proposal in the generator, before it reaches the inbox.
- Approval and dry run
- No change touches the store without approval, and before approving, the operator sees the exact effect.
- Ports
- A closed registry of write classes. There are no generic ports, and an architecture test won’t let one be added.
- Write guard
- Every write to the outside world calls the guard as its first instruction. Without it, the primitive fails its test.
- Kill switch
- Cuts off writes per store and globally. Measured in seconds, not in deployments.
- Store isolation
- The data layer refuses any query that doesn’t name a store. Database policy is the second belt.
Full cognitive power. Zero executive power. No configuration can move this boundary, because it isn’t a setting — it’s the construction. Each of the eight layers has its own test in the code and fails the deployment the moment it stops being true.
A number without a source isn’t evidence.
If Astarmo shows an 8.4% margin or 29,500 PLN of campaign revenue, the operator must be able to trace it to the data that justifies the value: which orders, what period, which metric definition, and which event in the history. The model doesn’t state the number itself, either: it gives a reference, and the system pulls the value from it.
The veto applies to a single field, though, not the whole answer. One number without provenance doesn’t wipe out the rest of the response — it drops out and is marked as missing, while everything that has backing stands.
“I don’t know, I’m missing this number” is a valid output, not a failure. A system that always has an answer will sooner or later have a made-up one — and the operator has no way to tell the two apart.
Signal
Astarmo doesn’t wait for the operator to ask the right question.
Code computes the fact: stock coverage in days, a margin drop against the store’s own baseline, a campaign below breakeven. Whether the fact deserves the operator’s attention is for the model to judge. Astarmo doesn’t hire a hundred rules to guess what matters in this particular store — it hires the best available model and shows it this store.
Facts aren’t guessed
If the margin is 19.8%, the system has to compute it. If the threshold is 22.0%, the threshold has to exist in the system. If the cost changed by 38.00 PLN, the value has to follow from the data, not from an estimate.
Computation is deterministic: the same data gives the same result, today and a year from now. But computation only rules on the fact. Whether the fact matters is for the model to judge — because importance depends on the store, the season, the supplier agreement and the operator’s plans, not on a threshold written into a rule by someone who has never seen this store.
A signal isn’t a decision yet
A signal says only this: something has happened here that may call for action. It doesn’t say what to do, and it proposes nothing.
signal margin_leak product Vantar RX · SKU 4471 margin 19.8% minimum 22.0% (operator rule) cost +38.00 PLN since Jul 11 sales 41 units · 28 days stock 112 units
An inbox is an inbox, not a firehose.
Every operator knows notification fatigue: a hundred alerts a day, not one of them a decision. A tool that finds everything hands back to a human exactly the work it was supposed to take away — choosing what matters.
That’s why Astarmo’s output is a handful of decisions, not a thousand findings. Each one comes with the reason it ranks above the rest, and the list is meant to end. An inbox that can be emptied before noon is worth more than a stream that can never be closed.
Proposal
From signal to proposal.
A signal alone isn’t enough for a decision. Only the context gathered around it lets reasoning begin — and only from that context does a proposal emerge that gives the operator something to evaluate.
signal + evidence numbers and the events behind them + history previous decisions on this product + store policies minimum margin, price-change tolerance + allowed actions what is even on the table here = decision context
A closed action ontology
The model can’t invent an arbitrary action. The vocabulary of possible actions is closed and enumerable — the model composes within it freely and parameterizes the proposal at will, but has no way to extend it, and every value has to fit within bounds computed from the store’s data.
Each action type has its own specification: parameters, bounds, risk, approval policy, and method of execution. A value outside the bounds isn’t quietly corrected — it goes to rejection together with the whole proposal.
action test_price parameter delta_pct bound [−8.0%, +8.0%] value +4.0% accepted price 899.00 PLN -> 935.00 PLN port price_write risk medium · needs approval
What this vocabulary lacks, and must lack: permissions created on the fly, generic writes, arbitrary HTTP requests, running a script.
Pause the ad group “Gaming chairs — generic”.
Once the cost of goods is subtracted, the group loses money on every sale.
172 PLN/day -> 0 PLN/day
ad group 7719241 · Google Ads · ads_budget · strategy default −60%
Evidence
- spend
- 4,820 PLN · 28 days
- revenue
- 29,500 PLN · 41 orders
- cost of goods
- 25,340 PLN
- result
- −660 PLN · the group loses money
- ROAS
- 612% vs breakeven 709%
- attribution
- first-party tag, not the Google Ads panel
Provenance
- model
- Claude · version in fingerprint
- prompt version
- rev 12 · 9f2c…a81b
- fingerprint
- f4d1…07c6
- bounds
- −100% in [−100%, +30%] · accepted
Decision inbox
Nothing touches the store without the operator’s approval.
The operator doesn’t get an empty dashboard, or a prompt asking “what do you want to do?”. They get a prepared decision: what happened, why it matters, what Astarmo proposes, on what basis, within what bounds, and exactly what the change will do. They can approve it, modify it, or reject it.
Dry run
After approval, the change doesn’t go straight to the store. First the operator sees its exact effect on specific entities — not a description of intent, but values before and after.
entity SKU 4471 · Vantar RX before 899.00 PLN after 935.00 PLN scope 1 entity · 1 of 1 effect margin 19.8% -> 23.4%
Execution
The change is taken over by a separate layer. The model has no such path and no way to build one for itself.
proposal -> operator approval -> dry run -> bounds validation -> write-class port -> guard_write -> change in the store
There is no universal executor with arbitrary access to systems. Every class of action has its own controlled path — the registry holds no generic ports, and a test won’t let one be added.
Outcome
“Executed” doesn’t mean “worked”.
After the change, Astarmo watches what happened. A change observed after a decision isn’t automatically treated as its effect — the system distinguishes the outcome observed after a decision from the impact that can actually be attributed to it.
Scorecard
Where there is a basis for it, the system uses baselines, comparisons and experiments. Where there isn’t, the outcome is labeled observational, and nobody promotes it to proof of cause.
before margin 19.8%
41 units / 28 days
decision test_price +4.0% · 09:02
after margin 23.4%
38 units / 28 days
margin +2.1 pp
CI [+0.4, +3.8]
revenue +1,240 PLN
CI [−310, +2,790]
reading correlational,
no control group
Memory
The next decision doesn’t start from zero. Astarmo remembers what it detected, what it proposed, what the operator accepted and rejected, what was executed, and what the outcome was.
From this history, the model can propose a rule. The proposal alone changes nothing — the operator approves the rule. Only an accepted rule enters memory, and from then on it has veto power over later proposals: a conflicting proposal is rejected in the generator before it ever reaches the inbox.
Data isn’t the whole context.
Two stores can have the same numbers and make different, equally correct decisions. What sets them apart is minimum margin, price-change tolerance, rules for flagship products, stock thresholds, the required level of approval, and exceptions only the operator knows about.
That’s why Astarmo has to know not only what the store looks like, but also the rules the store wants to operate by. The operator’s rules are part of the context of every decision, on a par with the data.
Evidence
A chain that can’t be rewritten.
Every event lands in a hash-linked audit chain. Swapping a single entry breaks every entry after it, and verification shows it. Below is one full decision cycle — exactly as the system records it.
[AI] language model [HUMAN] store operator no marker deterministic code
Rollout
We start with decisions, not systems.
The first question isn’t “which systems do you run”. It’s: which decisions take regular operator work today. Only from that follows which facts are needed and where to get them.
The first six weeks run on a fixed rhythm: a one-page report every week, and at the end a joint recommendation on whether to keep going. The operator sets the criterion for that decision at the start, before the first number is on the table.
-
01
Decision classes
We choose the first classes of decisions: prices, margin, promotions, campaign profitability, stock, products that don’t sell.
-
02
Facts
We establish which facts those decisions need — and only those.
-
03
Sources
We establish where those facts live in this particular store, and map them onto the Astarmo model.
-
04
Rules
We write down the operator’s semantics and rules: thresholds, exceptions, required approvals.
-
05
Observation
Astarmo observes and prepares proposals. The operator evaluates them on the live store, changing nothing.
-
06
Execution
Only later, class by class, is controlled execution switched on.
We don’t connect everything we can. We connect the data specific decisions need. The goal of a rollout is a correct model and correct decisions; integrations are a means to that end.
Systems of record below.
System of decision above.
Astarmo doesn’t replace the systems the store already runs on. The e-commerce platform still handles sales, the ad systems still run campaigns, the rest of the tools still do their job.
Above them, a layer appears that doesn’t exist today: context, signals, decisions, rules, controlled execution, outcomes and memory.
That’s the system. The rest depends on the specific store.
The first conversation starts from a real operating decision. We establish which facts it needs, where they come from, which rules apply, and what the path from signal to action looks like today. One message opens the conversation: a description of the decision is all the inquiry needs, and a reply comes back within one business day.