Serdica · IPAL / ABACUS · design

Product Configurator Design

How to build the thing the requirements describe: a workspace that turns an insurer's product specification into configuration for four surfaces across two databases. The design turns on one decision — a single canonical model, and every emitted file a projection of it — and on two isolations that keep the model honest. Below: the architecture, the workspace laid out file by file, seven prompts that carry most of the behaviour, the one-way ABACUS seed and its limits, and the decisions with the alternatives they beat.

Notation

Provenance tags appear throughout, and are the same three the model uses: SPEC the specification says so · SYS derived from the system · DEC a decision taken here. ISOLATED marks a component deliberately denied context that the rest of the workspace has. Sections marked + open to the prompt text or the evidence.

Companion document: Product Configurator Requirements — what the thing must do. This one is how it is built.

01Architecture

Configuration for one product has to be stated in four places that no constraint ties together. Authored four times it drifts; derived one from another it loses the rates. So the design puts a written model in the middle and makes every surface a projection of it — and then refuses to let the model verify itself.

Source set tariff · covers · screens revisions · one email tariff-reader no DB access CANONICAL MODEL 05_model.md every line SPEC · SYS · DEC estate-prober SELECT only structure counts projects into IPAL database INSIS database SRD_ANLT tariff SRD_IPROD product SRD_INTEGR rules · INSIS proxy ABC_ACCESS convert-stage checks seeds 3 of the 12 parts · ~1 row in 7 no FK · nothing keeps them agreeing after in force Running system a quote that renders acceptance counts · internal agreement only a rendered premium · the only proof from outside
The input is a set of documents, not one. The short dashed arrow inside the IPAL database is the ABACUS seed — one-way, three of the twelve parts, and nothing keeping the two surfaces in agreement once it has run. Two returns reach the model and only one is evidence. The dashed path is the acceptance script: its expected counts are derived from the model, so it detects drift between projections and is blind to a step the model never contained. The accent path is a quote produced by the running system and reconciled against the tariff by hand — the only check whose result the model did not decide in advance.

02The parts, and what each is for

Layer 1

CLAUDE.md

Always-loaded context

Identity, the four boundaries, and routing to everything else. Kept under 120 lines because it is paid for on every request — anything longer belongs in knowledge/.

Layer 2

skills/

Procedures

Four: configure a product, read a tariff, explore an unfamiliar column, verify. Each is a sequence with gates, loaded only when its trigger matches.

Layer 3 · ISOLATED

agents/

Two denied contexts

tariff-reader is given no schema vocabulary; estate-prober is given no product intent. Each returns a fact the caller cannot bias.

Layer 4

prompts/

The reusable wordings

Seven texts that recur across products — the identity, the source inventory, the scope question, the model header, the open question, the handover note, the customer reply. Versioned, so a phrasing that worked is not re-invented.

Layer 5

knowledge/

Domain reference

Thirteen documents behind one index — the two schemas, the tariff engine, the INSIS seam, the pricing-factor glossary, the SRD_INTEGR packages, premium theory, environments. Read on demand, never all at once. Facts about the system, not about any one product, cut to product-configuration scope on 2026-09-02.

Layer 6

memory/

What changed the behaviour

Corrections, measured counts with their dates, products in flight. The index loads every session; the entries load when relevant.

Layer 7

scripts/

Determinism

Whatever should give the same answer every time: domain probes, cross-surface diffs, document assembly. Anything a script can settle is not left to judgement.

Layer 8

deliverables/

The output

One directory per product, self-contained: model, SQL per part, acceptance, rollback, document. Nothing flat at the root.

03The workspace, from scratch

Everything the configurator needs and nothing it does not. The comment on each line says why the file exists — a file whose reason cannot be stated in one line is usually two files.

product-configurator/
│
├── CLAUDE.md                             # always loaded · ≤120 lines · identity, boundaries, routing
├── README.md                             # how to open the workspace · start here · before the first run
├── _PROVENANCE.md                        # exported from the AISA workspace 2026-09-02 · what came, what was cut
├── .mcp.json                             # sqlcl read-only · the binary lives in tools/, git-ignored
├── .gitignore                            # deliverables/ stays local until a run is signed off · tools/sqlcl/
├── .gitattributes                        # LF in the repo · binaries never normalised
│
├── .claude/
│   ├── skills/
│   │   ├── configure-product/skill.md    # ENTRY · the 11 steps and the 3 gates
│   │   ├── read-tariff/skill.md          # a tariff document → a rate structure, in the doc's terms
│   │   ├── explore-schema/skill.md       # what does this column actually do · constraint + writer + count
│   │   ├── verify-product/skill.md       # acceptance script, then the manual premium proof
│   │   └── workspace-initialize/skill.md # JDK · SQLcl into tools/ · MCP starts · connections reachable
│   │
│   └── agents/
│       ├── tariff-reader.md              # ISOLATED · no schema vocabulary · returns a rate structure
│       └── estate-prober.md              # ISOLATED · SELECT only · returns counts, never advice
│
├── prompts/
│   ├── 00_identity.md                    # who the configurator is · the four refusals
│   ├── 05_source_inventory.md            # N files in → one table: what each says, which part it owns
│   ├── 10_scope_question.md              # the A/B/C question put to the customer before any work
│   ├── 20_model_header.md                # model front matter · the SPEC/SYS/DEC legend
│   ├── 30_open_question.md               # how a DEC line becomes a numbered, answerable question
│   ├── 40_internal_note.md               # handover to the dev team · table.column, wrong → right
│   └── 50_customer_reply.md              # the reply that exposes no table name and no row count
│
├── knowledge/
│   ├── system.md                         # THE INDEX · one row per file · consulted before any read
│   ├── product_config_new_product.md     # START HERE · the twelve parts · counted domains · CreateProduct's 91 tables
│   ├── product_configuration.md          # a configured product walked table by table · the four surfaces live
│   ├── insurance_premium_pc.md           # the vocabulary · base/written/earned · order of application · 17 defects
│   ├── factor_glossary.md                # 134 pricing-factor codes · BG description · products · LOV values
│   ├── abacus_schema.md                  # SRD_ANLT · versions, rate files, the deploy chain, version selection
│   ├── abacus_engine_vs_abc_access_schema.md # design side (SRD_ANLT) vs resident side (ABC_ACCESS)
│   ├── ipal_schema.md                    # SRD_IPROD · SRD_CUST · SRD_PR · SRD_SYS · tables, keys, writers
│   ├── insis_schema.md                   # the incumbent PAS · only what the mapping seam targets
│   ├── insis_seam.md                     # CFG_MAPPING_* · product-aware pre-processors · ABC_ACCESS convert hooks
│   ├── db_packages.md                    # the 95 SRD_INTEGR packages · the 15 product-aware · config-table → reader
│   ├── currency_and_rates.md             # main units · EUR default · the 1.95583 fixing
│   ├── roles_and_authorization.md        # what makes a configured product reachable by an operator
│   ├── environments.md                   # DEV/TEST/QA/PROD deltas · which one a request targets
│   └── sources/<code>/                   # ONE DIRECTORY PER PRODUCT · every file byte-identical
│       └── _inventory.md                 # file → date → parts it covers → authoritative for
│
├── memory/
│   ├── MEMORY.md                         # THE INDEX · one line per memory · loaded every session
│   ├── feedback/                         # 18 corrections · each says what to do differently, and why
│   ├── reference/                        # 43 measured facts · every count carries its date
│   └── project/                          # products in flight · decisions awaiting an answer
│
├── scripts/
│   ├── probe_domains.sql                 # one GROUP BY per configurable column · feeds estate-prober
│   ├── model_checks.py                   # model → the questions answerable before SQL exists
│   ├── abacus_seed.sql                   # ABACUS → IPAL skeleton · 3 of 12 parts · one-way, once
│   ├── diff_surfaces.sql                 # IPAL ↔ ABACUS · cover, factor and LD agreement
│   ├── check_links.py                    # every pointer resolves · CORE vs MEMORY split · should print zero
│   ├── finalize_doc.ps1                  # markers → the product document, shared CSS inlined
│   └── assets/                           # product_doc.css · product_doc.js · the shared document chrome
│
├── sql/
│   ├── configure_connections.sql         # the eleven BST-{ENV}-{SYSTEM}-{USER} connections · prompts for passwords
│   └── setup_connections.bat             # runs the above through tools/sqlcl
│
├── tools/                                # git-ignored · SQLcl per workspace · connections per user in %APPDATA%
│
└── deliverables/
    └── <code>_<slug>/                    # one directory per product · never a flat file at the root
        ├── 00_README.md                  # what is here and in what order to read it
        ├── 01_findings.md                # what the system taught me · where it contradicts itself
        ├── 05_model.md                   # THE MODEL · every line SPEC · SYS · DEC
        ├── 06_model_checks.md            # answered against the model, before any SQL is written
        ├── 20_abacus_design.sql          # group B · emitted first · versions, covers, LD, factors
        ├── 21_abacus_rates.sql           # group B · the rate files · the only never-derivable part
        ├── 22_seed_report.md             # what the seed filled, what it refused, coverage here
        ├── 10_texts.sql … 19_access.sql  # group A · catalogue → screen → packages → print → roles
        ├── 50_insis_core.sql … 52_*.sql  # group C · only when the customer commissioned it
        ├── 90_verify.sql                 # completeness · omissions · cross-layer · the manual test
        ├── 99_rollback.sql               # exactly what was added, in reverse dependency order
        └── <Product>.html                # the product document · decisions, questions, how to undo

Two ordering choices are load-bearing. ABACUS files are numbered 20 and 21 yet are emitted before the 1019 IPAL files: the numbers are reading order for a human, the emission order is the dependency PR_BASIC_LD has on the ABACUS loading list. And 99_rollback.sql is written in the same sitting as the SQL it undoes, not afterwards — a rollback written later is a rollback written from memory.

04The prompts that carry the design

Seven texts do most of the work. They are files rather than habits because a phrasing that reliably produces a good answer is an asset, and because a prompt in a file can be reviewed by someone who disagrees with it.

05_source_inventory.mdRun before anything else. The input is a set of files, and the set has to become one reading before it can become a model.step 1
The specification is a SET of files, not one document. Before reading any of them
closely, inventory the set.

One row per file:

  file · date · from whom · which parts of the product it speaks to ·
  is it a source, or a revision of another file in this set

Then, one row per part of the product (catalogue, covers, limits, factors, tariff,
packages, screen, texts, annexes, print, access, integration):

  part · the file that is AUTHORITATIVE for it · the other files that touch it

Rules for building that second table:

  · A later file supersedes an earlier one only where it SAYS it does. Otherwise two
    files covering the same part is a CONFLICT, and conflicts are reported, not
    resolved by date.
  · A part with no file is a gap. Name it. Do not fill it from a comparable product.
  · A part with two files that agree is fine — say which one the model will cite.
  · Reconcile ACROSS files, not within each. A cover priced in the tariff and absent
    from the cover list is a finding; so is a cover listed and never priced.

Output this inventory as the first section of the model, and cite files by name
everywhere afterwards. "The specification says" is not traceable when there are nine
of them.

Why this shape. The two failures are symmetrical and both common: reading the set as one document loses the fact that a later email overrode one clause; reading the files independently loses everything that only shows up between them — the priced cover nobody listed, the listed cover nobody priced. The inventory is the cheapest place to catch both, and it makes every later citation specific.

00_identity.mdThe always-loaded core. Four refusals, stated as refusals rather than as cautions — a caution is negotiable under pressure.CLAUDE.md
You configure insurance products for Serdica IPAL from a written specification.

Your deliverable is FILES. You never execute DDL or DML against any environment —
not DEV, not inside a block that rolls back. Auditing, triggers and redo capture the
attempt regardless of the rollback.

You never alter a core table or column without explicit permission. If a part cannot
be configured without an ALTER, stop and offer the alternative that needs none.

You never report a configuration complete on the strength of a check derived from
your own model. Completeness means a quote rendered in the running system and
reconciled against the tariff by hand.

You never fill a gap silently. A value the specification does not give and the system
does not settle is a decision — tag it DEC, and put it in the numbered questions.

Read memory/MEMORY.md at the start of every session. Read knowledge/system.md before
reading anything else in knowledge/.

Why this shape. Each refusal names the failure it prevents, because a rule whose cost is invisible gets relaxed by the next person under a deadline. The rollback clause exists because "it rolls back" is the most persuasive wrong argument in this domain.

10_scope_question.mdAsked before any file is written. Scope is the customer's decision and the three groups have very different costs.gate 3
Before I write anything, confirm which groups are in scope.

  A — the IPAL product        catalogue, covers, risks, limits, loadings, factors,
                              screen, packages, annexes, texts, print, access.
                              Makes the product reachable and quotable.

  B — the ABACUS tariff       versions, covers, LD list, factors, rate files.
                              Makes the premium a number rather than a zero.
                              Longest lead time; can be exercised on its own.

  C — the INSIS integration   mapping, validations, INSIS core rows, the ABC
                              request map. Makes the policy transferable.
                              Expect some of the 15 product-aware SRD_INTEGR
                              packages to need a DEV CHANGE, not a config row.

A and B together give a product that quotes and issues with no INSIS anywhere.
C is separate work with a separate risk profile.

Which groups, and against which environment?

Why this shape. It names what each group buys in outcomes, not in tables, so a commercial reader can answer it. And it warns about the dev-change tail inside group C before that cost is discovered halfway through.

agents/tariff-reader.mdAn isolation, not a specialisation. The reader is denied every database word so that the tariff is read on the tariff's terms.ISOLATED
You read insurance tariff documents and return their pricing structure.

You have no access to any database and no knowledge of any schema. That is
deliberate. If you find yourself wanting to know a table name, a column name or how
a value is stored, you have drifted out of your job — the storage is one realisation
of the pricing taxonomy, not the taxonomy itself.

Return, in the document's own vocabulary:
  · every cover the document prices, and every cover it names without pricing
  · the base rate for each: the number, its unit (percent / promille / lump sum /
    per day / per mile), and what it applies to
  · every loading and discount: its value, whether it is a percent or an amount,
    at what level it applies, and whether it is automatic or given by hand
  · the composition rule if the document states one, and say so if it does not
  · every limit, deductible, minimum and maximum
  · every condition attached to any of the above

Quote the clause for anything a reader would otherwise have to take on trust.
Where the document is silent, say "not stated" — never infer a market-standard value.

Why this shape. A reader that knows the schema starts mapping while it reads, and mapping is lossy in a way that is invisible afterwards: the factor with no column becomes the factor that was not mentioned. Denying the vocabulary makes the omission surface as an unmapped item later, where somebody notices it.

agents/estate-prober.mdThe other isolation. It is told nothing about the product being built, so it cannot return the answer the caller wants.ISOLATED
You measure what the existing configuration contains. You are given a column, or a
set of columns. You are NOT told what product is being built or what answer would be
convenient, and you must not ask.

For each column return, from the environment named in the request:
  · the constraint — type, length, NOT NULL, CHECK, foreign key
  · the live distribution — SELECT , COUNT(*) ... GROUP BY , every value
  · the count of rows and the count of distinct products
  · the date you measured it

Return counts. Do not return a recommendation, a "usual value", or a default.

Two distinctions the caller depends on you keeping:
  · a value permitted by a constraint but present in zero rows is PERMITTED, NOT USED
  · a value present in one or two rows out of thousands is reported as such, with
    the count, and never described as a convention

SELECT only. If a question cannot be answered without a write, say so and stop.

Why this shape. The prober exists to stop the estate being read as a template. Withholding the intent removes the pull toward the convenient finding, and forcing raw counts out rather than adjectives leaves the judgement where it belongs — with the model, where it gets a DEC tag and a question number.

20_model_header.mdThe front matter every model file opens with. Defines the tags before their first use, which is the whole point of having them.model
# <Product name> — <code> — canonical model

Source:      <file>, <date>, <version>      Environment: <DEV|TEST|QA|PROD>
Scope:       group A / B / C as confirmed <date>
Projections: every SQL file in this directory names the section of this file it projects.

Provenance tag on every line — no line is untagged:

  SPEC   the specification says so — the customer wrote it
  SYS    derived from the system — a CHECK, a key, a foreign key, a counted
         convention. Not a choice; the database already settled it.
  DEC    a decision taken here, and therefore something to confirm

Every DEC line appears again in § Open questions, numbered. A model with no DEC lines
means the gaps were filled silently.

Why this shape. Three things are fixed at the top because each is expensive to establish later: which source the model answers, which environment it targets, and what the tags mean. The last line is the honest one — it tells a reviewer what a suspiciously clean model actually indicates.

30_open_question.mdThe shape of a question that can be answered in one reading. Most unanswered questions are unanswerable as asked.model § 9
№ <n> — <the decision, as a question>

  Blocks:    <what cannot be finished until this is answered>
  Options:   A <option> — <consequence>
             B <option> — <consequence>
  Assumed:   <what the model currently says, so work continues>
  Cost of
  being
  wrong:     <what has to be redone if the assumption is rejected>

Why this shape. A question with a stated assumption does not block; work continues under it and the answer either confirms or triggers a known amount of rework. Naming the cost of being wrong is what lets the reader decide how quickly they need to answer — and it stops a list of twelve questions arriving with no order of importance.

05The ABACUS seed — and its edges

Once the tariff is in SRD_ANLT, some of the IPAL product can be written from it instead of typed again. Measured across the 63 products ABACUS knows: the covers transfer almost completely, the loadings nearly so, the factors less than half, and everything else not at all. That shape decides what the component is — a seed run once at creation, not a synchronisation that could be run again.

Coverage, PROD 2026-09-02, over the 63 products present in PPA_PRODUCTS
ObjectIn IPALABACUS knowsCoverageIPAL-onlyABACUS-only
Covers54753898.4 %9290
Loadings & taxes41538091.6 %35181
Pricing factors1 44363844.2 %805210

The averages hide the variance that matters. On product 2200 ABACUS knows 95 of 122 factors — a genuine head start. On 4800 it knows 6 of 57. The seed must therefore report its own coverage for this product before anyone leans on it.

Column by columnWhat arrives filled, what arrives empty, and what the seed is forbidden to guess4 targets
TargetFilled from ABACUSLeft for IPAL
PR_BASIC_PRODUCTSBASIC_PRODUCT_CODE, LOBGENERIC_NAME, validity
PR_BASIC_COVERSCOVER_CODE, OBJECT_CODE — 537 of 538 shared covers agree on the objectCOVER_TYPE DEC, LIABILITY, COVER_ORDER, validity
PR_BASIC_LDLD_CODE, LD_TYPE, LD_SIGNSEP_ACCOUNT (derive), NCB, FINST, REFUNDABLE, cover-vs-product level
PR_PRICING_FACTORSPRICING_FACTOR_CODE, PRICING_FACTOR_NAME, DATATYPE, QT_REQUIREDPRICING_TYPE, POLICY_TABLE, VALUE_SOURCE, UI_FLAG, UI_ORDER, UI_GROUP, DEFAULT_VALUE, COVER_CODE, OBJECT_CODE, SALE_STAGE

One near-miss to refuse. CALC_ORDER on the ABACUS side looks like COVER_ORDER and is not: one is the sequence the engine prices in, the other the sequence an operator reads. Seeding one from the other gives a screen ordered by arithmetic.

What the seed does not writeNine parts with no ABACUS counterpart, and the live rows they carry across the same 63 products≈ 7 870 rows
PartRowsWhy the engine cannot know it
PR_PRICING_FACTOR_VALUES2 639ABACUS stores the values that differentiate a rate, never the list an operator chooses from
PR_BASIC_COVER_DEF2 278Limits and deductibles are policy terms; the engine sees only their effect on price
LB_OFFER_COVERS1 541Packaging is commercial and has no rating meaning
CFG_FLD_VALIDATION530Field rules live in the integration layer
PR_BASIC_COVER_RISKS320Perils are the wording of the cover, invisible to a rate
LB_OFFERS289The layer whose absence let a product reconcile to the cent and fail to quote
PR_BASIC_COVER_EVENTS216Claim events, same reason as perils
PR_ANNEX_TYPES37A lifecycle concern
PR_BASIC_PRODUCT_DOCUMENTS20Print configuration

And with no row count because they are not per-product tables: every SR_MESSAGES label and its Bulgarian translation, CFG_POLICY_NO_SEQ, the roles and routes that make the product reachable at all, and the entire screen layer. A seeded factor arrives with a code, a name and a datatype — nothing that puts it on a screen or tells the backend where to read its value.

98 % of the covers, 44 % of the factors, and none of the nine parts that make the product reachable.

— why the component is called a seed and not a sync

The name is the design. Sync implies the two sides can be held in agreement, which invites a second run — and a second run would overwrite COVER_TYPE, UI_GROUP and every other column IPAL alone owns. Seed is a one-time head start that removes typing, not judgement. It fills the skeleton, tags what it could not decide DEC, refuses the 290 covers and 210 factors that exist only in ABACUS, and leaves the twelve-part completeness check exactly where it was.

06One product, end to end

  1. The source set lands in knowledge/sources/<code>/Every file byte-identical to what was sent. _inventory.md records what each says and which part it is authoritative for; conflicts between files are reported, not resolved by date.
  2. tariff-reader returns a rate structureIn the tariff's vocabulary, with clauses quoted and silences marked not stated. No schema words in the output.
  3. Scope is confirmedGroups A / B / C and the target environment, in writing, before a file exists.
  4. estate-prober returns countsConstraints and live distributions for every column the model will set. Counts, not advice.
  5. The model is writtenOne file. Vocabulary frozen. Every line SPEC, SYS or DEC. Every DEC numbered in § Open questions.
  6. The model is checked against itself06_model_checks.md — cover set, factor set, value sets, the tariff's arithmetic. Free, and before any SQL exists.
  7. The seed runs, onceABACUS → IPAL skeleton: covers, loadings, the factor list. It reports its coverage for this product, tags what it could not decide, and lists the rows it refused.
  8. SQL is emitted, ABACUS firstEach file headed with the model section it projects. Idempotent, so a partial run can be repeated.
  9. Acceptance and rollback are written togetherExpected counts derive from the model. The rollback is written while the SQL is still in view.
  10. A premium is proved in the running systemA rendered quote, reconciled by hand against the tariff. This is the only step whose result the model did not decide.
  11. The product document is publishedWhat was configured, what was decided, what is still open, how to undo it.
  12. What was learned goes back into memory/A newly counted convention, a defect found in an existing product, a correction. The next run reads it.

07Decisions, and what they beat

DecisionChosenRejectedWhy
HubOne canonical model; every file a projectionAuthor each surface directlyNo FK ties the four surfaces, so nothing catches divergence. Four authorings drift, and the drift is invisible until a premium is wrong.
DerivationNeither surface generates the otherGenerate IPAL from ABACUS, or the reverseMeasured on 2215: 49 factors in IPAL, 29 in ABACUS, 3 in the rate files. Every direction is lossy; the losses differ.
Emission orderABACUS before IPALIPAL first, tariff lastRates are the only never-derivable artefact and carry the longest lead time; they can be exercised before an IPAL row exists. PR_BASIC_LD also derives from the ABACUS list.
Tariff readingAn isolated agent with no schema vocabularyRead the tariff in the main contextA reader that knows the schema maps while it reads, and an unmappable factor silently becomes an unmentioned one.
Estate readingAn isolated prober returning raw countsAsk the main agent for "the usual value"Withholding the intent removes the pull toward the convenient finding and keeps permitted distinct from used.
CompletenessA rendered premium, reconciled by handThe acceptance script aloneCounts derived from the model prove internal agreement. A product has been handed over reconciled to the cent and unable to quote.
ExecutionEmit files; a human runs themLet the configurator apply its own SQLThe blast radius of a wrong configuration is every policy written afterwards, and the review has to happen before the write, not after.
IPAL → ABACUSThe backend calls the engine directlyRoute IPAL pricing through SRD_INTEGR.ABC_PREMThat proxy exists for INSIS — every entry point is typed policy@insis%ROWTYPE and it writes results back into INSIS. IPAL assembles the request from PR_PRICING_FACTORS and calls rpc.abacus itself. An IPAL-only product needs none of the 7 packages or 14 tables.
ABACUS → IPALA one-way seed, run once at creationA standing two-way syncABACUS knows 98 % of covers but 44 % of factors and none of the nine remaining parts. A second run would overwrite COVER_TYPE, UI_GROUP and everything else IPAL alone owns.
InputA set of files with an authority table per partOne merged specification documentMerging first loses which file said what, and the conflicts between files are findings. Reading them independently loses everything visible only across them.
Unpriced coversConfigure at rate 0 in both surfacesOmit until a tariff arrivesCHANGEABLE='Y' on all 7 035 157 base-premium rows ever written means an underwriter can price it by hand. An absent cover cannot be sold at all.

"Both reported five packages. A check that agrees with itself cannot detect an absent step."

— the post-mortem that put gate 9 in the sequence

This is the single observation the verification design rests on. A product was handed over complete, its pricing reconciled to the cent, and it could not produce a quote: the packaging layer had never been wired, and the check that should have caught it measured the work against the plan that produced it. Everything in the diagram's accent path exists because of it.

08Standing the workspace up

In this order, because each step is only checkable once the one before it exists.

StepDoDone when
1Create the tree; write CLAUDE.md from prompts/00_identity.mdA session starts and states its own boundaries unprompted
2Wire .mcp.json: SQLcl read-only, installed into tools/ by /workspace-initializeSELECT 1 FROM dual answers on every environment named in environments.md
3Write knowledge/system.md and the thirteen topic documents it indexesEvery claim in them carries a count or a source, and a date
4Run scripts/probe_domains.sql against PROD; seed memory/reference/Every configurable column has a measured distribution with a date
5Add the two isolated agents and confirm the isolations holdThe tariff-reader's output contains no schema identifier; the prober's contains no adjective
6Write the five skills, entry point lastThe entry skill only sequences the others — it holds no procedure of its own
7Rehearse on a product that already existsThe emitted SQL reproduces the live configuration, and every difference is explained
8Configure a real product against TESTA quote renders and reconciles by hand — gate 9

Step 7 is the one people skip. Reproducing a product that already works is the only rehearsal that has an answer key, and the differences it surfaces are exactly the parts the knowledge base got wrong. Expect it to fail the first time in a way that improves knowledge/ rather than the skill.

09What this design does not solve

Named, so that nobody assumes otherwise.

Moving a finished product between environments. Nothing in the estate carries a complete product across: the migration tool touches neither the tariff nor the INSIS-side ABACUS configuration. The configurator re-emits against the target instead, which is correct but not cheap.

Keeping a configured product correct as the system changes. The model is a statement about one moment. When a shared register or a validation framework changes underneath, no part of this design notices.

Anything requiring a write. Backend branches for product-aware SRD_INTEGR packages, INSIS user exits, a column too short for a code — all of them leave the workspace as a request, not a file.

Judging the tariff. The configurator implements pricing and raises numbered questions about it. Whether the price is right is an actuarial decision, and it stays one.