Characteristic, class, object, restriction, procedure, variant table — anyone who wrote $SETinto an IPC dependency net, or sat through CU50, will be at home in an .ssc file in five minutes. The difference is not the vocabulary. The difference is that the same engine runs in a browser, on a PC, and on a microcontroller that configures a live machine: thermometer in, relay out.
SAP configures an order. Spore configures a board. Anyone who also needs the rest of SAP — BOM, routing, pricing, a support contract — will not find it here. This page does not hide that.
If you can read an SAP dependency net, you can read .ssc. The naming is deliberate. Behind it is one static heap and a total recompute, not a session on an application server. How to write the language itself is in the language reference; this table is only the mapping — what the SAP word means here, and where it does not.
| SAP LO-VC / IPC | Spore | Note |
|---|---|---|
| Characteristic | characteristic | Three types: SINGLE, MULTI, RANGE. No date type, no table type, no multi-interval RANGE. |
| Class, Object | class, object | Same idea. No BOM and no configurable product structure behind it. |
| Constraint / Restriction | restrictions: | Narrows a domain. Never writes a value. Re-derived from committed state on every solve_config(). |
| Procedure ($SET, $SET_DEFAULT, $DEL_DEFAULT) | procedure | One-to-one, including default provenance (revocable vs. an explicit pick). unset is host-only, not a procedure. |
| Precondition / invisible / required / no-input | property restrictions | Derived flags, not memory. A precondition is OR across rows: the target is visible if any one holds. |
| Variant table | table / .vtable | Rows inline or in an attached file. Bidirectional propagation, with the gaps called out below. |
| GUI dependency-net editor | .ssc DSL | Langium grammar, kb-emit → .kb.img plus a UI manifest. No visual canvas. |
| Session on an application server | one heap[] | No malloc. PC default 8000 ints; AVR 400 ints = 800 bytes. Multi-tenant isolation is the host’s job, not the engine’s. |
01
A characteristic marked observed is written from a sensor (set_cstic_observed), not a user. No admission gate, no domain narrowing, no CONFIG_BAD because it is freezing outside. A conflict from a thermometer would be a lie, not a configuration error. The UI treats it as read-only. SAP never needed this: it configures an order, not a live physical state.
02
A value the settled configuration is meant to leave toward the host — a relay, an LED, a PWM output. The solver decides; hardware executes. It is manifest metadata, not a bit in the .kb.img: pin binding stays on the host, by object.characteristic name.
03
The same engine.c (~1560 lines of portable C11, no struct pointers) compiles with gcc for a PC, emcc for WASM in the browser, avr-gcc for an ATtiny1614 with 2 KB of RAM, and the ESP-IDF / PlatformIO toolchain for an ESP32-C3. No JVM, no application server, no per-platform engine binary. The knowledge base lives in flash as a .kb.img.
04
Every solve_config() call recomputes all derived state — domains, properties, completeness — from committed state. There is no bug class where derived state drifted from the source. The cost is a full recompute on every change. Defaults are cleared and re-derived at the start of each call; an explicit pick stays.
Both sides detect the same infeasibility. They disagree on what stays “assigned” once there is a conflict.
In Spore, set_cstic rejects a value that is no longer in the domain. In real SAP, RESTRICTION: FALSE accepts the pick, leaves it assigned, and flags the whole configuration as conflicting. That is a deliberate choice, not a second, non-gating mode waiting to be added.
Forward and reverse both work: a pick on a dependent column can narrow a primary. A reverse key must not be RANGE, must not be MULTI, and a SINGLE must be an explicit pick, not an inferred default. The one thing those per-value rules structurally could not do — narrow a dependent while the primary is still ambiguous, as long as every remaining row agrees on it — engine.c now does too, reading the table's own rows at solve time instead of a precompiled per-value rule. A MULTI primary is still out of scope.
SAP writes two roles bound to one characteristic and a joint condition. Spore’s object model has no equivalent. Instead, includes / not includes reads the current chosen set. Same intent, different spelling, one direction.
The solver on the board is pure propagation, not search. It can report a conflict where a different pick order would still have a legal solution — the classical gap of any propagation-only CSP, not unique to Spore. search_complete() exists: real depth-first backtracking, a full heap snapshot at every choice point. It can prove that no solution exists, not just get stuck. It is deliberately not shipped on AVR. It was tried live on ESP32-C3 (~60 KB RAM for a usable depth) and taken back out: the KBs a board actually runs are hand-authored and checked before deployment, so this gap almost never fires there. It stays a PC / WASM tool while writing a KB, where the memory is free.
Another school of configurators precomputes the legal space into a compressed table and gets completeness without runtime search. Industrial engines do that. Spore does not, on purpose: that table would not fit on an ATtiny. Footprint is why the data model looks like this, not an omission.
There is a browser editor for writing a KB (live preview, diagnostics, WASM or a WebSocket to a PC / a bridge to a board). There is no drag-and-drop canvas like SAP GUI. No BOM, routing, or pricing. Pure boolean legality — allowed or not — not “cheapest”. No coefficient / weight equivalent.
No years of production on thousands of daily transactions, no certification, no support contract. Exercised on a handful of demo knowledge bases and real hardware (ATtiny1614, ESP32-C3) — not on an industrial catalogue with tens of thousands of combinations.