☰ Contents
AISA v2.0 / Technical documentation / Stages S1–S2 — Planning, placement and the brief

Stages S1–S2 — Planning, placement and the brief

F verified factP decided planC open challenge

Purpose

P Real intake and lineage: a verbal/internal request is recorded through authorised admin/client intake with a stable source identity and an explicit objective; an SD id is not required. Support owns Next Release waiting and recruits Development when its recorded release trigger fires. Pin actual source/target heads and inspect placement per lineage. Customer logic in a shared service requires the existing core-placement decision rather than an automatic code-path exemption. riskmodel is a backend-repository Python skill, not an intentgpt task. Programme-scale reinsurance and currency work follows the explicit referral path in Case journeys § 6.

In the Development module (id source), a defect or a small extension arrives. Before a line of code is written this stage answers four questions and puts the answers in front of a person: what actually happens (the reproduction), which code the customer runs (the branch set), where the change belongs (the placement), and what breaks if it is wrong (the blast radius). The customer's lineage forked from master on 18.03.2025 and has diverged since — the counts live in memory with their measuring query, never as a quoted figure — so "fix it on main" is not a plan — it is a guess about which system the fix reaches.

The stage ends at gate H2 — the S2 approval of the brief. Nothing is implemented before it is answered.

Inputs → Outputs

In: the case — a defect report, a small extension request, or a handover at Hd from Support or Configuration. Out: the reproduction or the evidence in its place, the branch set, the placement decision with its alternatives, the test plan, the revert intent, and the brief the human approves.

What happens

Stage What happens Who
S1 planning reproduce (or evidence) the issue; resolve the environment branch set across the source's repositories + the environment DB — the start environment per the case type's flow (source.start_flows, D63: running products → bulstrad-staging, then PROD; new/unreleased → bulstrad-qa) and the environment path per product line (§ PL-3, D123); the placement decision — customer-specific plugin / microservice / environment-resident PL/SQL first; core only as a core-change proposition [F: the BusinessInsurance/Bst* and __Plugins seams exist, inspected 31.08.2026] planner + placement analyst; experience-index lookup ("seen before?")
S2 approval the human brief: why this placement, blast radius, test plan — before any code human gate

What runs this stage

Agents source.planner with the placement analyst
Isolated sub-agents the experience index lookup (“seen before?”) runs without the case's reasoning
Memory domain it owns source/<repository> for the repositories in scope, plus the experience index (Agents Memory)
Skills it runs worktree + build + test runner is not used here; S1 reads (Agents Memory and Skills)
Its branch of the graph Development Module § 0 — the module's agent tree, its memory branch and its skills · the component list is Components — Development

Every stage also uses source.root — the only profile that reaches the human — and the platform's write_executor and write_auditor (Gating).

Rules this stage carries, taken from real changes

P A change spanning two repositories carries a contract paper from S1 onward — the shape both sides agree on — because the deploy order in S6 is decided from it.

The placement model

Every Development case begins with the same question, and the answer decides its blast radius, its test surface, its deployment path and its upgrade burden for years: where does this change go? A fix put in core to save an afternoon reaches every customer and every future upgrade. A fix put in a customer plugin when the behaviour is genuinely standard gets re-implemented three more times.

The placement decision is a deliverable of S1 planning, presented at the S2 approval gate before any code is written (DG-4).

PL-1 The seams

P The seam catalogue is configuration of the source (D58): each declared source carries its seams, their paths per lineage and their blast radius; this page's table is the Bulstrad estate's catalogue, the decision mechanism over it is generic.

F They exist in the Bulstrad estate today (inspected 31.08.2026), in increasing blast radius:

# Seam Reaches Use when
1 Customer plugin__Plugins/…Plugin.Bulstrad* one customer the behaviour is a customer's own rule: their tariff quirk, their document, their prompt catalogue
2 Customer serviceBusinessInsurance/Bst* (BstPasPaymentPlan, BstPasPolicyService, the PasClaim* family, Integrations) one customer the customer needs a whole capability the core does not have
3 Environment-resident PL/SQL — every SRD_INTEGR package (POLQRY, TR_*, INS_*, ABC_PREM*, the UEXIT_* exits — 95 bodies on PROD) one environment the logic belongs to the data layer and must run where the data is; ⚠ those bodies are not git-managed (catalog)
4 Core, as a propositionsrc/Serdica/* platform and PAS services every customer, every upgrade the behaviour is genuinely standard for all customer types

P The order is the decision order: seam 4 is chosen only when 1–3 are shown to be wrong, and choosing it produces a core-change proposition carrying the same content as any other placement brief.

P A core-change proposition is addressed to the estate development lead as a GitLab issue on master carrying the brief, rejected alternatives, blast radius and test plan (D108). It leaves the Development module; the Development task records the issue id and returns an external-owner result; the owning case remains pending that obligation unless the agreed objective was the referral itself, and issue state changes return as ledger events.

P Amendments from the case studies (02.09.2026) (Development Module § 5):

PL-2 The criteria

P Four questions, answered with evidence rather than instinct:

  1. Who else has this behaviour? If only Bulstrad does, it is not core — whatever the code would look like. If every customer type does, a plugin is a duplication waiting to happen.
  2. What breaks if it is wrong? Blast radius per seam is the table above. The brief states it explicitly.
  3. Where does the data live? Logic that needs the row it operates on, in the same transaction, belongs at seam 3 — not in a service that round-trips.
  4. What is the upgrade burden? A core change travels with every promotion; a plugin has to be re-verified against each core version. Both are burdens; the brief names which one is being taken on.

PL-3 The environment is a branch set

P Placement is meaningless without naming which code. An environment is a branch set across repositories plus the database:

intentgpt + serdica-ui + serdica-backend + the DB model and PL/SQL on that environment's own database.

P The path per product line (D123, Development Module § 1): the existing policy-administration products are developed locally, tested locally as far as they can be, then targeted at bulstrad-staging (TEST) and then PROD, with the port to master and on to bulstrad-qa afterwards. The health line (product 8000, ДЗО) works on QA, ports to master, and will target the planned bulstrad-prod-2. There is no global working environment — the product line says which it is.

F The customer lineage is not master: bulstrad-staging and bulstrad-prod fork from master at 2a2bdbbca4, 18.03.2025, and have diverged since — the counts are measured on demand and never quoted here (Development Details DG-2, branch/env memory). A fix developed on master may not apply to, or conceptually exist in, the code the customer runs. The start environment is configuration per case type (source.start_flows, D63): running products start on bulstrad-staging (then master and bulstrad-qa); new, unreleased products start on bulstrad-qa and are ported to master.

F Porting discipline, inherited: fix commits only, base commit named, appsettings.json never carried across branches (they are per-environment), and the ancestor-merge fast-forward trap avoided with its proof gate (same memory).

3b. Seam 3 has no branch — the PL/SQL sync

F The ~95 SRD_INTEGR packages are not in git, and this is measured rather than assumed: git ls-tree -r origin/bulstrad-staging over the package names returns empty, and MigrationService carries CFG/seed migrations only (workflow, catalog).

F The estate's PL/SQL developer edits packages directly on the environment's database, continuously, and commits nothing (stated 01.09.2026). Seam 3 therefore has a second author who does not use the platform's tools, and whose in-flight work an apply can silently overwrite: CREATE OR REPLACE PACKAGE BODY replaces the whole object, and nested procedures cannot be replaced independently (workflow).

P Detection is a content hash, not a timestamp. A poll over all_objects + all_source returns per object its last_ddl_time, line count and a normalised source signature. The signature decides whether the source changed; last_ddl_time travels as metadata only. F A timestamp cannot carry that decision: a recompile bumps it whether or not a change is in it, and one Toad compile invalid mass-action bumped ~30 SRD_INTEGR packages in six seconds on 15.05.2026 (SD-2003 note, ABC_PREM incident). The signature is the Layer-5 block-hash recipe, seeded with the line number so reordering is visible (4-layer audit).

P Capture is scheduled, and the store is the platform's, not the developer's. Snapshots land append-only in the target schema SRD_SUPPORT (not yet created — verified 03.09.2026; D30/D47) — one row per environment × object × digest — taken on signature change while a case is open, at case open, either side of every apply, and nightly regardless. The agent mirrors them into a git repository it owns alone, one commit per detected change, for diff and merge. P That mirror is not redundancy theatre: the target schema shares the Bulstrad QA database failure domain with QA SRD_INTEGR — the database the PL/SQL developer actually works on [P: decision 02.09.2026; Architecture § 1], so the snapshot table alone would share a failure domain with the packages it protects once created. Snapshots of the staging and production packages are taken over connections to those databases into the same table; for them the failure domain differs, the mirror rule does not. The git mirror is the copy that survives losing that database. F The existing extracted corpus shows the risk of no cadence: bst_health/db/plsql/ is a single 06.07.2026 snapshot, never refreshed, whose files carry no CREATE OR REPLACE, no owner qualifier, no terminator and no digest — readable, but not re-appliable and not verifiable against the database.

P A signature detects drift; it is not an atomic compare-and-replace. The signature recorded at extraction is a precondition of the write grant (Trust and Data § 2). If the developer saved in between, the signature no longer matches and the apply is refused after a fresh authorised read detects the changed signature, before any DDL is sent. The agent re-extracts and proposes a three-way merge — base = the snapshot, ours = the fix, theirs = the live body — which re-enters the gate as a new proposal. Nothing merges itself. An advisory lock is rejected: a lock only the developer could honour, and never takes, guards nothing.

P Attribution is not solved, and the design says so rather than implying otherwise. F ALL_SOURCE carries no author column; unified_audit_trail returned a complete identity for one actor and zero rows for another on the same schema the same day; ASH is 1-in-10 sampled and yields host and tooling, not a person (incident). Artefacts therefore state what changed and when, and name an actor only where an audit row supports it — otherwise "actor not recorded". P A unified-audit policy on CREATE/ALTER PACKAGE for the IPAL schemas is raised with the Bulstrad DBA — it needs a DBA, not just Ablera dev (Measurements § DB-5; Software Architecture § 10.5); F IPAL can afford it — its SYS.AUD$ is ~2 MB against INSIS's 6,213 MB (AUD$ comparison).

P The apply does not recompile anything as a matter of course. CREATE OR REPLACE compiles the object it replaces, and locally invalidated dependents revalidate on first use — Oracle's work, not the agent's. Recompilation appears below exactly twice, and in opposite senses: once as something never to do (schema-wide, because it erases the change signal), and once as the remedy for the single case Oracle does not handle itself — a spec change whose dependents live on another database.

P DDL is its own effect class (G9): auto-committed, not rolled back; its undo is re-applying the pre-apply snapshot of the object, so no DDL runs without that snapshot in the case, and a CREATE OR REPLACE is recorded as DDL with the snapshot id. The full capture → preflight → apply → verify → compensate loop is the DDL protocol of Failure and Recovery § 2 (D60). P The write executor applies it under the HW-ddl gate with the identity of the current D65 stage — stage 1 uses the existing privileged identity, stage 2 the least-privilege platform identity; the DBA reviews the identity path rather than being an apply step (D104). P The cross-database dependency check names its tool (G12): the plan queries the INSIS side's ALL_DEPENDENCIES (the connector's role needs SELECT on it) for referencing objects over a PROD / INSIS / read grant before any SRD_INTEGR spec changes; it is a plan step with an assertion.

P Standing prohibitions on the apply, each with its guard:

Never Guard Why — all F, each a recorded incident, verified 01.09.2026
replace from a stale extract signature precondition on the grant the developer's work disappears with no trace
promote a spec without its body both objects in one script set PROD POLQRY stood VALID with a NULL; stub raising ORA-06503 on every call (SD-1863)
run a schema-wide recompile recompile only the named dependents it erases the change signal for every package at once
change a spec with @insis/@abacus dependents without a plan for the far side cross-database dependency check in the plan Local dependents revalidate themselves; remote ones across a DB link do not. On 15.05.2026 a spec change to SRD_INTEGR.ABC_PREM_OBJ left every INSIS.ABC_ACCESS.* caller raising ORA-06508 — not the self-healing ORA-04068 — breaking every ABACUS-priced product from 09:49 to 10:12, until ~16 INSIS packages were recompiled by hand; IPAL cleanup ran until 10:20, so D20's "30 minutes" is that outer window (DB-6)
delete a routine without grepping its callers caller sweep is a plan step SD-1732
overwrite a snapshot in place the store is append-only the snapshot is the merge base

F Cadence, measured 01.09.2026 on the QA environment the developer actually uses (db.serdicaqa.bulstrad.bg, host ablera-db-actuary), 90 days of SRD_INTEGR package DDL:

Active days 13 of 90 — roughly one day in seven
A normal active day 1–7 objects, spread over minutes to a few hours
The tightest cluster seen 7 objects in 15 seconds (20.08.2026) — one action touching several
The largest day 105 objects on 07.08.2026, but across 42 distinct timestamps over 5.4 hours — a working session or a staged deployment, not a mass recompile

P What that settles. The developer is sparse, not continuous, so the background poll does not need to be aggressive: the gates matter more than the frequency. Capture at case open, capture immediately before every apply, and sweep in the background at a low rate. The collision probability inside a one-hour agent working window is small but real — which is why the signature precondition on the write grant stays mandatory rather than becoming a best-effort check.

F The 15-second/7-object cluster (measured 01.09.2026) is why polling can never be the only detection: a poll at any sane interval sees the end state, not the intermediate ones. That is sufficient, because the signature is compared against the live body at apply time rather than against what the poll happened to catch.

F Re-measured on STAGING and PROD, 02.09.2026 (Measurements § DB § 4): STAGING had 6 active days in 90 (QA 13, PROD 15), a normal day 2–5 objects, and one mass recompile of 171 packages inside a single minute on 19.08.2026 (0 created, 11 left INVALID) — the case the signature-not-timestamp rule exists for; the description "most frequently changed" does not hold for DDL cadence in this window. QA's 07.08 day of 105 objects contained an 86-object burst in 28 seconds. Current package census, invalid bodies, measured 02.09.2026 (Measurements § DB-4): PROD 0 of 95 · QA 46 of 108 · STAGING 13 of 104 — DB-4 carries no DEV figures — the snapshot records OBSERVED_STATUS per object so a post-apply INVALID is attributable to the apply or to the baseline (DB-5).

3c. PLSQL-SIG-1 — the content signature, defined

P External-writer boundary. A hash comparison immediately before CREATE OR REPLACE cannot prevent another tool saving immediately afterwards. Automatic replacement therefore requires a connector capability that establishes exclusive write ownership for the operation, including the external author, or a controlled maintenance window confirmed by that owner. Platform impact locks alone do not establish this. Without exclusion, prepare the exact script and pre-image, return an external-owner step for application, and verify the result afterwards. Compensation checks the live signature against the version this case wrote; it must not overwrite a newer external edit with the old snapshot.

P The signature is what PLSQL_SNAPSHOTS.SIGNATURE holds, what the branch set's database.stored_code[].signature carries (branch-set.schema.json, Contracts § 2), and what the write grant's precondition compares — so it is defined once, versioned by name (SIGNATURE_ALGORITHM = 'PLSQL-SIG-1'), and a later recipe is -2, never a silent change. It generalises the Layer-5 block-checksum recipe the support team already uses by hand F: [4-layer audit § Layer 5] into one value per object:

  1. Source: ALL_SOURCE rows for (owner, name, type) ordered by line; type is one of the eight in CK_PLSQL_TYPE. A spec and its body are two objects, two signatures.
  2. Per-line normalisation: strip CR; strip trailing whitespace; keep leading whitespace, comments and case — a comment's indentation is a change (SD-2183 showed one), and the developer must see it, so the signature does not hide it. Nothing else is rewritten: no keyword folding, no header removal.
  3. Canonical text: the normalised lines joined with LF, with one trailing LF; encoding UTF-8 of the database's NLS text.
  4. Signature: SHA-256(canonical text), lowercase hex, 64 characters. SOURCE_HASH beside it is SHA-256 of the raw ALL_SOURCE text without step 2, so a whitespace-only drift is visible as signature equal, source hash different.
  5. Block hashes (diagnostic, not stored): to locate a difference the skill re-runs the Layer-5 statement — 100-line blocks of ORA_HASH over the normalised lines, then 10-line blocks on the differing block — and reads only those lines; the signature says whether, the blocks say where.

P What the definition settles: a recompile without a change leaves the signature equal (LAST_DDL_TIME moved, nothing else); a CREATE OR REPLACE with an identical body is a no-op for the grant precondition; a Toad mass-compile is invisible to it, as § 3b requires. P The seam catalogue that names which objects are ours to touch (sources in the Whitelabel Catalogue § 4) is configuration, and the branch set lists exactly the objects the brief may write with the signature each had at extraction — the apply refuses when any differs.

PL-4 The brief

P What the human approves at gate H2, before implementation:

P A brief that cannot name the branch set is not ready: it means the case does not yet know which code it is fixing.

Challenges