# Developer agent prompt — Sibyla v5.0, assignment **P1-1a deliverable 2**

Hand this to the coding agent on the **development machine**, working in
`D:\fileStorage\repos\GOTT.Sibyla`, branch `feature/p1-0d-follow-up`. It is written to be
pasted as the opening message of a fresh session.

**Predecessor:** `docs/AGENT-PROMPT-v5-P1-1.md` — still normative for everything it says about
precedence, non-negotiables and P1-1b. This prompt narrows it to what is left, and corrects the
record it left behind. Read that prompt's §Precedence and §Non-negotiables in full; do not rely
on this file's summaries of them.

---

> ## Read this first — one deliverable, one mechanical block, one stale document
>
> **Read `docs/PROJECT-STATE.md` first. It is the only source of project state.** No status
> report, section title or per-entry marker in any other document may be quoted as status —
> including `docs/p1-1a-status.md`, which is known stale (see Step 2 below, and
> `docs/PROJECT-STATE.md` §2.3).
>
> **P1-1a deliverable 1 is done and committed.** `e2f4c2a` on `feature/p1-0d-follow-up`, pushed:
> the single migration `20260805180000_P11aFdrSchema`, `P11aSchemaSql`, the EF model
> configuration, `CodeIdentityStabilityVerifier`, synthetic fixtures, and the disposable-database
> suite. 18 files, 9,665 insertions. Build 0 warnings; 601/601 normal tests; 9/9 `P11aDatabase`
> tests. **Do not re-derive, re-review or "improve" the migration.** If you believe you have found
> a defect in it, report it — do not change it on your own judgement. The single exception is
> stated in "What you may not do" below: if the validation service genuinely cannot be built
> without a schema change, **stop and report before making it**, and make only that change.
>
> **You are here to do exactly one thing: deliverable 2, the validation service.** It was
> scope-blocked in `e2f4c2a`'s own commit message because `Scripts/validate_registry.py` could not
> be read — the prototype clone was not on the machine. **That is a mechanical block with a
> mechanical fix: clone the prototype at the pin.** It is not a design question and it does not
> need a decision.
>
> **The authorization is unchanged and narrow:** write the validation service, exercise it against
> a **disposable** database, prove it on **synthetic** fixtures.
>
> **It still does not cover:** applying a migration to any shared or persistent database,
> importing Luís's live reference data, importing the 2026 history, merging to `main`, or claiming
> P1-1 acceptance. Those are P1-1b and they stay behind the sign-off. If you find yourself about
> to do one, stop and report instead.

---

## Step 0 — clear the block before anything else

This is the whole reason the previous session stopped, and it is the first thing you do.

```
cd D:\fileStorage\repos            # the PARENT of GOTT.Sibyla, not GOTT.Sibyla itself
git clone git@gitlab.com:gottsolutions.dev/sibyla/invoice-skill-build.git
cd invoice-skill-build
git checkout 9359c67c4ef0101218d7e0ffff1986114ba5cc7a
```

The `cd` on the first line is load-bearing. Cloning from inside `GOTT.Sibyla` puts the prototype —
with its real financial data — inside this repository, which rule 1 below forbids.

Rules, all of them already established and none negotiable:

1. **Clone it BESIDE `GOTT.Sibyla`, never inside it.** Never a submodule, never committed here.
   If a clone already exists beside the repository, use it and `git checkout` the pin.
2. **Read-only. Never push, never commit, never open a merge request.** Luís works in it daily.
3. **Pin to `9359c67c4ef0101218d7e0ffff1986114ba5cc7a`** — the same pin P1-0, P1-0c and P1-0d
   used. Record the pin in your status document. If you change it, say why, in writing.
4. **It contains real financial data** — real IBANs, fiscal numbers, salaries, bank statements
   for Gott and Itoorer, committed rather than gitignored. **No real value reaches this
   repository, a commit message, a log line, a test file or your status document.**
5. **Port the behaviour, not the Python.** A script that reads a JSON file and rewrites it is
   solving a problem you do not have.

**If the clone fails** — no SSH key, no network, host unreachable — **stop and report that
precisely**, naming the failure. Do not reconstruct `validate_registry.py` from the backlog
summary, from `docs/project-todo.md`, or from this prompt's check list. The previous session was
correct to stop rather than invent, and that judgement stands. What was missing was the clone,
not the permission.

Read, at the pin:

- `Scripts/validate_registry.py` — **the specification for this deliverable**
- `Scripts/verify_code_identity_stability.py` — already ported; read it only to confirm the port
- `Scripts/add_row_codes.py`, `Scripts/build_bnkmov.py`, `entity_utils.generated_source_index()`
- `Specs/Data Schema/schema.md`
- `Backups/Control Log.md` — worth more than it looks: when a rule seems arbitrary, there is
  usually an entry naming the specific failure that produced it

---

## Step 1 — the validation service

Port the checks. **Read the script; do not reconstruct it from the list below** — the list tells
you what must be present, not what the script does.

Required from the prototype:

- unique DOCLOG filenames
- no `(CodeName, DocumentID)` collisions on payables
- header ↔ detail within 2 cents; no zero-line rows
- every CodeName resolves to ENTMST with a non-blank FiscalNo
- every `(Period, Currency)` in use has an EXCRAT row
- every DOCLOG row has a DocumentType
- Flag ↔ FlagCategory both-or-neither
- every `(DocumentType, OriginClass)` **on a captured document (`Source <> 'BNK'`)** has a DOCTYP
  rule and matches its Treatment — **C9**. The universal form fails on 547 bank-generated rows;
  implementing it universally is a known defect, not a stricter check
- synthetic-FiscalNo count reported as a **warning**, never a failure

Plus the three this project adds — nine ported checks and three added, twelve in total:

- **Orphan DOCLOG** — an EntryCode resolving to no document in either FDCHDR or the
  bank-generated document set. **Report only. Never delete, never propose deleting.** Of the rows
  this flagged in the prototype, most were honest audit traces of superseded generations, some
  were future-dated schedule rows, and **27 concealed a real EUR 142,835.50 gap** — six months of
  2026 payroll with no FDCHDR row at all. A blanket delete would have destroyed the only
  surviving trace.
- **Code uniqueness as a `(Filename, EntryCode)` ↔ LGCode bijection.** LGCode *is* the permanent
  DOCLOG row identity; there is **no `DocLogId`**. The naive "no permanent code may appear
  against more than one EntryCode" test is **wrong under this model, would reject internally
  consistent data, and is explicitly prohibited.**
- **The DOCEFL seed/import completeness assertion — this is the one P1-0e handed forward and it
  is the reason this deliverable matters more than a port.** P1-0e made fourteen DOCEFL columns
  nullable so the three authored rows could land without invented values. The cost: **the schema
  can no longer reject an import that silently drops a value on the 45 imported rules.** The
  `RequireRunnableActiveDecisionDOCEFL` CHECK reaches only *active Decision* detectors — the
  imported set includes Status and Annotation rows it never sees. The guard therefore has to live
  here. **Report it as a failure, not a warning.** Reverting a column to `NOT NULL` is not the
  fix; that is exactly the collision P1-0e resolved, arriving from the other direction.

Design constraints:

- The service is **authenticated deterministic .NET code**, consistent with the v5.0
  non-negotiable that only such code validates, decides, persists and executes side effects.
- Every check reports **failure or warning explicitly**. A check that cannot run reports as a
  failure — silence is not a pass.
- **A metric that improves because a check was removed is a regression, not progress.** If a
  check is inconvenient against the fixtures, the fixtures are wrong, not the check.
- Prove it **clean on synthetic fixtures**. Build the fixtures yourself; never lift real rows.
  Extend the existing `SyntheticRegistryFixtures` / `SyntheticP11aAuthoredRows` rather than
  starting a parallel set. Cover at minimum the seven payroll documents carrying a comma-joined
  list of up to 15 BMCodes, and the two byte-identical statement lines on one day that make the
  occurrence tiebreaker load-bearing — merging them causes *under*-generation.
- Fixtures must also prove the failure direction: a deliberately incomplete DOCEFL row **fails**
  the completeness assertion, an orphan DOCLOG **is reported and not removed**, and a bijection
  violation **fails** while a legitimate multi-EntryCode arrangement passes.

---

## Step 2 — correct the record left by `e2f4c2a`

Three defects in the record, none of them in the schema. Fix all three; none is optional.

1. **`docs/p1-1a-status.md` denies a commit and push that happened.** Its closing line reads
   *"No shared/persistent database migration, live-data import, external deletion, deployment,
   commit, push, merge, or rebase was performed."* Git records `e2f4c2a` committed **and pushed
   to `origin/feature/p1-0d-follow-up` at 17:08:56 UTC**. The file was written before the commit
   and never amended. **Amend it to state what actually happened**, keeping the true parts: no
   shared or persistent database was migrated, no live data imported, no external deletion, no
   deployment, no merge, no rebase. Do not delete the section — correct it and say it was
   corrected.
2. **The commit body of `e2f4c2a` contains literal `\n` escape sequences instead of newlines.**
   Do not rewrite history to fix it — the branch is pushed and the planning documents arrive on
   it from another machine. **Note it in your status document** and make sure your own commits do
   not repeat it.
3. **Record the two intentional constructs so they are never "fixed" by a later session:**
   - the test that sets `sibyla.actor='forged-allocation-actor'` and
     `sibyla.authorities='AllocatePermanentCode'` **forges those GUCs deliberately**, to prove
     authorization does not derive from caller-controllable settings — the function uses the
     server-side trusted principal keyed by `session_user`;
   - `VSTestTestCaseFilter` defaults the test project to `Category!=P11aDatabase` so ordinary
     `dotnet test` runs are deterministic without infrastructure, and
     `scripts\run-p11a-disposable-tests.ps1` overrides it on the command line with
     `Category=P11aDatabase`. **Both halves are intended.**

---

## What you may not do

Report these as blocked rather than attempting them. The reason is the same for all: they depend
on Luís's review of the schema/key mapping against the pinned prototype, and doing them now means
doing them twice.

- importing live reference data from `Invoice Skill Build/Editor/Data/*.json` (ENTMST, ENTALS,
  ITMMST, ITMALS, ENTITM, ITMCLS, MNGACC, SNCACC, COCACC, EXCRAT, DOCTYP, DOCEFL, DOCFLG
  instances, `user_observations.json`)
- the D4 2026 history import (DOCLOG 1,475 · FDCHDR 1,153 · FDCDTL 1,190 as at 2026-08-04 —
  **re-measure, do not quote**)
- applying the migration to any shared or persistent environment
- the P1-1 acceptance criterion — "seeded database reproduces the FDR reference sheets exactly
  through the P1-12 renderer" — unreachable without the seed
- any change to the P1-0 drafts themselves
- any change to the P1-1a migration on your own judgement. This is the one exception to the
  "do not touch the migration" rule at the top, and it is deliberately narrow: if the validation
  service genuinely cannot be built without a schema change, **stop and report before making it**,
  then make only that change and nothing adjacent to it

---

## Repository hygiene

- Stage only files you deliberately changed, **by explicit path**. Never `git add -A`, never
  `git add docs`.
- Focused commits on `feature/p1-0d-follow-up`. **Do not merge to `main`**, do not rebase, do not
  force-push — planning documents arrive on this branch from another machine.
- Keep `.gitattributes` in force and do not touch either line-ending commit.
- Real newlines in commit bodies. See Step 2, item 2.
- The prototype clone lives outside this repository.

---

## Definition of done

- The prototype was cloned at pin `9359c67c4ef0101218d7e0ffff1986114ba5cc7a` and
  `Scripts/validate_registry.py` was **read**, not reconstructed. The pin is recorded.
- The validation service implements every check above, **passes clean on synthetic fixtures**,
  and each check reports failure or warning explicitly.
- The **DOCEFL seed/import completeness assertion exists and reports as a failure**, and a
  fixture proves it fires on a deliberately incomplete row.
- The orphan-DOCLOG check reports and never deletes; a fixture proves it.
- The `(Filename, EntryCode)` ↔ LGCode bijection is implemented as a bijection; a fixture proves
  the prohibited naive form would have been wrong.
- `dotnet build GOTT.Sibyla.slnx` — **0 warnings, 0 errors**.
- `dotnet test tests\Sibyla.Tests\Sibyla.Tests.csproj` — 0 failures, with the `P11aDatabase`
  category excluded by the project default as designed.
- `scripts\run-p11a-disposable-tests.ps1` — green, disposable container removed in `finally`,
  **no residual `sibyla-p11a-*` container after the run** (check, do not assume).
- **No real prototype value appears anywhere** in this repository, its commits, its logs or its
  documents.
- `docs/p1-1a-status.md` is amended per Step 2 item 1 and now agrees with git. `docs/PROJECT-STATE.md` §2.3 is the recorded version of that discrepancy — read it before amending.
- A short `docs/p1-1a-d2-status.md` recording: the pin you read, every check ported and how it
  maps to the prototype's version, **every place the prototype's script disagreed with the P1-0
  drafts and how you resolved it**, the fixtures added, and the exact list of P1-1b items still
  blocked and why.
- **Nothing from the P1-1b list was started.** No shared database was touched. No live reference
  data was imported.

Report back with that status document. **Do not treat P1-1 as accepted** — acceptance needs the
seed, and the seed needs the sign-off.
