# P1-0 draft — FDR to PostgreSQL schema mapping

Status: accepted P1-0 field-level draft; migration work is explicitly out of scope. Prototype schema and behavior
reference: `invoice-skill-build` detached at
`9359c67c4ef0101218d7e0ffff1986114ba5cc7a`. This maps structure and rules only; no prototype row
values are copied except the explicitly authorized controlled DOCEFL code-table rows transcribed
with per-field provenance below.

## 1. Mapping conventions

PostgreSQL tables use the six-character FDR names and quoted PascalCase columns so the on-demand
export can reproduce the FDR column names without an alias registry. EF entities may use idiomatic
C# names. `uuid` surrogate keys are allowed only for internal relational joins; permanent business
codes and proven natural keys remain alternate keys and are never replaced by a surrogate in an
API/export identity.

Type shorthand below: `id=uuid`, `code=varchar(16)`, `text=text`, `short=varchar(256)`,
`date=date`, `instant=timestamptz`, `money=numeric(19,4)`, `rate=numeric(19,8)`, `bool=boolean`,
`json=jsonb`. Required/optional status follows the FDR rule and import reality: identifiers and
keys are required; a source field that can honestly be unknown is nullable and must not receive a
fabricated default. Every mutable operational table additionally has `CreatedAt instant`,
`CreatedBy short`, `UpdatedAt instant`, `UpdatedBy short`, and `Version bigint`; imported rows
record the import process as actor while retaining source dates in their own fields.

All code fields have format checks and unique indexes. All foreign keys use `RESTRICT`; audit,
queue, flag, disposition, and tombstone records never cascade-delete. Currency and country codes
are normalized uppercase. Amounts preserve their signs.

Every controlled domain and CHECK literal used by a pinned import is generated from
`docs/p1-0-vocabularies.md`; imports fail closed on an unlisted value. The annex is regenerated and
diffed at every new prototype pin, and a newly observed literal is a finding. A target-only
extension is permitted only when C1–C19 explicitly decides it (for example D6's distinct
ledger-reference/no-entry result), and it is versioned and validated fail-closed as a target
extension. It is never invented or represented as a pinned value. Similar-looking domains remain
distinct: DOCRQE Priority, DOCEFL/DOCFLG ReviewPriority, and backlog severity are not one type.

### Controlled company scope

`CompanyRegistry` is the supporting bridge to the existing authenticated company model: `Id id
PK`, `CompanyId id FK BusinessEntity.Id` (the company/buyer role), `CompanyCode varchar(128) NOT
NULL`, `Active bool`, `EffectiveFrom instant`, `RetiredAt instant NULL`; unique `CompanyCode`,
unique `(CompanyId, CompanyCode)`, and `CREATE UNIQUE INDEX
ux_company_registry_one_active_per_company ON CompanyRegistry (CompanyId) WHERE Active;` to permit
at most one active registry row per company.
`CompanyCode` is a controlled code, not an entity CodeName. Its stored value preserves the exact
prototype company spelling required for import and export; changing a spelling is a governed
registry migration, never normalization during ingestion.

Every command resolves the existing `BuyerEntityId` from the authenticated server-side
membership/context, uses it as CompanyId, and then
resolves its single active CompanyRegistry row. A payload, filename, model response, bank file, or
import row cannot select company scope. If it carries `Company`/`CompanyCode`, that value is only a
claim to compare byte-for-byte with the resolved controlled code; blank, unknown, inactive,
ambiguous, or mismatched claims fail closed before any financial write. Imports run under an
authenticated company-scoped command and additionally require every preserved source value to
match the declared CompanyRegistry mapping.

Source columns retain their pinned names: fields rendered as `Company` remain `Company`, and fields
rendered as `CompanyCode` remain `CompanyCode`. Each is an FK to
`CompanyRegistry(CompanyCode)`. Financial parent tables expose company-qualified alternate keys and
children use composite FKs so a valid identifier from one company cannot be attached to another:
FDCHDR `(Company, EntryCode)`, BNKACC `(Company, BankAccount)`, BNKMOV `(Company, BMCode)`, and
PAYCTR/RCVCTR `(CompanyCode, FiscalDocumentID)`.
ENTALS, ITMALS, and ENTBNK `Company` fields and ManualEntryEvidence `Company` are direct registry
FKs. DOCLOG, DOCFAI, DOCARC, extraction/import evidence, and integration operations inherit and
cross-check the authenticated company of their Document (`BuyerEntityId`) or ImportBatch anchor
rather than trusting denormalized payload text.

Before any company-scoped composite FK references `Document(Id, BuyerEntityId)`, `Document` must
declare the redundant alternate key `UNIQUE (Id, BuyerEntityId)`; the primary key on `Id` alone
does not remove PostgreSQL's requirement that the referenced column list itself be unique.

In each mapping below, the first field list preserves every rendered FDR source column at the
pin, including its source spelling; text introduced by "plus" is a production-only extension.
Explicitly named source-internal JSON fields (for example ENTMST balances, ITMMST `Service`,
BNKMOV `SourceFile`, and generated-document `SourceKey`) are also retained even though they are
not rendered columns. Export aliases restore accented source headings such as `Descrição classe`,
`Código`, `Nível`, `Conta pai`, and `Flag / Review Notes`; ASCII property names below are storage
names, not a claim that the source headings differ.

### A2 column provenance register

Every declared column has exactly one column-provenance marker. This axis is separate from seed
value provenance (`extracted|authored|absent`). The register is exhaustive by construction:

1. a column named in the `source` column below is marked **`source`**;
2. a column named in the `derived` column below is marked **`derived`**;
3. every other column declared for that table is individually marked **`production`** (the set
   difference is the explicit production register, not an implicit importer default).

No name may occur in both named sets. An importer reads only `source`, computes only `derived`, and
never attempts to read `production`. Tables described as “all columns” apply that marker to each
column in their declaration independently.

| Table | `source` columns | `derived` columns | `production` columns |
|---|---|---|---|
| DOCLOG | LGCode, Date, Filename, EntryCode, Source, Entity, ItemCode, ItemDesc, DocumentType, OriginClass, CaptureQuality, Flag, FlagCategory, RiskFactor, ReviewedBy, ReviewDate | — | every other declared DOCLOG column |
| DOCFAI | FACode, Filename, Date, Category, Reason | — | every other declared DOCFAI column; `FailureCategory` does not exist |
| DOCTYP | DTCode, DocumentType, OriginClass, Treatment, Notes, DocClass | — | every other declared DOCTYP column |
| DOCEFL | FlagReasonCategory, DetectionPattern, FlagCategory, RiskFactor, AutoReviewable, Summary, FlowDimension, EFCode, AppliesToTable, AppliesToField, PolicyReference, RequiredAction, AutoActionAllowed, ReviewOwnerRole, ReviewPriority, BlockingLevel, ResolutionEvidenceRequired, RuleVersion, Active, EffectiveFrom | AutoActionCondition | EnforcementStartsAt, ItemClass and every other target-only column |
| DOCEFLAppliesTo | — | EFCode, SourceOrdinal, AppliesToTable | — |
| DOCFLG | FlagInstanceID, EFCode, Status, RelatedRecordType, RelatedRecordID, SourceReference, DetectedAt, DetectedBy, ReviewOwnerRole, ReviewPriority, BlockingLevel, RequiredAction, ResolutionEvidence, ResolutionNotes, ResolvedBy, ResolvedAt, RecheckDate, InstanceNote, SourceTextHash, PriorRelatedRecordID | IsGrandfathered, GrandfatherReason | ItemClass, HashAlgorithm, SupersededBy, SupersedeReason and every other target-only column |
| DOCRQE | ReviewID, Priority, Status, CreatedDate, Layer, EvaluationFlag, BlockingLevel, SourceRecordType, SourceRecordID, DocID, DocFilename, ReviewType, TargetTable, TargetField, ProposedValue, CurrentValue, AIProposal, UserDecision, UserInput, Observations, DecidedBy, DecisionDate, AppliedReference, ItemKey, ResolutionEvidence | — | Company, ItemClass, SuccessorReviewID, SupersedeReason, MatcherProposalId and every other target-only column |
| UserObservation / UserObservationTarget | ObservationKey, ObservationText, QueueKind, ReviewID, SourceOrdinal | ObservationTextSha256 | every other declared observation column |
| ENTMST | EntityCode, EntityName, FiscalNo, Country, CurrencyCode, CodeName, Role, FlagReviewNotes, InvoiceFrequency, ExpectedInvVal, OpenPayableBalance, OpenReceivableBalance | — | DirectDebit, RelatedParty, RelatedPartyKind, IsProvisional, TaxIdVerificationStatus and every other target-only column |
| ENTALS, ITMMST, ITMALS, ENTITM, ITMCLS, MNGACC, SNCACC, COCACC, ENTBNK, EXCRAT | all columns in each pinned/source declaration, including ITMMST Service and AutoCreated | — | — |
| ENTBNKAccount / ENTBNKMethod | — | all declared columns (lossless expansion of source multi-values) | — |
| FDCHDR | EntryCode, Company, FiscalNo, Period, AccountPeriod, DocumentID, DateDoc, DateDue, DatePay, ItemDesc, ItemCode, EICode, PLMKEY, PLMKO, NetAmount, VATAmount, TotalAmount, Currency, FEX, LocalAmount, Filename, ManualEntry, AutoEntry, DataSource, FlagReviewNotes, CompanyCodeName, ProviderCodeName, ProviderName, EntityCodeName, SourceKey, SourceBMCode | CounterpartyCodeName, FlowType, NormalizedDocumentID, SourceKeyHash, ImportTargetTable | DocumentIdNormalizationVersion and every other declared FDCHDR target/evidence column |
| FdcHdrBankMovement | — | — | all declared columns |
| FDCDTL | EntryCode, FlowType, CodeName, DateDoc, DocumentID, ItemDesc, ItemCode, EICode, PLMKEY, PLMKO, NetAmount, VATAmount, TotalAmount, Filename, FlagReviewNotes | Company | Id, LineNumber, VatRate, ExemptionReason and every other target-only column |
| DOCARC | — | — | all declared columns, including CounterpartyCodeName |
| BNKACC | BankAccount, Company, BankName, AccountName, Currency, AccountType, AccountNumber, IBAN, CreditLimit, SparseStatements, StartDate, EndDate | — | ArchiveAccountToken and every other target-only column |
| BNKMOV | BMCode, Company, BankAccount, Period, DocDate, MovDate, Description, Amount, Currency, FEX, LocalAmount, RunningBalance, LocalBalance, ReferenceNumber, CodeName, ItemCode, ItemDesc, Class, Subclass, PLMKEY, PLMKO, Classified, MatchStatus, MatchedRef, Flag, SourceFile, SourceKey | Occurrence | CompanyId, BankInputEvidenceId and every other target-only column |
| BNKCHK | Period, BankAccount, Currency, InitialBalance, EndBalance, InitialBalanceAvailable, EndBalanceAvailable, Lines, State, Notes | — | Company, BCCode and every other target-only column |
| BNKMAT | BTCode, MatchType, MatchDescription, EntrySource, ReconciliationType, Explanation, AutoProposeAllowed, AutoCloseAllowed, RequiresFDCHDR, RequiresReview, MaxDateWindowDays, ToleranceAmount, ControlFormula, FlagReviewNotes | — | RequiredEvidence, TestCaseReference, Active, RuleVersion and every other target-only column |
| BNKREC | BRCode, MatchGroupID, BMCode, BankAccount, BankDate, BankDescription, BankAmount, BankCurrency, EntryCode, DocumentType, DocumentEntity, DocumentDate, DocumentDesc, DocumentAmount, DocumentCurrency, RecordedMatchStatus (source `MatchType`), Difference, GroupBankTotal, GroupDocumentTotal, GroupDifference, ReconciliationMatchType, Notes, ConsistencyFlag | — | Id, Company, GovernedMatchRuleCode, AppliedMatcherProposalId, FindingDiscriminator and every other target-only column |
| RECREV | ReviewID, Priority, Status, CreatedDate, Layer, EvaluationFlag, BlockingLevel, SourceRecordType, SourceRecordID, DocID, DocFilename, ReviewType, ProposedMatchType, CurrentValue, AIProposal, UserDecision, UserInput, Observations, DecidedBy, DecisionDate, AppliedReference, ItemKey | ProposedMatchBTCode | Company, ItemClass, MatcherProposalId, SuccessorReviewID, SupersedeReason and every other target-only column |
| PAYCTR / RCVCTR | PAYCODE or RCVCODE, CompanyCode, FiscalDocumentID, DocID, EntityCode, DocDate, DueDate, DocAmount, Currency, FEX, LocalAmount, PayEntity, PayBankAccount, PayCurrency, PayDate, PayDocAmount, PaidAmount, PayDocID, BankReference, MatchType, DueAmount, Status | — | ImportPeriod, ImportBatchId, EnteredAt, EnteredBy and every other target-only column |
| PAYDTL / RCVDTL | FiscalDocumentID, DocID, PayEntity, PayBankAccount, PayDate, PayAmount, Currency, BankReference, Source, Tier, ResolvedCodeName, Notes | CompanyCode, PaymentSequence, SequenceTieBreakHash | PayDtlCode or RcvDtlCode, ImportPeriod, ImportBatchId, EnteredAt, EnteredBy and every other target-only column |
| CompanyRegistry, all observation/import/evidence/ledger/support tables, ControlGoverningPeriod, ExportRun and existing-integration extensions | — | generated constants/hashes explicitly declared as generated | every other declared column |

**O9-D1 correction provenance (2026-08-06):** the frozen register above originally named
`ENTMST.DirectDebit` as `source`. The read-only P1-1b check at immutable pin `b917685…` proved that
none of the 119 ENTMST rows has that property. Under
`docs/AGENT-PROMPT-v5-P1-1b-o9-amendment.md`, the historical claim is corrected rather than
silently rewritten: `DirectDebit` is **target-only (`production`), nullable, and has no default**.
Imported historical rows therefore carry honest SQL `NULL` with seed provenance `absent`; any
later governed value carries `authored` provenance.

The five source fields omitted by the earlier mapping are therefore explicit: payables preserve
`CompanyCodeName`, `ProviderCodeName`, and `ProviderName`; receivables preserve `CompanyCodeName`
and `EntityCodeName`. `FDCDTL.LineNumber`, BNKCHK `BCCode`, PAY/RCV detail permanent codes,
DOCRQE `SuccessorReviewID`, and DOCFLG `ItemClass` are production fields. Nothing attempts to read
them from the pin.

### Group-B placement

All schema/integrity items B5–B11 belong to P1-1a's same initial squashable migration train: active
company scope, company-safe polymorphic references, append-only enforcement, the scoped DOCTYP FK,
SNCACC/COCACC FKs, sequence registries, and ArchiveNameLedger directory identity. B2, B3, B4, and
B12 are the go-live governance specifications now recorded in the lifecycle, roles, and archive
drafts; they do not add a second migration train. B1 is closed: byte storage is per capture,
`RetainedContentHash` is a duplicate-detection index and never a storage pointer, reference
counting is not implemented, and purge is capture-scoped storage hygiene rather than erasure. The
hash-index swap is the one explicit exception: its own
reviewed migration is scheduled to P1-1b and is never folded into P1-1a.

## 2. Capture and governance

### DOCLOG

`LGCode code PK`; `Date date`; `Filename text`; `EntryCode code NULL`; `Source varchar(16)`
(`FDR|BNK|MET|CHANNEL|WEB|IMPORT`); `Entity short NULL`; `ItemCode code NULL`; `ItemDesc text NULL`;
`DocumentType short`; `OriginClass varchar(16)`; `CaptureQuality varchar(32) NULL`; `Flag text NULL`;
`FlagCategory varchar(32) NULL`; `RiskFactor text NULL`; `ReviewedBy short NULL`;
`ReviewDate instant NULL`; plus `DocumentId id`, `ExtractionRevisionId id NULL`, `SourceFilename
text`, `SourceChannel varchar(32)`, `SourceLocation text NULL`, `Sha256 char(64)`, `ByteLength
bigint`, `ArchivePath text NULL`, `ArchiveDate instant NULL`, `DocumentState varchar(24)`.

Natural/alternate key: `(Filename, EntryCode)` with PostgreSQL `NULLS NOT DISTINCT`; LGCode is the
per-row identity and no `DocLogId` is introduced. A second failure-only row with the same filename
and blank EntryCode is a collision and is rejected rather than assigned a fabricated identity.
Add unique `(LGCode, DocumentId)` for same-document composite FKs, unique `(DocumentId,
DocumentState)` for disposition-qualified evidence FKs, and unique `(LGCode, DocumentId,
DocumentState)` for official-statement evidence. `DocumentId` links to the existing
intake/evidence aggregate. State is the closed enum Posted /
ReferenceOnly / Discarded / Purged and maps the separate registry disposition; it does not replace
the current operational `Document.Status`. DOCLOG is never deleted.

Pinned-source discrepancy resolved: `schema.md` prose says DOCLOG has `ItemClass` at column 2 and
describes a queue-style `Status`, but its own exact DOCLOG column list and
`build_workbook.py::build_doclog()` both render the 16 fields mapped above and neither field.
Therefore P1-0 does not fabricate them on DOCLOG. D8 ItemClass belongs on emitted findings/review
items (including the duplicate-capture Annotation), where its lifecycle is enforceable.

### DOCFAI

`FACode code PK`; `Filename text`; `Date date`; `Category varchar(32)`; `Reason text`;
`DocumentId id NULL`;
`ExtractionAttemptId id NULL FK ExtractionAttempt`; `ImportEvidenceRowId id NULL FK
ImportEvidenceRow`; `LGCode code NULL FK DOCLOG`; `FailureKind varchar(24)`
(`MalformedResponse|ValidFailureResponse|ImportedFailure`);
`ResolvedAt instant NULL`.

Every native DOCFAI row has DocumentId plus a restrictive composite
`(ExtractionAttemptId, DocumentId)` FK and is idempotent under a partial unique index on
`(ExtractionAttemptId, FailureKind) WHERE ExtractionAttemptId IS NOT NULL`. `MalformedResponse`
requires LGCode NULL because invalid JSON creates neither ExtractionRevision nor DOCLOG.
`ValidFailureResponse` includes schema-conformant `NOT_A_DOCUMENT` and requires a non-NULL LGCode;
a restrictive `(LGCode, DocumentId)` FK proves that the attempt and normal extraction-derived
DOCLOG row belong to the same Document. Imported failures use `ImportEvidenceRowId`, leave the two
native anchors NULL, preserve the pinned FACode/`Filename` source key under a partial unique index
on `Filename WHERE FailureKind='ImportedFailure'`, and may have LGCode only where the source
provides the exact link. The mode check permits exactly one native or imported branch; it never
manufactures an anchor. Terminal evidence is retained.

### DOCTYP

`DTCode code PK`; `DocumentType short`; `OriginClass varchar(16)`; `Treatment varchar(24)`
(`Include|Exclude`); `Notes text NULL`; `DocClass varchar(24)`; plus `ProcessingRoute varchar(24)`
(`PostFiscal|ArchiveOnly|ReferenceOnly|Review`), `FiscalEligible bool`,
`FiscalEligibilityCondition text NULL`, `HeaderOnlyAllowed bool`, `Active bool`, `EffectiveFrom
instant`. Unique `(DocumentType, OriginClass)` and unique `(DTCode, DocClass, DocumentType,
ProcessingRoute)` for classification-qualified archive FKs. Conditional source values set
`FiscalEligible=true` and retain their condition separately; a condition is forbidden when the
boolean is false. Pinned rows retain `Payables|Receivable`; target-only non-fiscal DocClass and
ProcessingRoute values are explicit extensions, never import translations. The seeded taxonomy
is in `docs/p1-0-codes-taxonomy-archive.md`. Every multi-valued `Allowed OriginClass` source cell
is expanded losslessly into one row per `(DocumentType, OriginClass)` pair; a DOCTYP row never stores
a list in OriginClass.

O8-D2 keeps the governed DOCTYP import manifest at exactly the 19 transcribed rows. At immutable
pin `b91768513fc638381fbde91f0b576b08220a98f6`, `Editor/Data/document_type_rules.json` blob
`b7560e9ea0c403e608b73ed3a3a4f6b53ce956cb` contains those same 19 rows plus a twentieth source
row, `Bank Statement | External | Exclude`, with blank `DTCode`. The twentieth row is explicitly
excluded: importing it would require inventing a permanent code, which is forbidden. The accepted
authored target rule remains `Bank Statement | External | Include | ArchiveOnly`; it is not an
import translation of the excluded source row.

### DOCEFL

`EFCode code PK`; `FlagReasonCategory short`; `DetectionPattern text NULL`; `FlagCategory
varchar(32) NULL`; `RiskFactor text NULL`; `AutoReviewable bool NULL`; `Summary text NULL`;
`FlowDimension varchar(64) NULL`; `AppliesToField varchar(64) NULL`; `PolicyReference text NULL`;
`RequiredAction text NULL`; `AutoActionAllowed bool NULL`; `AutoActionCondition text NULL`;
`ReviewOwnerRole varchar(64) NULL`; `ReviewPriority varchar(32) NULL`; `BlockingLevel varchar(40)
NULL`; `ResolutionEvidenceRequired text NULL`; `RuleVersion varchar(32)`; `Active bool`;
`EffectiveFrom instant NULL`; plus `EnforcementStartsAt instant NULL` and `ItemClass varchar(16)`.

The authored-row decisions against those declarations are:

| Row | Column | Route | Declaration reason |
|---|---|---:|---|
| `EF0000000` | `FlagCategory` | 1 | NULL: a no-detector reference has no rule category. |
| `EF0000000` | `RiskFactor` | 1 | NULL: a reference rule may have no assessed risk. |
| `EF0000000` | `AutoReviewable` | 1 | NULL: unspecified automation grants no authority. |
| `EF0000000` | `Summary` | 1 | NULL: a rule need not duplicate its category in prose. |
| `EF0000000` | `FlowDimension` | 1 | NULL: a no-detector reference has no dimension. |
| `EF0000000` | `PolicyReference` | 1 | NULL: a reference rule may cite no policy. |
| `EF0000000` | `RequiredAction` | 1 | NULL: a reference rule may require no action. |
| `EF0000000` | `AutoActionAllowed` | 1 | NULL: unspecified automation grants no authority. |
| `EF0000000` | `ReviewOwnerRole` | 1 | NULL: a non-Decision reference needs no owner. |
| `EF0000000` | `ReviewPriority` | 1 | NULL: a non-Decision reference needs no priority. |
| `EF0000000` | `ResolutionEvidenceRequired` | 1 | NULL: a reference rule may require no evidence. |
| `EF0000000` | `EffectiveFrom` | 1 | NULL: it has no FDR authoring time. |
| `EF0000000` | `EnforcementStartsAt` | 1 | NULL: the non-blocking Annotation enforces nothing. |
| `EF0000053` | `FlagCategory` | 1 | NULL while inactive; activation supplies it. |
| `EF0000053` | `RiskFactor` | 1 | NULL: a rule may have no assessed numeric risk. |
| `EF0000053` | `AutoReviewable` | 1 | NULL: unspecified automation grants no authority. |
| `EF0000053` | `FlowDimension` | 1 | NULL: no governed dimension was decided. |
| `EF0000053` | `PolicyReference` | 1 | NULL: C12 cites no governed policy. |
| `EF0000053` | `AutoActionAllowed` | 1 | NULL: unspecified automation grants no authority. |
| `EF0000053` | `ReviewOwnerRole` | 1 | NULL while inactive; activation supplies it. |
| `EF0000053` | `ReviewPriority` | 1 | NULL while inactive; activation supplies it. |
| `EF0000053` | `BlockingLevel` | 1 | NULL while inactive; activation supplies it. |
| `EF0000053` | `EffectiveFrom` | 1 | NULL: it has no FDR authoring time. |
| `EF0000053` | `EnforcementStartsAt` | 1 | NULL while inactive; activation supplies it. |

These columns are optional for every rule, not only for an authored seed row: a reference-only
rule may have no detector category, risk, automation behavior, summary, flow, policy, action,
routing, blocking effect, evidence requirement, or FDR authoring time. A NULL boolean means
unspecified and grants no automation authority. `EnforcementStartsAt` is NULL for a rule that
enforces nothing or has not been activated; it is mandatory at activation for an enforcing
detector. `DetectionPattern`, `AppliesToField`, and `AutoActionCondition` retain their existing
optional meanings.

Unique `NULLS NOT DISTINCT (FlagReasonCategory, FlowDimension)`: two rows with the same category
and no dimension still collide, so making `FlowDimension` nullable does not weaken category
identity under PostgreSQL's default NULL behavior. RuleVersion changes never change EFCode;
deactivation replaces deletion. Every
detector declares closed `ItemClass=Decision|Status|Annotation` before activation.
Add unique `(EFCode, ItemClass)` so DOCFLG can enforce rule/instance class consistency with a
restrictive composite FK. Conditional source values set `AutoActionAllowed=true` and preserve the
qualification in `AutoActionCondition`; a condition is forbidden when the boolean is false.
An active Decision detector must have non-NULL `FlagCategory`, `ReviewOwnerRole`, `ReviewPriority`,
`BlockingLevel`, and `EnforcementStartsAt`; the named
`RequireRunnableActiveDecisionDOCEFL` CHECK enforces that implication. The authenticated,
company-scoped and audited `ActivateDOCEFLRule` command supplies those five governed values in one
operation and refuses activation if any is absent.
`EffectiveFrom` preserves the FDR rule-authoring timestamp. `EnforcementStartsAt` is the distinct
Sibyla enforcement timestamp and is never derived from it. The 52 imported rules receive the
Sibyla go-live timestamp as `EnforcementStartsAt`. The named
`RejectDOCEFLEnforcementStartsAtMutation` trigger rejects an UPDATE of `EnforcementStartsAt` after
the rule has been imported or first referenced by DOCFLG. Persisted `IsGrandfathered` verdicts are
never recomputed. Correcting an enforcement date means issuing a new DOCEFL rule version with its
own EFCode and enforcement date; there is no recompute path or recompute audit table.

`DOCEFLAppliesTo`: `EFCode code FK DOCEFL`, `SourceOrdinal integer NOT NULL`, `AppliesToTable
varchar(32)`; PK `(EFCode, SourceOrdinal)`, unique `(EFCode, AppliesToTable)`, and CHECK
`SourceOrdinal >= 1`. Each semicolon-separated pinned source value expands left-to-right into one
child row per table; the renderer reconstructs `DOCEFL.AppliesToTable` by `ORDER BY SourceOrdinal`.
Neither the rule table nor the child table stores a multi-valued cell.

#### Extracted 52-row DOCEFL base seed

Source: `Editor/Data/flag_evaluation.json` at prototype pin
`b91768513fc638381fbde91f0b576b08220a98f6`, blob
`22252b90a52b2a114cfbd74de0d6bd0272fc5b0e`.

The ordered field list is `FlagReasonCategory`, `DetectionPattern`, `FlagCategory`, `RiskFactor`,
`AutoReviewable`, `Summary`, `FlowDimension`, `EFCode`, `AppliesToTable`, `AppliesToField`,
`PolicyReference`, `RequiredAction`, `AutoActionAllowed`, `ReviewOwnerRole`, `ReviewPriority`,
`BlockingLevel`, `ResolutionEvidenceRequired`, `RuleVersion`, `Active`, `EffectiveFrom`.
Every property value in every JSON row below is independently marked **`extracted`** by this scoped
per-field declaration. Values are verbatim; JSON escaping is representational only. A source
property that is absent is honestly absent; no target value is inferred from it. The target-only
`EnforcementStartsAt` and `ItemClass` fields remain governed outside the source transcription.

```json
{"FlagReasonCategory":"Date proxy (file mtime)","DetectionPattern":"Date = file's last-modified timestamp","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"Explains that the DocumentLog Date column reflects the source file's last-modified timestamp on disk rather than a true receipt/email date, because this batch came from a local folder with no receipt metadata. Purely a provenance note about the Date column itself - does not affect any extracted amount, party, or classification, so it carries no accuracy or completeness risk.","FlowDimension":"Document Registry","EFCode":"EF0000001","AppliesToTable":"DOCLOG","AppliesToField":"Date","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Record provenance note","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Source file metadata","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Duplicate detected & excluded","DetectionPattern":"DUPLICATE of","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"A file was identified as a duplicate of another already-logged invoice - matched on the objective (ProviderCodeName, InvoiceNo) pair, or on confirmed byte-identical/near-identical content - and excluded from InvoiceHeader/InvoiceDetails to avoid double-counting. High-confidence, evidence-based exclusion; worth a human's awareness but not urgent review.","FlowDimension":"Document Registry","EFCode":"EF0000002","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"DocumentID; Filename","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Link duplicate and exclude from posting","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Before Posting","BlockingLevel":"Block Financial Entry","ResolutionEvidenceRequired":"Duplicate key and original source file","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"DocumentType reclassified from explicit source text","DetectionPattern":"Reclassified DocumentType","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"The document's own printed text unambiguously states its type (e.g. 'this document is not an invoice'), which overrode the default heuristic classification. High confidence since the evidence is the document's own explicit statement, not an inference.","FlowDimension":"Document Registry","EFCode":"EF0000003","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"DocumentType","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Apply explicit type and retain source evidence","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Before Posting","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Printed document text","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Outgoing/Internal origin confirmed","DetectionPattern":"Outgoing/accounts-receivable|Outgoing / accounts-receivable","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"The document's extraction notes explicitly identify one of our own entities as the issuer billing a third party - an outgoing/accounts-receivable document, not a payable. Explicit textual evidence, not a guess.","FlowDimension":"Document Registry","EFCode":"EF0000004","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"OriginClass","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Set origin and route to receivable flow","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Before Posting","BlockingLevel":"Block Financial Entry","ResolutionEvidenceRequired":"Issuer/recipient evidence on document","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"DocumentType assumed from indirect evidence","DetectionPattern":"DocumentType assumed","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"DocumentType was inferred from indirect signals (e.g. payment terms like 'Pronto Pagamento') rather than an explicit statement on the document itself. The true category could differ if those signals don't reflect what actually happened - worth a periodic human check, not urgent.","FlowDimension":"Document Registry","EFCode":"EF0000005","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"DocumentType","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Mandatory review and confirm type","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Financial Entry","ResolutionEvidenceRequired":"Document plus reviewer decision","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"DocumentType uncertain (ambiguous boilerplate)","DetectionPattern":"DocumentType uncertain","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"The source document contains boilerplate language that could suggest a different DocumentType, but nothing on the document actually confirms it, so classification was left at the more conservative default. A human with more context on the provider's conventions may be able to resolve it faster than another automated pass.","FlowDimension":"Document Registry","EFCode":"EF0000006","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"DocumentType","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Mandatory review and classify or reject","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Financial Entry","ResolutionEvidenceRequired":"Document plus reviewer decision","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"OriginClass defaulted without a signal","DetectionPattern":"OriginClass defaulted","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"OriginClass could not be confirmed from the available extraction notes and was defaulted to External (the overwhelming majority case for this kind of batch) rather than guessed as Internal without evidence. Worth confirming, especially if the underlying document is unreadable or has insufficient data.","FlowDimension":"Document Registry","EFCode":"EF0000007","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"OriginClass","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Mandatory review and confirm origin","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Financial Entry","ResolutionEvidenceRequired":"Document issuer/recipient evidence","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Sanity check failed (provider is one of our own entities)","DetectionPattern":"sanity check failed","FlagCategory":"High Risk","RiskFactor":4,"AutoReviewable":"No","Summary":"A document landed in InvoiceHeader as a payable, but its provider resolves to one of our own Company-role entities rather than a third party - looks like an outgoing document was mis-included as a payable. Could directly overstate accounts payable if not corrected.","FlowDimension":"Document Registry","EFCode":"EF0000008","AppliesToTable":"FDCHDR; ENTMST","AppliesToField":"EntityCode; OriginClass","PolicyReference":"Financial Document Entry Policy","RequiredAction":"Stop posting; validate direction and correct linkage","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Immediate","BlockingLevel":"Block Financial Entry","ResolutionEvidenceRequired":"Entity identity, document parties and reviewer decision","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Conflicting status between copies","DetectionPattern":"conflicting status|flag for human review|Anulada","FlagCategory":"High Risk","RiskFactor":4,"AutoReviewable":"No","Summary":"Two copies of what appears to be the same document disagree on status (e.g. one copy is stamped cancelled/'Anulada' while the other is not) - could mean the invoice should not be treated as a payable at all. Needs a human decision before the amount is trusted.","FlowDimension":"Document Registry","EFCode":"EF0000009","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"Flag (document status conflict)","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Mandatory review; select valid copy or reject","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Immediate","BlockingLevel":"Block Financial Entry","ResolutionEvidenceRequired":"All copies and reviewer decision","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Routed to FailureTable","DetectionPattern":"FailureTable:","FlagCategory":"High Risk","RiskFactor":4,"AutoReviewable":"No","Summary":"The document could not be processed as a normal payable and was excluded entirely from InvoiceHeader/InvoiceDetails (unreadable, insufficient data, or not a payable at all). If the exclusion reasoning turns out to be wrong, a real payable could be missing from the registry - the highest-consequence category, always needs a human look.","FlowDimension":"Document Registry","EFCode":"EF0000010","AppliesToTable":"DOCLOG; DOCFAI","AppliesToField":"CaptureQuality","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Resolve failure or retain documented rejection","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Immediate","BlockingLevel":"Block Document","ResolutionEvidenceRequired":"Original document, error evidence and review decision","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Unclassified","DetectionPattern":"(fallback - no pattern matched)","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"Fallback for a Flag whose text doesn't match any rule above - a new flag wording or scenario nobody has categorized yet. Defaults to Medium Risk (better to over-flag an unknown than silently trust it) and needs a human to either resolve the underlying item or add a new rule to this table so future occurrences classify automatically.","FlowDimension":"Document Registry","EFCode":"EF0000011","AppliesToTable":"DOCLOG","AppliesToField":"Flag","PolicyReference":"Skill Review and Update Procedure","RequiredAction":"Create rule or resolve as exception","AutoActionAllowed":"No","ReviewOwnerRole":"Process Owner","ReviewPriority":"Before Posting","BlockingLevel":"Block Financial Entry","ResolutionEvidenceRequired":"Underlying source, analysis and new-rule decision","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"FiscalNo Missing — Synthetic FiscalNo Applied","DetectionPattern":"FiscalNo Missing|SYNTHETIC placeholder|synthetic placeholder|No .*(tax ID|CNPJ|VAT).* printed|no tax ID found|no tax ID/EIN printed","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"A synthetic FiscalNo in the approved [ISO2]-SYN-[SequentialNumber] format is an ACCEPTED OPERATIONAL OUTCOME for an entity established outside the European Union when no genuine fiscal/tax number is available from the reliable source documents, the formal name and country identify the entity safely, and ENTMST/ENTALS were searched first (no duplicate). Not an error, exception, or blocking condition (Stage 9 Round 2 clarification -- was High Risk under v1.x). A synthetic id is internal-only: never a verified tax identifier, never exported to statutory/tax/external systems as official. If a genuine number is later obtained, replace it through a controlled Sec6 update with audit history and duplicate check. EU-country, unclear-identity, duplicate-risk, or insufficient-evidence cases do NOT qualify -- they raise EF0000032 instead (build_docflg.py's data-aware qualifier routes between the two; this rule's text patterns alone do not decide).","FlowDimension":"Entity Item Classification","EFCode":"EF0000012","AppliesToTable":"ENTMST","AppliesToField":"FiscalNo; Country; Flag / Review Notes","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Create and record approved synthetic FiscalNo","AutoActionAllowed":"Yes (provided all Entity Entry Flow requirements are verified)","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Country, formal entity name, source document reference, and confirmation that no genuine FiscalNo is available","RuleVersion":"2.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Entity Name/Identity Unresolved","DetectionPattern":"Entity Name Unknown|Identified only by initials|NOT a real counterparty|no full name.*available|Entity Identity Unresolved","FlagCategory":"High Risk","RiskFactor":4,"AutoReviewable":"No","Summary":"The entity's real legal name/identity isn't fully established - either it's known only by initials or a generic label with no company name at all, or the party actually invoicing is a reseller/dealer/marketplace merchant that may differ from the recognizable brand kept as CodeName. This blocks confident deduplication (a second, differently-labeled document for the same real party could get booked as a new entity) and should be resolved before relying on downstream company-level totals.","FlowDimension":"Entity Item Classification","EFCode":"EF0000013","AppliesToTable":"ENTMST; ENTALS","AppliesToField":"EntityName; FiscalNo","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Mandatory review; identify entity or keep unresolved","AutoActionAllowed":"No","ReviewOwnerRole":"Master-Data Reviewer","ReviewPriority":"Immediate","BlockingLevel":"Block Classification","ResolutionEvidenceRequired":"Reliable source document and identity evidence","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Non-Standard FiscalNo Scheme","DetectionPattern":"EU VAT MOSS|EU OSS|EU-prefixed|Business ID|[Cc]ompany registration number|non-standard.*(VAT|format)|CNPJ","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"The tax identifier on file is real but doesn't follow the plain national-VAT shape this registry normally expects (an EU MOSS/OSS scheme number, a non-EU company registration number standing in for a VAT number, or a foreign CNPJ normalized with a 'BR' prefix). Worth a periodic confirmation that the identifier is being read/used correctly, but it's a real, sourced ID - not a missing one.","FlowDimension":"Entity Item Classification","EFCode":"EF0000014","AppliesToTable":"ENTMST","AppliesToField":"FiscalNo","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Validate identifier type and retain source note","AutoActionAllowed":"No","ReviewOwnerRole":"Master-Data Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Classification","ResolutionEvidenceRequired":"Original fiscal identifier and country context","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Legal Entity vs Billing Branch/Country Mismatch","DetectionPattern":"branch|registered in [A-Z]|legal HQ|R\\.C\\.S\\.|different country's tax ID","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"The entity's Country/FiscalNo reflect the specific local branch or subsidiary that actually issued the invoice, which differs from where the group's legal headquarters is registered. Recorded deliberately (the branch is who actually charged the tax), but worth a periodic check that this doesn't cause the same real corporate group to be split across more than one EntityMaster row.","FlowDimension":"Entity Item Classification","EFCode":"EF0000015","AppliesToTable":"ENTMST; ENTALS","AppliesToField":"Country; FiscalNo","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Validate billing entity and add alias if needed","AutoActionAllowed":"No","ReviewOwnerRole":"Master-Data Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Classification","ResolutionEvidenceRequired":"Invoice issuer, fiscal number and branch evidence","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Brand/Trading Name vs Legal EntityName Divergence","DetectionPattern":"trading as|brand/franchise|CodeName.*(diverge|differ)|reads as a brand","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"The recognizable brand/trading name kept as CodeName differs from the full legal EntityName printed on the source document (a franchise tag, a shortened trade name, or a holding-company suffix). Low risk - the distinction is already captured and doesn't affect matching, since CodeName is the stable join key.","FlowDimension":"Entity Item Classification","EFCode":"EF0000016","AppliesToTable":"ENTMST; ENTALS","AppliesToField":"EntityName; CodeName","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Create or update validated entity alias","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Document legal name and trading name","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Dual Role (also billed as Customer/Provider)","DetectionPattern":"Also appears as a ReceivableHeader customer|Role stays Provider|Role is not an exclusivity constraint","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"This entity is billed in more than one direction (e.g. an existing AP vendor is also occasionally billed as an AR customer, or vice versa). Purely informational - Role reflects the dominant/original relationship and this doesn\u0027t affect entity identity or reconciliation.","FlowDimension":"Entity Item Classification","EFCode":"EF0000017","AppliesToTable":"ENTMST","AppliesToField":"Role","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Add permitted secondary role where supported","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Document-direction evidence","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Currency Doesn\u0027t Match Home Country","DetectionPattern":"Invoices in USD despite being.*based|CurrencyCode reflects what the invoice actually uses|billed in USD","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"The entity\u0027s invoicing currency doesn\u0027t match what its home country would suggest (e.g. a Brazil-based provider invoicing in USD). Confirmed from the invoices themselves, not assumed - low risk, but worth a glance if it ever looks like a data-entry slip rather than a real cross-border billing choice.","FlowDimension":"Entity Item Classification","EFCode":"EF0000018","AppliesToTable":"ENTMST","AppliesToField":"CurrencyCode","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Validate main document currency and update if supported","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Accepted documents showing recurring currency","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Unclassified","DetectionPattern":"(fallback - no pattern matched)","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"Fallback for an ENTMST Flag whose text doesn\u0027t match any EntityCreation rule above - a new flag wording or scenario nobody has categorized yet. Defaults to Medium Risk and needs a human to either resolve the underlying entity or add a new rule to this table so future occurrences classify automatically.","FlowDimension":"Entity Item Classification","EFCode":"EF0000019","AppliesToTable":"ENTMST","AppliesToField":"Flag","PolicyReference":"Skill Review and Update Procedure","RequiredAction":"Create rule or resolve as controlled exception","AutoActionAllowed":"No","ReviewOwnerRole":"Process Owner","ReviewPriority":"Before Posting","BlockingLevel":"Block Classification","ResolutionEvidenceRequired":"Underlying source, analysis and new-rule decision","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"DocLog entry backfilled by safety net (sync_doclog.py)","DetectionPattern":"sync_doclog\\.py safety net","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"This DocumentLog row was created (or its Source/EntryCode backfilled) by sync_doclog.py\u0027s own safety-net pass, which sweeps up documents that reached FDCHDR/BNKMOV through an earlier pipeline path without ever getting a DOCLOG row of their own. Purely a provenance note about how the row entered the log - the underlying document was already captured and processed through its normal path, so this carries no accuracy or completeness risk on its own.","FlowDimension":"Document Registry","EFCode":"EF0000020","AppliesToTable":"DOCLOG","AppliesToField":"Source; EntryCode","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Record provenance note","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Pipeline provenance (sync_doclog.py safety-net pass)","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Date proxy (document\u0027s own stated Date field)","DetectionPattern":"Date = document\u0027s own stated Date field","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"A sibling of EF0000001 - explains that the DocumentLog Date column was set from the document\u0027s own stated Date field (DateDoc or equivalent) rather than a file\u0027s last-modified timestamp. Purely a provenance note about the Date column\u0027s source, not an accuracy concern - no extracted amount, party, or classification is affected.","FlowDimension":"Document Registry","EFCode":"EF0000021","AppliesToTable":"DOCLOG","AppliesToField":"Date","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Record provenance note","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Document\u0027s own stated date field","RuleVersion":"1.1","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Manual entry upgraded to document-backed row","DetectionPattern":"manual entry upgraded|Replaces MANUAL-ENTRY placeholder|upgraded from MANUAL-ENTRY|previously only captured as a MANUAL-ENTRY|real source document behind MANUAL-ENTRY|UPGRADED [^:]{0,60}\\(Document wins\\)","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"A row that started as a MANUAL-ENTRY placeholder (created from a bank movement or reconciliation input before the real source document was on file) was later matched to, and replaced/upgraded by, its actual source document per the project\u0027s Document-wins merge rule. High-confidence, evidence-based resolution - worth a human\u0027s awareness but not urgent review.","FlowDimension":"Document Registry","EFCode":"EF0000022","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"EntryCode; DocumentID","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Confirm Document-wins merge and retain replaced-placeholder reference","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Original placeholder reference and replacing source document","RuleVersion":"1.2","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Routine batch capture provenance note","DetectionPattern":"captured from Inputs/Documents batch|capture batch \\(Inputs/Documents|captured from Inputs/Documents|captured per user instruction on","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"Benign provenance note recording that the row was captured from a normal Inputs/Documents batch run, usually followed by document-specific extraction detail (VAT exemption notes, policy/contract references, kept-vs-excluded reasoning). The capture itself followed the standard flow -- the note explains where the row came from, not an open question about it.","FlowDimension":"Document Registry","EFCode":"EF0000023","AppliesToTable":"DOCLOG","AppliesToField":"Flag","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Record provenance note","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Source batch reference and captured document","RuleVersion":"1.2","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Cancelled (Anulado) document reclassified as negative line","DetectionPattern":"Anulado/Cancelled document now recorded as a negative-value line","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"A cancelled (Anulado) document is deliberately recorded as a negative-value line in the receivable flow instead of being excluded to the FailureTable -- the Stage 3 Round 6 treatment governed by document_type_rules. Evidence-based, already-applied treatment; worth awareness, not urgent review.","FlowDimension":"Document Registry","EFCode":"EF0000024","AppliesToTable":"DOCLOG; FDCHDR; FDCDTL","AppliesToField":"DocumentType; NetAmount","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Verify negative-line treatment matches document_type_rules","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Cancelled document and its negative-value line","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Recurring series continuation / late-arriving document","DetectionPattern":"continues the existing monthly|late-arriving 20[0-9][0-9] document","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"The document continues an already-established recurring series (same policy/contract, prior instalments captured) or is a late-arriving prior-year document confirmed as genuinely new (no existing FDCHDR row references it). Provenance note about series membership and arrival timing, not an open question.","FlowDimension":"Document Registry","EFCode":"EF0000025","AppliesToTable":"DOCLOG","AppliesToField":"Flag","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Record provenance note","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Series reference (policy/contract) on the document","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Running-account balance excluded from capture","DetectionPattern":"RUNNING-ACCOUNT TRAP","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"The invoice\u0027s own payable total bundles a carried-forward unpaid balance from a prior invoice on top of the current month\u0027s new charge (SKILL.md Section 5 running-account trap); only the own-month charge was captured to avoid double-counting the carried balance. Documented, already-applied treatment -- worth a periodic confirmation that the split was read correctly.","FlowDimension":"Document Registry","EFCode":"EF0000026","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"NetAmount; TotalAmount","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Confirm only the own-month charge was captured (no carried-forward double count)","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Invoice showing carried-forward vs own-month amounts","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Cross-document identity ambiguity resolved (date/number collision)","DetectionPattern":"Three different dates appear|coincides with an unrelated existing invoice","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"The document presented an identity ambiguity -- multiple candidate dates, or a document number colliding with an unrelated provider\u0027s invoice -- that was investigated and resolved with evidence (confirmed genuinely different document, date source selected and recorded). Resolution note, not an open conflict.","FlowDimension":"Document Registry","EFCode":"EF0000027","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"Date; DocumentID","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Retain disambiguation evidence","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Both candidate documents/dates and the disambiguation rationale","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"DirectDebit payment-method note","DetectionPattern":"DirectDebit vendor","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"The entity is paid via automatic recurring direct debit (Stage 4 Round 4 convention): clean reconciliation is common for this payment method, and the note exists so a real reconciliation difference on such a vendor is treated as signal, not noise. Payment-method provenance, not an identity or accuracy issue.","FlowDimension":"Entity Item Classification","EFCode":"EF0000028","AppliesToTable":"ENTMST","AppliesToField":"DirectDebit","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Record payment-method note","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Recurring debit evidence on bank movements","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Internal control / statutory counterparty entity (auto-created per instruction)","DetectionPattern":"Auto-created Stage|seeded per explicit instruction","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"The entity was deliberately created per explicit instruction to give a class of bank movements or statutory payments a real ENTMST/FDCHDR document trail (bank costs, stamp duty, financing counterparty, tax authority). Not sourced from a third-party invoice by design -- the creation instruction is the evidence.","FlowDimension":"Entity Item Classification","EFCode":"EF0000029","AppliesToTable":"ENTMST","AppliesToField":"EntityCode","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Retain creation instruction reference","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Explicit instruction and purpose of the internal entity","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Entity identity externally validated or corrected per instruction","DetectionPattern":"confirmed via public business-registry lookup|CodeName corrected from|Renamed '","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"The entity's identity data was strengthened after creation -- a real fiscal number confirmed via a public business-registry lookup, or a CodeName/name corrected or renamed per explicit instruction (e.g. country-specific legal entities of the same brand). Records the validation/correction event and its source; no open identity question remains.","FlowDimension":"Entity Item Classification","EFCode":"EF0000030","AppliesToTable":"ENTMST; ENTALS","AppliesToField":"EntityName; CodeName; FiscalNo","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Retain validation/correction evidence","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Registry lookup result or correction instruction","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Informal/affiliate provider pattern","DetectionPattern":"Informal commission/affiliate provider","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"Yes","Summary":"The entity is an informal commission/affiliate provider (same pattern as the established Diego Berlitz / Lari Intercambio / Viajando Com Amor group): documents are accepted and evidenced, but formal identity data may be thinner than a registered company's. Worth a periodic check that formal identity data gets captured when it appears on future documents.","FlowDimension":"Entity Item Classification","EFCode":"EF0000031","AppliesToTable":"ENTMST","AppliesToField":"EntityName; FiscalNo","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Monitor for formal identity data on future documents","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Accepted informal-provider documents","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Synthetic FiscalNo — Requirements Not Met","DetectionPattern":"(data check: synthetic FiscalNo present and Entity Entry Flow requirements not met)","FlagCategory":"High Risk","RiskFactor":4,"AutoReviewable":"No","Summary":"A synthetic FiscalNo is recorded but the Entity Entry Flow Procedure's conditions are NOT met: the entity is established inside the European Union (a genuine fiscal number must exist and must be sourced), or the country/identity is unclear, or duplicate risk exists, or the available document evidence is insufficient. Not detected by text regex -- build_docflg.py's data-aware qualifier raises this instead of EF0000012 by checking the actual FiscalNo shape, Country, and EU membership. The synthetic id may remain temporarily for join integrity, but classification use for NEW records is blocked until a genuine number is sourced or the identity is formally resolved/waived with evidence.","FlowDimension":"Entity Item Classification","EFCode":"EF0000032","AppliesToTable":"ENTMST","AppliesToField":"FiscalNo; Country; EntityName","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Obtain genuine fiscal number or formally resolve entity identity; synthetic FiscalNo not permitted in this case","AutoActionAllowed":"No","ReviewOwnerRole":"Master-Data Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Classification","ResolutionEvidenceRequired":"Genuine fiscal number from a reliable source or public registry, or documented identity resolution/waiver with duplicate check","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"FlagReasonCategory":"Controlled FiscalNo update (Sec6 audit note)","DetectionPattern":"FiscalNo updated per Entity Entry Flow Procedure","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"The entity carries a Sec6-compliant controlled-update audit note recording a FiscalNo change (a genuine number applied with registry evidence, an ISO2-prefix normalization, or a legacy-synthetic migration to the approved [ISO2]-SYN- format): previous value, new value, reason, source, and process are all recorded in the note itself. The note is the audit trail the Entity Entry Flow Procedure requires -- a provenance record of a completed, evidenced update, not an open question.","FlowDimension":"Entity Item Classification","EFCode":"EF0000033","AppliesToTable":"ENTMST","AppliesToField":"FiscalNo; Flag / Review Notes","PolicyReference":"Entity Entry Flow Procedure","RequiredAction":"Retain controlled-update audit note","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Previous and new value, reason, and source recorded in the note","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-07-31"}
{"EFCode":"EF0000034","FlagReasonCategory":"Reconciliation Broken Link","DetectionPattern":"data-aware (build_docflg.py reconciliation detector)","FlagCategory":"High Risk","RiskFactor":8,"AutoReviewable":"No","Summary":"Matched movement references an EntryCode that does not exist","FlowDimension":"Bank Reconciliation","AppliesToTable":"BNKMOV","AppliesToField":"MatchStatus, MatchedRef","PolicyReference":"Bank Movement Reconciliation Procedure; Reconciliation Review Procedure","RequiredAction":"Review the reconciliation finding in RECREV and resolve with evidence","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Reconciliation","ResolutionEvidenceRequired":"Reconciliation evidence (document, counterpart movement, or corrected match) cited in the RECREV decision","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-01"}
{"EFCode":"EF0000035","FlagReasonCategory":"Reconciliation Duplicate Settlement Risk","DetectionPattern":"data-aware (build_docflg.py reconciliation detector)","FlagCategory":"High Risk","RiskFactor":8,"AutoReviewable":"No","Summary":"Two or more 1:1-type matched movements claim the same document","FlowDimension":"Bank Reconciliation","AppliesToTable":"BNKMOV","AppliesToField":"MatchStatus, MatchedRef","PolicyReference":"Bank Movement Reconciliation Procedure; Reconciliation Review Procedure","RequiredAction":"Review the reconciliation finding in RECREV and resolve with evidence","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Reconciliation","ResolutionEvidenceRequired":"Reconciliation evidence (document, counterpart movement, or corrected match) cited in the RECREV decision","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-01"}
{"EFCode":"EF0000036","FlagReasonCategory":"Duplicate Movement Suspicion","DetectionPattern":"data-aware (build_docflg.py reconciliation detector)","FlagCategory":"High Risk","RiskFactor":7,"AutoReviewable":"No","Summary":"Same account/date/amount/description ingested under different source files","FlowDimension":"Bank Reconciliation","AppliesToTable":"BNKMOV","AppliesToField":"MatchStatus, MatchedRef","PolicyReference":"Bank Movement Reconciliation Procedure; Reconciliation Review Procedure","RequiredAction":"Review the reconciliation finding in RECREV and resolve with evidence","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Block Reconciliation","ResolutionEvidenceRequired":"Reconciliation evidence (document, counterpart movement, or corrected match) cited in the RECREV decision","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-01"}
{"EFCode":"EF0000037","FlagReasonCategory":"Reconciliation Amount Mismatch","DetectionPattern":"data-aware (build_docflg.py reconciliation detector)","FlagCategory":"High Risk","RiskFactor":7,"AutoReviewable":"No","Summary":"Matched row/group difference exceeds the EUR 0.02 tolerance","FlowDimension":"Bank Reconciliation","AppliesToTable":"BNKMOV","AppliesToField":"MatchStatus, MatchedRef","PolicyReference":"Bank Movement Reconciliation Procedure; Reconciliation Review Procedure","RequiredAction":"Review the reconciliation finding in RECREV and resolve with evidence","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Reconciliation evidence (document, counterpart movement, or corrected match) cited in the RECREV decision","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-01"}
{"EFCode":"EF0000038","FlagReasonCategory":"Single-Leg Internal Transfer","DetectionPattern":"data-aware (build_docflg.py reconciliation detector)","FlagCategory":"High Risk","RiskFactor":6,"AutoReviewable":"No","Summary":"Internal movement with no plausible counterpart leg on file","FlowDimension":"Bank Reconciliation","AppliesToTable":"BNKMOV","AppliesToField":"MatchStatus, MatchedRef","PolicyReference":"Bank Movement Reconciliation Procedure; Reconciliation Review Procedure","RequiredAction":"Review the reconciliation finding in RECREV and resolve with evidence","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Reconciliation evidence (document, counterpart movement, or corrected match) cited in the RECREV decision","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-01"}
{"EFCode":"EF0000039","FlagReasonCategory":"FX Difference Exceeds Tolerance","DetectionPattern":"data-aware (build_docflg.py reconciliation detector)","FlagCategory":"Medium Risk","RiskFactor":5,"AutoReviewable":"No","Summary":"Cross-currency difference exceeds the 3 percent FX band","FlowDimension":"Bank Reconciliation","AppliesToTable":"BNKMOV","AppliesToField":"MatchStatus, MatchedRef","PolicyReference":"Bank Movement Reconciliation Procedure; Reconciliation Review Procedure","RequiredAction":"Review the reconciliation finding in RECREV and resolve with evidence","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Reconciliation evidence (document, counterpart movement, or corrected match) cited in the RECREV decision","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-01"}
{"EFCode":"EF0000040","FlagReasonCategory":"Matched Without Approved Match Type","DetectionPattern":"data-aware (build_docflg.py reconciliation detector)","FlagCategory":"Medium Risk","RiskFactor":5,"AutoReviewable":"No","Summary":"Matched row resolves to no BNKMAT ReconciliationMatchType (e.g. genuine N:M)","FlowDimension":"Bank Reconciliation","AppliesToTable":"BNKMOV","AppliesToField":"MatchStatus, MatchedRef","PolicyReference":"Bank Movement Reconciliation Procedure; Reconciliation Review Procedure","RequiredAction":"Review the reconciliation finding in RECREV and resolve with evidence","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Reconciliation evidence (document, counterpart movement, or corrected match) cited in the RECREV decision","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-01"}
{"EFCode":"EF0000041","FlagReasonCategory":"Unmatched Aging Over 90 Days","DetectionPattern":"data-aware (build_docflg.py reconciliation detector)","FlagCategory":"Low Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"Movement unmatched more than 90 days after its own DocDate","FlowDimension":"Bank Reconciliation","AppliesToTable":"BNKMOV","AppliesToField":"MatchStatus, MatchedRef","PolicyReference":"Bank Movement Reconciliation Procedure; Reconciliation Review Procedure","RequiredAction":"Review the reconciliation finding in RECREV and resolve with evidence","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Periodic Review","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Reconciliation evidence (document, counterpart movement, or corrected match) cited in the RECREV decision","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-01"}
{"EFCode":"EF0000042","FlagReasonCategory":"OriginClass confirmed by review decision","DetectionPattern":"OriginClass confirmed '(Internal|External)' by user review decision","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"The row's OriginClass, previously defaulted without a signal (EF0000007), has been explicitly confirmed by a user decision in the DOCRQE review queue -- the note records the confirmed value, the deciding ReviewID, and the date. A provenance record of a completed, evidenced confirmation, not an open question.","FlowDimension":"Document Registry","AppliesToTable":"DOCLOG","AppliesToField":"OriginClass","PolicyReference":"Document Entry Review Procedure","RequiredAction":"Retain the confirmation note (audit trail)","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"DOCRQE ReviewID cited in the note","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-02"}
{"EFCode":"EF0000043","FlagReasonCategory":"Dormant placeholder entity accepted by review decision","DetectionPattern":"Dormant catch-all placeholder \\(review decision","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"A deliberate placeholder/catch-all entity whose open identity findings were formally accepted and closed by a DOCRQE review decision: the placeholder is fully drained (no transactional rows reference it) and is retained dormant for future use. The note records the deciding ReviewID(s) and date -- a provenance record of a completed decision, not an open identity question.","FlowDimension":"Entity Item Classification","AppliesToTable":"ENTMST","AppliesToField":"EntityName; FiscalNo","PolicyReference":"Document Entry Review Procedure; Entity Entry Flow Procedure","RequiredAction":"Retain the dormant-status note (audit trail)","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"DOCRQE ReviewID(s) cited in the note","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-02"}
{"EFCode":"EF0000044","Active":"Yes","AppliesToTable":"FDCHDR","AppliesToField":"DocumentID; ProviderCodeName","DetectionPattern":"detect_duplicate_documents.py EXACT tier","EffectiveFrom":"2026-08-03","FlagCategory":"High Risk","FlagReasonCategory":"Duplicate document: same DocumentID and vendor captured twice","FlowDimension":"Document Entry","PolicyReference":"Document Entry Flow Procedure; Payment and Receivable Sanity Check","RequiredAction":"Retire one copy via the de-duplication cleanup, preserving the twin that carries ground truth, a PAYDTL line or a bank match; record it on retired_duplicate_documents.json","ResolutionEvidenceRequired":"Retirement ledger entry citing both EntryCodes","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"High","RiskFactor":9,"BlockingLevel":"Block Reconciliation","AutoReviewable":"Yes","AutoActionAllowed":"No","RuleVersion":"1.0","Summary":"Two FDCHDR rows share a normalised DocumentID AND vendor AND DateDoc AND total -- the same document captured twice, almost always because it arrived under two naming conventions (the supplier's own filename and the archive's {Company}_{DocumentType}_{Entity}_{Period}_{NN} name). This inflates payables directly. Filename is NOT a document identity; identity is normalised DocumentID + vendor. A same-DocumentID collision across DIFFERENT vendors is NOT this flag -- short document numbers legitimately collide between counterparties."}
{"FlagReasonCategory":"Reference only - deliberately not posted","DetectionPattern":"REFERENCE ONLY -- NOT POSTED TO FDCHDR","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"A document read, archived and fully recorded in reference_only_documents.json but DELIBERATELY not posted to FDCHDR -- out of the engagement's documented scope, captured on the user's instruction so the evidence is kept without moving any balance. This is a RECORDED DECISION, not a pending one: the row carries no EntryCode by design, so it can never reach payables, aging, matching or reconciliation, and there is nothing for a reviewer to decide. Added Stage 10 Round 6 because the fallback rule EF0000011 fired on all seven of them at 'Block Financial Entry' -- blocking the posting of documents that were deliberately not posted. That fallback did exactly its job (its own Summary says 'add a new rule to this table so future occurrences classify automatically'); this is that rule. Distinct from EF0000010 'Routed to FailureTable', which means the document COULD NOT be processed -- these were processed fine and chosen not to be posted.","FlowDimension":"Document Registry","EFCode":"EF0000045","AppliesToTable":"DOCLOG","AppliesToField":"Flag","PolicyReference":"Financial Document Capture Policy","RequiredAction":"None - retain as a recorded scope decision","AutoActionAllowed":"Yes","ReviewOwnerRole":"Process Owner","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"The archived document and its reference_only_documents.json entry","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-03"}
{"EFCode":"EF0000046","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-05","FlagReasonCategory":"Source read visually - no extractable text layer","DetectionPattern":"SCANNED SOURCE|read visually, pdftotext extracted nothing|photographed (store )?receipt","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"No","Summary":"The source PDF carries no extractable text -- a photographed or scanned receipt -- so every figure on the entry was read from the image rather than parsed. The capture may well be correct, but it rests on visual reading alone and no second mechanism can confirm it, which is a different evidence quality from a machine-read document. A human spot-check against the image closes it.","FlowDimension":"Document Registry","AppliesToTable":"DOCLOG; FDCHDR; FDCDTL","AppliesToField":"Amount; VATAmount; DocumentID","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Spot-check the captured figures against the image","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Routine","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Reviewer confirmation against the source image"}
{"EFCode":"EF0000047","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-05","FlagReasonCategory":"Customer fiscal identity absent or not the booking company","DetectionPattern":"Consumidor Final|NO customer tax number|no fiscal number printed for the customer|ADDRESSEE NOTE|entity of record on the document is","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"The customer side of the document does not identify the company whose books carry the cost: either no customer tax number is printed at all (a Fatura Simplificada issued to \u0027Consumidor Final\u0027, which is not VAT-deductible evidence as printed), or the document names a DIFFERENT group entity as the addressee while the cost was booked here because this company actually bore it. The expense may be entirely genuine; what is at risk is the VAT deduction and, in the addressee case, whether the cost belongs on this company\u0027s books or should be recharged.","FlowDimension":"Document Registry","AppliesToTable":"DOCLOG; FDCHDR","AppliesToField":"Company; FiscalNo (customer side); VATAmount","PolicyReference":"Financial Document Capture Policy; Financial Document Entry Policy","RequiredAction":"Confirm the booking company and the VAT treatment","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Reviewer decision on booking company and deductibility"}
{"EFCode":"EF0000048","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-05","FlagReasonCategory":"Prior-period document settled in the current window","DetectionPattern":"payment of a 20\\d\\d invoice|inside the user\u0027?s .{0,30}window","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"A document dated in a prior period, captured because the engagement\u0027s stated scope window includes the current-period payment that settles it. This is a recorded scope decision, not an error -- but the population matters: these rows are the reason document-count and value totals do not tie to a single period, and they are the same population parked out of the reconciliation count scope. Flagging them makes that set enumerable instead of implicit.","FlowDimension":"Document Registry","AppliesToTable":"DOCLOG; FDCHDR; BNKMOV","AppliesToField":"Date; AccountPeriod","PolicyReference":"Financial Document Capture Policy","RequiredAction":"None - retain as a recorded scope decision","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"The document and the settling movement"}
{"EFCode":"EF0000049","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-05","FlagReasonCategory":"Date or period value assumed - not printed on the document","DetectionPattern":"not printed; assumed|DatePay inferred|correctable assumption|AccountPeriod override","FlagCategory":"Low Risk","RiskFactor":2,"AutoReviewable":"No","Summary":"A date or accounting-period value on the entry was ASSUMED because the document does not print it -- DateDue defaulted to DateDoc, DatePay inferred from a request date, or AccountPeriod overridden because the printed coverage period starts after the document date. EF0000005 already covers an assumed DocumentType; this covers the same class of reasoning applied to dates, which drive ageing, period allocation and the payment-control tables. Each is individually correctable and none is self-evidently wrong.","FlowDimension":"Document Registry","AppliesToTable":"DOCLOG; FDCHDR; PAYCTR","AppliesToField":"DateDue; DatePay; AccountPeriod","PolicyReference":"Financial Document Capture Policy","RequiredAction":"Confirm or correct the assumed date/period","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Routine","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"The document, or a reviewer decision to keep the assumption"}
{"EFCode":"EF0000050","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-05","FlagReasonCategory":"Byte-identical batch twin retained; twin logged as duplicate","DetectionPattern":"[Bb]atch twin .{0,80}byte-identical","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"The same file arrived twice in one capture batch under two filenames and was byte-identical. The twin was logged and excluded (EF0000002 sits on the excluded copy); this flag sits on the RETAINED copy, so the surviving row carries the record that a twin existed. Distinct from EF0000044, which is the far more serious case of the same DocumentID and vendor captured twice as two different files.","FlowDimension":"Document Registry","AppliesToTable":"DOCLOG","AppliesToField":"Filename","PolicyReference":"Financial Document Capture Policy","RequiredAction":"None - retain as duplicate-handling provenance","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Both filenames and the duplicate log entry"}
{"EFCode":"EF0000051","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-05","FlagReasonCategory":"Settling movement payment reference does not match the invoice number","DetectionPattern":"a different reference from this invoice|mistyped/stale payment reference","FlagCategory":"Medium Risk","RiskFactor":3,"AutoReviewable":"No","Summary":"The bank movement that settles this document names a payment reference that is NOT this document\u0027s number, while the amount and date evidence point conclusively at this document. Two readings survive: the memo reference is stale or mistyped (the common case), or a second real invoice with that number exists and has not been captured -- in which case one of the two is settled twice or not at all. The match should not be trusted as final until someone confirms the referenced number does not exist as its own document.","FlowDimension":"Document Registry","AppliesToTable":"BNKMOV; FDCHDR; BNKREC","AppliesToField":"Description; DocumentID","PolicyReference":"Bank Movement Reconciliation Procedure","RequiredAction":"Confirm the referenced invoice number does not exist separately","AutoActionAllowed":"No","ReviewOwnerRole":"Finance Reviewer","ReviewPriority":"Before Posting","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"Vendor statement or confirmation that no such invoice exists"}
{"EFCode":"EF0000052","FlagReasonCategory":"Bank statement logged for capture completeness","DetectionPattern":"Bank statement logged for capture completeness","FlagCategory":"Information","RiskFactor":1,"AutoReviewable":"Yes","Summary":"A bank account statement recorded in DOCLOG so the capture log is complete (conflict C-05, closed Stage 10 Round 7 by user decision). It carries no EntryCode by design and never posts to FDCHDR -- it is the source extract behind a period\u0027s BNKMOV rows, and BNKCHK controls its balances. Provenance, not a finding.","FlowDimension":"Document Registry","AppliesToTable":"DOCLOG","AppliesToField":"Flag","PolicyReference":"Financial Document Capture Policy; Financial Document Entry Policy","RequiredAction":"None - retain as capture-completeness provenance","AutoActionAllowed":"Yes","ReviewOwnerRole":"AI","ReviewPriority":"Informational","BlockingLevel":"Non-Blocking","ResolutionEvidenceRequired":"The statement PDF and its BNKCHK control row","RuleVersion":"1.0","Active":"Yes","EffectiveFrom":"2026-08-05"}
```

The semicolon-joined extracted `AppliesToTable` value is retained above as provenance. Import expands
it left-to-right into `DOCEFLAppliesTo`; it never stores the joined value in the relational rule row.


Seed additions required by this freeze are:

- `EF0000000`, “imported instance with no rule attached”, `ItemClass=Annotation`,
  `BlockingLevel=Non-Blocking`, `Active=Yes`, `RuleVersion=1.0`; every other target field is
  explicitly `absent` as recorded in `docs/p1-0-c20-authored-rows.md`. The four imported blank
  EFCode values map to this sentinel and EFCode remains non-nullable.
- `EF0000053`, the Monthly-only expected-invoice-gap rule. It evaluates only ENTMST providers whose evidenced
  `InvoiceFrequency` is exactly `Monthly`, declares `ItemClass=Decision`, and seeds with `Active=No`.
  Assumed or other frequencies do not qualify. OFDGAP is not imported or stored as a table.
  `ActivateDOCEFLRule` must atomically supply `FlagCategory`, `ReviewOwnerRole`, `ReviewPriority`,
  `BlockingLevel`, and `EnforcementStartsAt`; only then may the rule emit persistent DOCFLG and
  DOCRQE findings and render OFDGAP. Other fields not decided or structurally implied are `absent`;
  the complete per-field row and allocation rule are in
  `docs/p1-0-c20-authored-rows.md`.
- a related-party funding rule, `ItemClass=Annotation`, `FlagCategory=Information`,
  `BlockingLevel=Non-Blocking`, applying to BNKMOV. It recognizes a movement only when its entity
  has `RelatedParty=true` **and** its classified item references the existing ITMCLS `CLCode`
  `CL000005` (`Banks / Financing`) or `CL000025` (`Revenue / Intercompany`). Entity relationship
  alone never matches or excludes a movement. Those two controlled rows are transcribed with
  per-field extraction provenance in `docs/p1-0-codes-taxonomy-archive.md`.

### DOCFLG

`FlagInstanceID code PK`; `EFCode code FK DOCEFL`; `ItemClass varchar(16)`; `Status varchar(24)`; `RelatedRecordType
varchar(32)`; `RelatedRecordID text`; `SourceReference text NULL`; `DetectedAt instant`;
`DetectedBy short`; `ReviewOwnerRole varchar(64)`; `ReviewPriority varchar(32)`; `BlockingLevel
varchar(40)`; `RequiredAction text`; `ResolutionEvidence text NULL`; `ResolutionNotes text NULL`;
`ResolvedBy short NULL`; `ResolvedAt instant NULL`; `RecheckDate date NULL`; `InstanceNote text
NULL`; `SourceTextHash char(12)`; `HashAlgorithm varchar(16)`; plus `PriorRelatedRecordID text
NULL`, `IsGrandfathered bool`, `GrandfatherReason text NULL`, `SupersededBy code NULL FK DOCFLG`,
`SupersedeReason varchar(40) NULL`.

`(EFCode, ItemClass)` has a restrictive composite FK to DOCEFL, so an instance cannot disagree with
its detector rule. `ItemClass` is snapshotted from the rule when the instance is detected and is
never recomputed or rewritten when the rule changes. Decision instances use the lifecycle governed by
`docs/p1-0-vocabularies.md`, including `Waived` as a terminal state with its own evidence and
authorized actor. Only Decision items may be open:
`CHECK (NOT (Status IN ('Open','InReview') AND ItemClass <> 'Decision'))`. Blocking predicates use
the annex's exact `Non-Blocking` literal. Status and Annotation history may progress through the
terminal states observed in the annex and is non-enforcing; it is not forced into `Recorded`.

The natural key for every finding is `(EFCode, RelatedRecordType, RelatedRecordID, SourceTextHash,
DetectedAt)`. `SourceTextHash` preserves the pinned lowercase 12-hex `md5-12` value and
`HashAlgorithm='md5-12'`; comparisons never cross algorithms. A future algorithm change must
migrate and recompute both historical and new sides together. Changed source text therefore mints
a distinct stable FL identity, while identical terminal evidence is not reopened. `DetectedAt` is
the explicit tiebreaker for otherwise colliding imported instances. `PriorRelatedRecordID` is the
pinned source-internal audit field written by the LGCode identity migration before replacing a
DOCLOG `RelatedRecordID`; preserve it as nullable provenance, do not render it as one of DOCFLG's
19 workbook columns, and never use it as the current FK target.

All current identity fields are `NOT NULL` and non-empty (the `md5-12` hash is lowercase 12-hex).
Open/InReview require `ResolvedAt` and `ResolvedBy` NULL. Terminal Decision states require
`ResolvedAt` and a nonblank `ResolvedBy`, except system-generated Superseded, which requires an
audited process actor. `SupersedeReason` is closed to `ReplacedByInstance|RuleRedefined|
ConditionResolvedAtSource`; a Superseded row must populate exactly one of `SupersededBy` or
`SupersedeReason`, and `SupersededBy` cannot self-reference. The 204 pinned Superseded rows import
with `SupersedeReason='RuleRedefined'`. `IsGrandfathered` is `NOT NULL`; true requires a nonblank
reason. Terminal
rows are immutable. The single enforcement predicate is
`ItemClass='Decision' AND Status IN ('Open','InReview') AND BlockingLevel<>'Non-Blocking' AND NOT
IsGrandfathered`; DOCRQE never enforces.

### DOCRQE

`ReviewID code PK`; `Company varchar(128) FK CompanyRegistry.CompanyCode`; `ItemClass varchar(16)`; `Priority varchar(24)`; `Status varchar(24)`;
`CreatedDate instant`; `Layer varchar(64)`; `EvaluationFlag code NULL FK DOCEFL`;
`BlockingLevel varchar(40)`; `SourceRecordType varchar(32)`; `SourceRecordID text`; `DocID text
NULL`; `DocFilename text NULL`; `ReviewType varchar(48)`; `TargetTable varchar(32) NULL`;
`TargetField varchar(64) NULL`; `ProposedValue json NULL`; `CurrentValue json NULL`; `AIProposal
text NULL`; `UserDecision varchar(32) NULL`; `UserInput json NULL`; `Observations text NULL`;
`DecidedBy short NULL`; `DecisionDate instant NULL`; `AppliedReference text NULL`; `ItemKey text`;
`SuccessorReviewID code NULL FK DOCRQE`; plus `SupersedeReason varchar(40) NULL` with the same
closed reason vocabulary as DOCFLG, and `MatcherProposalId id NULL`. Add unique `(ReviewID,
Company)` and composite FK `(MatcherProposalId, Company)` to MatcherProposal. A CHECK requires the
proposal link exactly for matcher-origin rows and forbids it for non-matcher findings.

Unique `ItemKey`; permanent ReviewID; never-delete. Only Decision may be Open. Controlled decision
values: Accept / Reject / Accept with Changes / Need More Information / Defer. Recorded human
fields survive reruns. A state transition closes the predecessor and identifies its successor.

`ItemClass`, `Status`, `CreatedDate`, source identity, and `ItemKey` are `NOT NULL`; text identities
must be nonblank. A check constraint enforces only
`CHECK (NOT (Status IN ('Open','InReview') AND ItemClass <> 'Decision'))`. The terminal vocabulary
comes from the generated annex and is not duplicated in this cross-field constraint.
`UserDecision`, `DecidedBy`, and `DecisionDate`
are all NULL before a decision and all non-NULL once decided; `AppliedReference` is non-NULL only
for Applied. A predecessor marked Superseded requires exactly one of a different
`SuccessorReviewID` or a nonblank `SupersedeReason`; other rows leave both NULL. The successor FK
is restrictive and deferrable so a two-row replacement commits atomically without manufacturing a
successor when the rule changed or the condition resolved at source.

The pinned JSON also contains nullable source-internal `ResolutionEvidence` on DOCRQE. Preserve it
as `ResolutionEvidence text NULL` during import and on rerun; it is decision/audit evidence, not an
AI-refreshed field. It is not among `build_workbook.py::build_docrqe_sheet()`'s rendered columns.

### USEROBS — replayable human observations

The pinned `Editor/Data/user_observations.json` is part of D4 complete-reference import, not an
optional queue convenience. Its root has `_readme` as a string array and `notes` as an object array;
every note has exactly `key`, `text`, `docrqe`, and `recrev`, with the latter two string ReviewID
arrays. Only schema and consumer
behavior were inspected at the pin; no note or queue value is reproduced here.

`UserObservation`: `Id id PK`, `ObservationKey varchar(128)`, `ObservationText text`,
`ObservationTextSha256 char(64)`, `ImportEvidenceRowId id FK ImportEvidenceRow`, `AuthoredBy short NULL`,
`ImportedAt instant`, `ImportedBy short`; unique `ObservationKey`, unique `ImportEvidenceRowId`,
plus `ImportTargetTable text` generated always as `'UserObservation'`; unique
`(ImportEvidenceRowId, ImportTargetTable, ObservationKey)` with a deferrable composite FK to
`ImportEvidenceRow(Id, TargetTable, TargetPermanentCode)`.
`ObservationText` stores the JSON string losslessly, including Unicode and line breaks; its hash is
over the exact UTF-8 string. The prototype consumer's trimmed, append-if-absent rendering is a
separate deterministic projection and never replaces the stored text.

`UserObservationTarget`: `Id id PK`, `UserObservationId id FK UserObservation`, `QueueName
varchar(8)` (`DOCRQE|RECREV`), `SourceOrdinal integer`, `SourceReviewID code`, `DOCRQEReviewID code NULL FK DOCRQE`,
`RECREVReviewID code NULL FK RECREV`, `ResolutionStatus varchar(16)` (`Resolved|Pending`),
`ResolvedAt instant NULL`; unique `(UserObservationId, QueueName, SourceOrdinal)`. Repeated source
ReviewIDs, if any, remain separate ordinal evidence rows rather than being silently deduplicated. A check permits
exactly the FK matching QueueName when Resolved and requires both FKs NULL when Pending. This keeps
every original ReviewID even if the pinned consumer would report it as not yet recreated. A later
deterministic resolution fills only the matching FK and audit fields; it never edits the source
ReviewID or observation.

Imported queue `Observations` cells remain preserved as their own human-authored audit text. The
projection appends a note only when its exact rendered text is absent and never replaces existing
text, matching the pinned consumer. Claude/AI may read these observations and propose consequences
but cannot create, edit, resolve, stamp, or delete them. Only the import process or an authenticated
human command may create observation evidence; deterministic code performs the audited projection.

## 3. Entity and item reference layer

### ENTMST

`EntityCode code PK`; `EntityName text`; `FiscalNo varchar(64)`; `Country char(2)`; `CurrencyCode
char(3)`; `CodeName varchar(128)`; `Role varchar(64)`; `DirectDebit bool NULL` with no default; `FlagReviewNotes text
NULL`; plus `RelatedParty bool`, `RelatedPartyKind code NULL FK ITMCLS.CLCode`, `InvoiceFrequency
varchar(32) NULL`, `ExpectedInvVal money NULL`, `OpenPayableBalance money NULL`,
`OpenReceivableBalance money NULL`, `IsProvisional bool`, `TaxIdVerificationStatus
varchar(24)`.

Unique `(FiscalNo, CodeName)` and unique `CodeName`. `RelatedPartyKind` stores a CLCode restricted to the
two existing ITMCLS rows resolved by unique `(Class, Subclass)`: `Banks / Financing` and `Revenue /
Intercompany`; it is required exactly when RelatedParty is true. `RelatedParty` identifies
relationship and never means “ignore this entity.” Intercompany matching additionally requires a
movement classified with one of those same CLCodes so dual-role suppliers remain eligible for
ordinary invoices.

### ENTALS

`ALCode code PK`; `Company varchar(128) FK CompanyRegistry.CompanyCode`; `Alias text`; `CodeName varchar(128) FK ENTMST.CodeName`;
`Flag text NULL`. Unique `(Company, Alias)` using case-insensitive normalized alias.

### ITMMST

`ItemCode code PK`; `ItemDesc text`; `Class varchar(128)`; `Subclass varchar(128)`; `PLMKEY
varchar(64)`; `PLMKO varchar(64)`; `SNCDSC text NULL`; `SNCACC varchar(64) NULL`; `CostCentre
varchar(64) NULL`; `FlagReviewNotes text NULL`; plus source-internal `Service text NULL` and
`AutoCreated bool`. `Service` is retained for lossless import but is legacy and is not used by
production classification.

### ITMALS

`ILCode code PK`; `Company varchar(128) FK CompanyRegistry.CompanyCode`; `Alias text`; `ItemCode code FK ITMMST`; `Flag text NULL`.
Unique `(Company, Alias)` using case-insensitive normalized alias.

### ENTITM

`EICode code PK`; `CodeName varchar(128) FK ENTMST.CodeName`; `ItemCode code FK ITMMST`;
`FiscalNo varchar(64)`; `ItemDesc text`; `Class varchar(128)`; `Subclass varchar(128)`; `PLMKEY
varchar(64)`; `PLMKO varchar(64)`; `SNCDSC text NULL`; `SNCACC varchar(64) NULL`; `CostCentre
varchar(64) NULL`; `FlagReviewNotes text NULL`. Unique `(CodeName, ItemCode)`.

The JSON ingestion surface stores `CodeName`. The rendered workbook's `EntityCode` column is a
derived export projection of the corresponding ENTMST `EC......` value and is not stored as an
ENTITM column. JSON import reads CodeName directly; workbook import resolves `EC -> CodeName`
through ENTMST. Either surface fails closed only when the value is unresolved or ambiguous; an EC
code is never rejected merely for being an EC code. The EI natural key remains `(CodeName,
ItemCode)` and this does not reopen the settled mapping.

### ITMCLS, MNGACC, SNCACC, COCACC

- ITMCLS: `CLCode code PK`, `Class varchar(128)`, `Subclass varchar(128)`, `PLMKEY varchar(64)`,
  `PLMKO varchar(64)`; unique `(Class, Subclass)`.
- MNGACC: `MACode code PK`, `PLMAINKEY varchar(64)`, `PLMKO01 varchar(64)`, `PLMKO02 varchar(64)`,
  `PLMKO03 varchar(64)`, `PLMKO varchar(64)`; unique `PLMAINKEY`.
- SNCACC: `SNCode code PK`, `Classe varchar(32)`, `DescricaoClasse text`, `Codigo varchar(64)`,
  `Conta text`, `Nivel integer`, `ContaPai varchar(64) NULL`; unique `Codigo`, self-FK `ContaPai`.
- COCACC: `CCCode code PK`, `CostCentreCode varchar(64)`, `CostCentreDesc text`,
  `FlagReviewNotes text NULL`; unique `CostCentreCode`.

### ENTBNK and EXCRAT

ENTBNK: `CodeName varchar(128) FK ENTMST.CodeName`, `Company varchar(128) FK
CompanyRegistry.CompanyCode`, `PayMethod text NULL`,
`PayAccount text NULL`, `Flag text NULL`; PK `(CodeName, Company)`. The five duplicate source pairs
that differ only in Flag are a source-data defect: import fails closed until Luís merges each pair
while retaining both notes. The source comma-joined fields remain verbatim for render parity.

`ENTBNKAccount`: `CodeName`, `Company`, `AccountOrdinal integer`, `PayAccount text`; PK
`(CodeName, Company, AccountOrdinal)`, unique `(CodeName, Company, PayAccount)`, composite FK to
ENTBNK. `ENTBNKMethod`: `CodeName`, `Company`, `MethodOrdinal integer`, `PayMethod text`; PK
`(CodeName, Company, MethodOrdinal)`, unique `(CodeName, Company, PayMethod)`, composite FK to
ENTBNK. Payment routing reads these child rows and never parses the render-parity strings at use
time.

EXCRAT: `Period char(6)`, `From char(3)`, `To char(3)`, `FEX rate`, `Flag text NULL`, `EXCode code
PK`; unique `(Period, From, To)`. Missing rates remain missing and block dependent auto-actions.

## 4. Fiscal entries and non-fiscal archive

### FDCHDR

`EntryCode code PK`; `FlowType char(1)` (`I|R|P|F|O`); `Company varchar(128) FK
CompanyRegistry.CompanyCode`; `CounterpartyCodeName
varchar(128) FK ENTMST.CodeName`; `FiscalNo varchar(64)`; `Period char(6)`; `AccountPeriod char(6)`;
`DocumentID text`; `DateDoc date`; `DateDue date NULL`; `DatePay date NULL`; `ItemDesc text`;
`ItemCode code FK ITMMST`; `EICode code NULL FK ENTITM`; `PLMKEY varchar(64)`; `PLMKO varchar(64)`;
`NetAmount money`; `VATAmount money`; `TotalAmount money`; `Currency char(3)`; `FEX rate NULL`;
`LocalAmount money NULL`; `Filename text`; `ManualEntry bool`; `AutoEntry bool`; `DataSource
varchar(32)`; `FlagReviewNotes text NULL`; source-internal `CompanyCodeName varchar(128)`,
`ProviderCodeName varchar(128) NULL`, `ProviderName text NULL`, `EntityCodeName varchar(128) NULL`;
plus `CompanyId id`, `DocumentId id NULL`,
`ExtractionRevisionId id NULL`, `ManualEntryEvidenceId id NULL`, `ImportEvidenceRowId id NULL`,
`SourceKey text NULL`, `SourceKeyHash char(64) GENERATED ALWAYS AS (CASE WHEN "SourceKey" IS NULL
THEN NULL ELSE encode(digest(convert_to("SourceKey", 'UTF8'), 'sha256'), 'hex') END) STORED`,
`SourceBMCode text NULL`, `EvidenceDocumentState varchar(24) NULL`, and `EvidenceMode varchar(24)`
(`NativeExtracted|NativeManual|BankGenerated|ImportedHistory`), plus `ImportTargetTable text`
generated always as `'FDCHDR'`, `NormalizedDocumentID text GENERATED ALWAYS AS
(regexp_replace(upper("DocumentID"), '[^A-Z0-9]', '', 'g')) STORED`, and
`DocumentIdNormalizationVersion varchar(16)` fixed to `v1`. `CounterpartyCodeName` is derived at
import from source `ProviderCodeName` on payables and `EntityCodeName` on receivables. `FlowType` is
derived from the source collection (`I`, `R`, `P`, `F`, or `O`) and stored. The restrictive FKs and
exact mode checks are in section 8.

Composite FK `(CompanyId, Company)` targets CompanyRegistry; native rows also use `(DocumentId,
CompanyId)` to target Document `(Id, BuyerEntityId)`. Add unique `(Company, EntryCode)` for
company-scoped child FKs. The two mutually exclusive identity
families use partial unique indexes, not one nullable composite constraint:

- source fiscal rows: unique `(Company, CounterpartyCodeName, NormalizedDocumentID,
  DocumentIdNormalizationVersion, FlowType)` where `SourceKey IS NULL`;
- deterministic bank-generated rows: unique `(Company, SourceKeyHash)` where `SourceKey IS NOT
  NULL`.

`NormalizedDocumentID` uppercases DocumentID and removes every character outside `[A-Z0-9]`.
Values created under different `DocumentIdNormalizationVersion` values are never compared; a rule
change is a migration that recomputes the generated column and increments the discriminator, not
an in-place redefinition. A check
requires exactly one identity family: nonblank SourceKey implies `FlowType IN ('P','F','O')` and
`DataSource='BankExtract'`; otherwise the normalized fiscal semantic fields must all be nonblank.
Native-extracted rows require `EvidenceDocumentState='Posted'` and a restrictive composite FK
`(DocumentId, EvidenceDocumentState)` to `DOCLOG(DocumentId, DocumentState)`; every other evidence
mode requires EvidenceDocumentState NULL. This CHECK+FK makes a ReferenceOnly document
structurally unreachable from FDCHDR. PAYCTR/RCVCTR and aging inherit the bar through their
mandatory FDCHDR FK.
The generated expression uses PostgreSQL `pgcrypto.digest(bytea, text)` over the UTF-8 bytes of
SourceKey; `encode(..., 'hex')` returns deterministic lowercase hexadecimal, and the explicit CASE
keeps SourceKeyHash NULL when SourceKey is NULL. `SourceKey` remains readable identity/audit text
and has no BNKMOV foreign key. `SourceBMCode` is
the imported comma-joined human pointer and is derivable from the ordered junction below; neither
field is referential truth. Imported bank-generated history may
use `EvidenceMode=ImportedHistory` while retaining its verified SourceKey; imported source-fiscal
history leaves it NULL. SourceFile and EntryCode are never identity inputs. Header amounts
reconcile to FDCDTL within the configured two-cent tolerance.

P1-1 has an explicit migration prerequisite: PostgreSQL `pgcrypto` must be available and
`digest(bytea, text)` must resolve before the generated column or its index is created. The
migration preflight fails closed if the extension cannot be enabled or the function is unavailable;
there is no fallback hash expression and no migration may continue with an unindexed raw SourceKey.

`FdcHdrBankMovement`: `Company varchar(128) FK CompanyRegistry.CompanyCode`, `EntryCode code`,
`BMCode code`, `ComponentOrder integer NOT NULL`; PK `(Company, EntryCode, ComponentOrder)`, unique
`(Company, BMCode)`, unique `(Company, EntryCode, BMCode)`, composite FK `(Company, EntryCode)` to
FDCHDR and composite FK `(Company, BMCode)` to BNKMOV, all `ON DELETE RESTRICT`. ComponentOrder is
official movement order. This represents all single and aggregate bank-generated documents while
enforcing that one movement is consumed by at most one generated document.

### FDCDTL

`Id id PK`; `Company varchar(128) FK CompanyRegistry.CompanyCode`; `EntryCode code`; `LineNumber
integer`; `FlowType char(1)`; `CodeName
varchar(128)`; `DateDoc date`; `DocumentID text`; `ItemDesc text`; `ItemCode code FK ITMMST`;
`EICode code NULL FK ENTITM`; `PLMKEY varchar(64)`; `PLMKO varchar(64)`; `NetAmount money`;
`VATAmount money`; `TotalAmount money`; `Filename text`; `FlagReviewNotes text NULL`; plus
`VatRate numeric(9,4) NULL`, `ExemptionReason text NULL`. Composite FK `(Company, EntryCode)` to
FDCHDR and unique `(Company, EntryCode, LineNumber)`; Company is resolved from the authenticated
header context, not accepted independently from line payload.

### DOCARC

`ArchiveId id PK`; `DocumentId id FK Document`; `LGCode code FK DOCLOG`; `DTCode code`; `DocClass varchar(24)`;
`DocumentType short`; `Title text NULL`; `Subject text NULL`; `Description text NULL`;
`DocumentDate date NULL`; `EffectiveDate date NULL`; `ExpiryDate date NULL`; `Reference text NULL`;
`Parties json`; `CounterpartyCodeName varchar(128) NULL FK ENTMST.CodeName`; `ArchivePath text NULL`;
`ArchiveSha256 char(64) NULL`; `ArchivedAt instant NULL`;
`DocumentState varchar(24)`; plus `ProcessingRoute varchar(24)`. The existing restrictive `(LGCode,
DocumentId)` DOCLOG anchor proves
both records belong to the same authenticated Document/BuyerEntity; DOCARC has no payload-selected
Company field. A restrictive composite FK `(DTCode, DocClass, DocumentType, ProcessingRoute)`
targets the matching DOCTYP alternate key, so DOCARC cannot assert a classification or route that
its governing rule does not carry. Add unique `(ArchiveId, DocumentId, LGCode, DTCode,
DocumentState, DocClass, DocumentType, ProcessingRoute)` for the official-statement qualification
FK.

DOCARC holds Bank/Legal/Procurement/Other basic metadata and ReferenceOnly evidence. It never
stands in for FDCHDR. A missing printed document date remains NULL and emits a Decision-class
review; intake/evidence may persist, but archive eligibility remains false and `ArchivePath` and
`ArchivedAt` remain NULL until an authenticated correction supplies the date. No intake,
extraction, issue, or filesystem timestamp substitutes for it. Once present, the class layout's
year/month Period derives from DocumentDate as already required. Purge is barred once DOCARC has
ever existed, including while the item awaits its date.

For Legal records the exact business-copy sub-path is
`{Company}/Legal/{Subtype}/{CounterpartyCodeName}/`. `Subtype` is the controlled DOCTYP legal
document type and `CounterpartyCodeName` maps to the declared DOCARC column; no period segment or
ambiguous `{CounterpartyCode}`, `{Counterparty}`, or `{Entity}` alias is permitted.

## 5. Bank movement and reconciliation scope

### BNKACC

`BankAccount varchar(64) PK`; `Company varchar(128) FK CompanyRegistry.CompanyCode`;
`ArchiveAccountToken varchar(64)`; `BankName short`; `AccountName short`;
`Currency char(3)`; `AccountType varchar(32)`; `AccountNumber varchar(64) NULL`; `IBAN varchar(64)
NULL`; `CreditLimit money NULL`; `SparseStatements bool`; `StartDate date`; `EndDate date NULL`;
unique `(Company, BankAccount)` for company-scoped child FKs and unique `(Company,
ArchiveAccountToken)`. Sensitive account values are encrypted or tokenized at rest and never
logged; Nextcloud directory and filename components use only ArchiveAccountToken.

### BNKMOV

`BMCode code PK`; `Company varchar(128) FK CompanyRegistry.CompanyCode`; `BankAccount varchar(64)`;
`Period char(6)`;
`DocDate date`; `MovDate date`; `Description text`; `Amount money`; `Currency char(3)`; `FEX rate
NULL`; `LocalAmount money NULL`; `RunningBalance money NULL`; `LocalBalance money NULL`;
`ReferenceNumber text NULL`; `CodeName varchar(128) NULL FK ENTMST.CodeName`; `ItemCode code NULL`;
`ItemDesc text NULL`; `Class varchar(128) NULL`; `Subclass varchar(128) NULL`; `PLMKEY varchar(64)
NULL`; `PLMKO varchar(64) NULL`; `Classified bool`; `MatchStatus varchar(64)`; `MatchedRef text
NULL`; `Flag text NULL`;
`SourceFile text`; `Occurrence integer`; `SourceKey text`; plus `CompanyId id NOT NULL` and
`BankInputEvidenceId id NOT NULL`.

`ItemDesc` and `Classified` are source-internal fields present in `bnkmov.json`: ItemDesc is the
resolved item description used by classification/matching, and Classified is the idempotent
classifier-visit marker (it does not assert that every classification field is nonblank). Preserve
both even though `build_workbook.py::build_bnkmov()` omits them from the 23 rendered columns. There
is no BNKMOV `Source` field in the pinned JSON or renderer; do not add one. Official-extract origin
is enforced by a mandatory evidence FK, not merely the ingestion path. `Occurrence` is a
production persistence of the prototype's transient `_OccSeq`, recomputed from official-statement
order before key comparison; it is not accepted from an extraction payload.

The restrictive `(Company, BankAccount)` FK targets BNKACC. The pinned unique natural key
`(BankAccount, MovDate, DocDate, Description, Amount, Occurrence)` remains unchanged; add unique
`(Company, BMCode)` and `(Company, SourceKey)` company-scoped alternate keys. SourceKey may remain
globally unique as a stronger invariant, but all financial children use the composite key. SourceFile and
running balance are provenance/control fields, not BM identity. Occurrence is recomputed
deterministically from official-statement order and persisted in PostgreSQL. Only official extract
ingestion or an approved bank integration can insert BNKMOV.

`ApprovedBankIntegrationReceipt`: `Id id PK`, `CompanyId id`, `Company varchar(128)`, `Provider
short`, `ExternalRequestId varchar(128)`, `ReceivedAt instant`, `PayloadSha256 char(64)`,
`CorrelationId varchar(128)`; unique `(Id, CompanyId, Company)`, unique `(Company, Provider,
ExternalRequestId)`, and composite FK `(CompanyId, Company)` to CompanyRegistry. It is
authenticated, append-only integration evidence.

`OfficialBankStatementEvidence`: `Id id PK`, `CompanyId id`, `Company varchar(128)`, `DocumentId
id`, `LGCode code`, `ArchiveId id`, `DocumentState varchar(24)`, `DocClass varchar(24)`,
`DocumentType short`, `DTCode code`, `ProcessingRoute varchar(24)`, `SourceSha256 char(64)`, `QualifiedAt instant`,
`QualifiedBy short`, `CorrelationId varchar(128)`; unique `(Id, CompanyId, Company)`. It has
composite FKs `(CompanyId, Company)` to CompanyRegistry, `(DocumentId, CompanyId)` to Document,
`(LGCode, DocumentId, DocumentState)` to a matching DOCLOG alternate key, and `(ArchiveId,
DocumentId, LGCode, DTCode, DocumentState, DocClass, DocumentType, ProcessingRoute)` to a matching DOCARC
alternate key. Its CHECK requires `DocClass='Bank'`, `DocumentType='Bank Statement'`,
`ProcessingRoute='ArchiveOnly'`, and `DocumentState NOT IN ('Discarded','Purged')`. Thus an
arbitrary Document row cannot qualify as official-statement evidence: the archived classification,
route, disposition, document, LGCode, and authenticated company must all agree.

`BankInputEvidence`: `Id id PK`, `CompanyId id`, `Company varchar(128)`, `Mode varchar(24)`
(`OfficialStatement|ApprovedIntegration`), `OfficialBankStatementEvidenceId id NULL`,
`ApprovedBankIntegrationReceiptId id NULL`, `SourceSha256 char(64)`, `CapturedAt instant`,
`CapturedBy short`, `CorrelationId varchar(128)`; unique `(Id, CompanyId, Company)`, with composite
FK `(CompanyId, Company)` to CompanyRegistry. A CHECK requires exactly the official-statement
evidence anchor or approved-integration receipt for its mode. The two nullable anchors use
composite FKs `(OfficialBankStatementEvidenceId, CompanyId, Company)` and
`(ApprovedBankIntegrationReceiptId, CompanyId, Company)` to their respective parent alternate keys.
BNKMOV adds `CompanyId id`, resolves it from authenticated scope, and has mandatory composite FKs
`(CompanyId, Company)` to CompanyRegistry and `(BankInputEvidenceId, CompanyId, Company)` to this
evidence row. Unofficial or cross-company movement origin is therefore unrepresentable.

### BNKCHK

`Company varchar(128) FK CompanyRegistry.CompanyCode`; `Period char(6)`; `BankAccount varchar(64)`;
`BCCode code`; `Currency char(3)`;
`InitialBalance money NULL`; `EndBalance money NULL`; `InitialBalanceAvailable bool`;
`EndBalanceAvailable bool`; `Lines integer`; `State varchar(24)`; `Notes text NULL`; unique
`(Company, Period, BankAccount)`, which is the production PK, with composite `(Company,
BankAccount)` FK to BNKACC. `Company` is production-only and is omitted from the pinned export.
BCCode remains a deterministic export/render
code, matching the pinned prototype; it is not added to the permanent-code ledger.

### BNKMAT

`BTCode code PK`; `MatchType short`; `MatchDescription text`; `EntrySource varchar(32)`;
`ReconciliationType varchar(64) NULL`; `Explanation text`; `AutoProposeAllowed bool NULL`;
`AutoCloseAllowed bool NULL`; `RequiresFDCHDR bool`; `RequiresReview bool`; `MaxDateWindowDays text
NULL`; `ToleranceAmount text NULL`; `ControlFormula text`; `FlagReviewNotes text NULL`; plus
`RequiredEvidence text`, `TestCaseReference text`, `Active bool`, `RuleVersion varchar(32)`.

| Row | Column | Route | Declaration reason |
|---|---|---:|---|
| `BT000012` | `ReconciliationType` | 1 | NULL: a classifier need not belong to a named reconciliation family. |
| `BT000012` | `AutoProposeAllowed` | 1 | NULL: unspecified proposal authority is denied. |
| `BT000012` | `AutoCloseAllowed` | 1 | NULL: unspecified closure authority is denied. |
| `BT000012` | `RequiresReview` | 2 | NOT NULL and `Yes`: D6 emits Decision-class review work. |
| `BT000012` | `MaxDateWindowDays` | 1 | NULL: this result uses no date window. |
| `BT000012` | `ToleranceAmount` | 1 | NULL: this result uses no amount tolerance. |

The five newly nullable governance columns are optional for every match rule: a classifier may not
belong to a named reconciliation family, may carry no proposal or closure authority, and may use no
date window or amount tolerance. NULL automation booleans confer no permission. `RequiresReview`
stays non-nullable because every rule must decide whether it creates review work.
Unique `MatchType`. D6 adds target-only rule `BT000012`, whose `MatchType` is
`Matched — ledger reference, no entry`; its deterministic formula is “ledger reference is present
and no FDCHDR EntryCode resolves”, it has `RequiresFDCHDR=No` and `RequiresReview=Yes`, and it is
excluded from the reconciliation numerator. Review is structurally required by D6's distinct,
visible Decision-class result; the five fields D6 does not decide are explicitly blank with
`absent` provenance.
The complete field-level row, required evidence, test case, and next-unused-code allocation rule
are normative in `docs/p1-0-c20-authored-rows.md`. The distinct BNKREC
`RecordedMatchStatus='Matched — ledger reference, no entry'`, using the exact wording already fixed
by C18. They are different columns with different jobs: `GovernedMatchRuleCode` references the
BNKMAT rule's BTCode, while `RecordedMatchStatus` records the result. The recorded status is
excluded from the reconciliation percentage and emits Decision-class review work.

`MaxDateWindowDays` and `ToleranceAmount` intentionally remain text because the pinned source
contains qualified ranges, chronological/payout-cycle descriptions, mixed amount/percentage
controls, and an explicitly undefined tolerance. Parsing them into integer/money would either
fail valid source rows or invent meaning. P1-0 adds no typed normalized copies. Before any P1-11
matcher activates a rule, its separately versioned deterministic control must define typed
operands appropriate to that rule (for example duration, money, percent, or payout-cycle policy),
required evidence, and tests while retaining these source texts verbatim as provenance.

### BNKREC

`Id id PK`; `Company varchar(128) FK CompanyRegistry.CompanyCode`; `BRCode code NOT NULL UNIQUE`;
`BRCodeBucket varchar(32) GENERATED ALWAYS AS ('BNKREC') STORED`; `MatchGroupID text`; `BMCode
code`; `BankAccount varchar(64)`;
`BankDate date`; `BankDescription text`; `BankAmount money`; `BankCurrency char(3)`; `EntryCode
code NULL`; `DocumentType short NULL`; `DocumentEntity varchar(128) NULL`; `DocumentDate
date NULL`; `DocumentDesc text NULL`; `DocumentAmount money NULL`; `DocumentCurrency char(3)
NULL`; `RecordedMatchStatus varchar(64)` (rendered to the source `MatchType` column; the 12 annex
values are pinned imports and `Matched — ledger reference, no entry` is the exact target-only D6
addition);
`GovernedMatchRuleCode code NULL FK BNKMAT.BTCode`;
`ReconciliationMatchType text NULL` (source-only free text, no FK and not authoritative);
`Difference money NULL`; `GroupBankTotal money NULL`; `GroupDocumentTotal money NULL`;
`GroupDifference money NULL`; `ConsistencyFlag varchar(64) NULL`; `Notes text NULL`; plus
`AppliedMatcherProposalId id NULL` with composite FK `(AppliedMatcherProposalId, Company)` to
MatcherProposal. It is required for every post-go-live matcher-produced reconciliation row;
imported history uses its ImportEvidenceRow anchor and leaves this field NULL rather than
fabricating a proposal.

`Company` is production-only and omitted from the pinned export. `MatchGroupID` is a run-scoped,
explicitly non-permanent grouping label over the recomputed reconciliation view. It has no
CodeLedger bucket and must never be quoted as a stable identifier or cited in a decision record.
BRCode retains its permanent-code bucket. The generated constant `BRCodeBucket` and restrictive
composite FK `(BRCodeBucket, BRCode)` to `CodeLedger(Bucket, Code)` bind every non-null, unique
BRCode to exactly the surviving `BNKREC` bucket; no application path can supply a different bucket.
Its measured natural key and collision evidence are recorded in
`docs/p1-0-c20-authored-rows.md`, and issuance remains P1-1b. Restrictive composite FKs
`(Company, BMCode)` to BNKMOV, `(Company, BankAccount)` to BNKACC, and nullable `(Company,
EntryCode)` to FDCHDR prevent cross-company links. Add `FindingDiscriminator varchar(64) NOT NULL`
(`'link'` for the ordinary pairing; a stable
detector code for an additional no-entry finding). The permanent alternate key is exactly the
unique constraint `(Company, BMCode, EntryCode, FindingDiscriminator) NULLS NOT DISTINCT`; it
never includes run-scoped `MatchGroupID`. Thus one ordinary
unmatched row with NULL EntryCode is unique, while distinct proven findings on that movement can
coexist without relying on PostgreSQL's default multiple-NULL behavior. `MatchGroupID`, `BMCode`,
and discriminator are nonblank; document-side columns must all be NULL when EntryCode is NULL,
and EntryCode requires a resolvable FDCHDR row. At the pin BRCode is a fresh order-derived export
code; production binds it permanently using the measured key decision before issuance in P1-1b.
PostgreSQL retains `Id` for internal joins plus the declared permanent alternate key above. Group
totals count each unique movement/document once and exclude
finding-only rows from the document denominator.

`BNKMOVReconciliationRate` is a view grouped by Company. Its denominator is `COUNT(DISTINCT
BNKMOV.BMCode)` over all company movements. Its numerator is the distinct BMCode count for which a
BNKREC row exists with `RecordedMatchStatus <> 'Unmatched'`, excluding the v5.0
`Matched — ledger reference, no entry` status; Internal therefore counts as reconciled. The view
exposes numerator, denominator, and `100.0 * numerator / NULLIF(denominator, 0)`. Baseline at
`9359c67` is 55.9% (approximately 1,096 of 1,960). The prototype's 94.6% measures an unreconstructible
different population; neither figure may be quoted without its definition. P1-11 acceptance is
55.9% ±0.1% from this view, not 94.6%.

### RECREV

Same common queue mapping/lifecycle as DOCRQE, retaining source-only `ProposedMatchType text NULL`
and deriving `ProposedMatchBTCode code NULL FK BNKMAT.BTCode` when the source text resolves exactly;
these replace `TargetTable/TargetField/ProposedValue`. The pinned renderer has exactly 21 fields, in
this order: `ReviewID`, `Priority`, `Status`, `CreatedDate`, `Layer`, `EvaluationFlag`,
`BlockingLevel`, `SourceRecordType`, `SourceRecordID`, `DocID`, `DocFilename`, `ReviewType`,
`ProposedMatchType`, `CurrentValue`, `AIProposal`, `UserDecision`, `UserInput`, `Observations`,
`DecidedBy`, `DecisionDate`, `AppliedReference`. Pinned `ItemKey` is persisted engine-only and is
not rendered. Production `Company`, `ItemClass` (always Decision for RECREV by D8),
`MatcherProposalId`, `SuccessorReviewID`, and `SupersedeReason` are also persisted/enforced but are
not claimed as pinned rendered fields. Add unique `(ReviewID, Company)` and the same matcher-origin
CHECK and composite MatcherProposal FK as DOCRQE.
Unique ItemKey; never-delete; rejected proposals persist as matcher constraints.

## 6. Layer 6 ground-truth slice

PAYCTR and RCVCTR share: permanent code (`PAYCODE`/`RCVCODE`) PK; `CompanyCode varchar(128) FK
CompanyRegistry.CompanyCode`; `FiscalDocumentID code`; `DocID text`; `EntityCode varchar(128)`;
`DocDate date`;
`DueDate date NULL`; `DocAmount money`; `Currency char(3)`; `FEX rate NULL`; `LocalAmount money
NULL`; `PayEntity varchar(128)`; `PayBankAccount varchar(64) NULL`; `PayCurrency char(3) NULL`;
`PayDate date NULL`; `PayDocAmount money NULL`; `PaidAmount money`; `PayDocID text NULL`;
`BankReference text NULL`; `MatchType varchar(64) NULL`; `DueAmount money`; `Status varchar(32)`;
plus `ImportPeriod char(6)`, `ImportBatchId id`, `EnteredAt instant`,
`EnteredBy short`. The pinned source is one control header per `FiscalDocumentID` and keys
PAYCODE/RCVCODE on that field alone; production uses key `(CompanyCode, FiscalDocumentID)` and a
composite FK on that same pair to FDCHDR. `PaymentSequence` does not exist on a control header.
CompanyCode is resolved from the authenticated import context and must exactly match the preserved
source field. `ImportPeriod` and `ImportBatchId` record which file governs the current row.

All PAYCTR/RCVCTR writes use the controlled `UpsertControlSnapshot` procedure; application roles
have no direct INSERT, UPDATE, or DELETE grant on either table. `ControlGoverningPeriod` stores
`(ControlKind, CompanyCode, FiscalDocumentID, MaxImportPeriod, LastImportBatchId)` with PK on the
first three fields and a composite batch/company FK; it survives deletion of a current-state
header. For declared company and period P, the procedure stages and validates the complete
snapshot, locks the affected governing-period rows, and rejects the whole batch with SQLSTATE
`23514` if any staged key has `MaxImportPeriod > P`. Only after that check, one transaction deletes
rows currently governed by P that are absent from the replacement snapshot, upserts the staged
rows, and advances the ledger with `greatest(MaxImportPeriod, P)`; rows governed by every other
period remain untouched. A named `RejectControlGoverningPeriodRegression` BEFORE INSERT OR UPDATE
trigger on both current-state tables independently queries this ledger and raises SQLSTATE `23514`
when `NEW.ImportPeriod < MaxImportPeriod`. The retained high-water row prevents delete-then-insert
from bypassing the guard. Same-period replacement and a strict advance are allowed; partial
acceptance of an older batch is not. This is the executable C17 reject semantics for D9. A non-NULL
PayBankAccount has composite FK `(CompanyCode, PayBankAccount)` to BNKACC; an unknown or
cross-company account fails closed rather than being retained as a plausible link.

PAYDTL/RCVDTL share: permanent code (`PayDtlCode`/`RcvDtlCode`) PK; plus production-only
`CompanyCode varchar(128) FK CompanyRegistry.CompanyCode`; `FiscalDocumentID code`; `DocID text`;
`PayEntity varchar(128)`; `PayBankAccount varchar(64) NULL`; `PayDate date`;
`PayAmount money`; `Currency char(3)`; `BankReference text NULL`; `Source varchar(32)`; `Tier
varchar(32)`; `ResolvedCodeName varchar(128) NULL`; `Notes text NULL`; plus `PaymentSequence
integer`, `SequenceTieBreakHash char(64)`, `ImportPeriod char(6)`, `ImportBatchId id`, `EnteredAt
instant`, `EnteredBy short`.
Composite FK `(CompanyCode, FiscalDocumentID)` targets FDCHDR and unique `(CompanyCode,
FiscalDocumentID, PaymentSequence)`. A detail also has a composite FK to its matching company-scoped
PAYCTR/RCVCTR header. `CompanyCode` is derived from that header and omitted from the pinned detail
export. Sequence is derived deterministically by ordered `(PayDate, BankReference, PayAmount,
SequenceTieBreakHash)`, where the final value is the canonical source-row hash. PayDtlCode and
RcvDtlCode are permanent, uniquely indexed, and bound in CodeLedger to `(CompanyCode,
FiscalDocumentID, PaymentSequence)`. Shuffled cold-rebuild tests must reproduce both the sequence
and codes with zero movement. A non-NULL PayBankAccount uses the same
composite `(CompanyCode, PayBankAccount)` FK to BNKACC.

ENTBLC and the remaining management/aging views stay Phase 2 and are not created by P1-1.

## 7. Supporting production tables

`ExportRun`: `Id id PK`, `RequestedBy short`, `RequestedAt instant`, `Target varchar(24)`,
`SchemaFingerprint char(64)`, `RowCounts json`, `Outcome varchar(24)`, `CompletedAt instant NULL`,
`Error text NULL`, `CorrelationId varchar(128)`; idempotency key `(Target, CorrelationId)`.

`CodeLedger`: `Bucket varchar(32)`, `NaturalKeyHash char(64)`, `NaturalKey json`, `KeyVersion
varchar(32)`, `Code code`, `IssuedAt instant`, `IssuedBy short`; PK `(Bucket, NaturalKeyHash)`,
unique `(Bucket, Code)`. It is append-only. Each bucket references a non-cycling PostgreSQL
sequence; `nextval()` values lost to rollback, failed commit, concurrency loss, or crash remain
gaps and are never inserted later. The ledger records successful bindings, while the sequence's
non-transactional high-water mark guarantees that unbound burned values are not reused.

`RejectedMatcherConstraint`: `Id id PK`, `Company varchar(128) FK CompanyRegistry.CompanyCode`,
`MatcherKind varchar(64)`, `SourceRecordType varchar(32)`, `SourceRecordID text`,
`CandidateFingerprint char(64)`, `DOCRQEReviewID code NULL FK DOCRQE`, `RECREVReviewID code NULL FK
RECREV`, `RejectedAt instant`, `RejectedBy short`, `Reason text`; unique `(Company,
SourceRecordType, SourceRecordID, CandidateFingerprint)`, with a CHECK requiring exactly one
rejected review FK. `MatcherKind` records which matcher produced the rejected proposal but is audit
metadata, not rejection identity. The review links are composite `(DOCRQEReviewID, Company)` and
`(RECREVReviewID, Company)` FKs to the queue alternate keys, so a rejection cannot cite another
company's decision.
It is append-only.

`MatcherProposal`: `Id id PK`, `Company varchar(128) FK CompanyRegistry.CompanyCode`, `MatcherKind
varchar(64)`, `SourceRecordType varchar(32)`, `SourceRecordID text`, `CandidateFingerprint char(64)`,
`GovernedMatchRuleCode code NULL FK BNKMAT.BTCode`, `ProposalPayload json`, `ProposalStatus
varchar(24)`, `CreatedAt instant`, `CreatedBy short`, `CorrelationId varchar(128)`; unique
`(Company, MatcherKind, SourceRecordType, SourceRecordID, CandidateFingerprint)` and unique `(Id,
Company)`. `CandidateFingerprint` is the deterministic SHA-256 of the versioned canonical candidate
identity, not free-form explanation text, and its canonicalization is matcher-independent so the
same candidate has the same fingerprint across matcher implementations.

Every matcher database role has only EXECUTE on `SubmitMatcherProposal` and no direct write grant
on MatcherProposal, DOCRQE, RECREV, BNKREC, or RejectedMatcherConstraint. The function inserts the
shared MatcherProposal row; the deterministic proposal router alone may create a queue row or apply
an auto-closable governed proposal. Matcher-generated DOCRQE/RECREV rows carry nullable
`MatcherProposalId`, required for matcher-origin rows and unique/FK to MatcherProposal; applied
BNKREC rows carry `AppliedMatcherProposalId` with the same company-scoped FK. Non-matcher findings
leave these columns NULL. This permission boundary plus the required queue/result FKs makes the
shared path structural rather than a caller convention.

`RejectPreviouslyRejectedProposal` is a PostgreSQL constraint trigger on MatcherProposal, `AFTER
INSERT OR UPDATE OF Company, SourceRecordType, SourceRecordID, CandidateFingerprint`,
`DEFERRABLE INITIALLY IMMEDIATE`. Its trigger function queries RejectedMatcherConstraint with all
four identity fields—Company, SourceRecordType, SourceRecordID, and CandidateFingerprint—and raises
SQLSTATE `23514` on a match, rolling back the proposal before routing or queue/result persistence.
It deliberately does not compare MatcherKind: a rejection originating from matcher A blocks the
same candidate from matcher B and every later matcher. Only the authenticated decision-application
path may append a rejection constraint. No matcher-specific bypass or private proposal path is
permitted.

`RetainedContentHash`: `Id id PK`, `CompanyId id FK BusinessEntity.Id`, `Sha256 char(64)`, `ByteLength bigint`,
`FirstSeenAt instant`; unique `(CompanyId, Sha256, ByteLength)`. `DocumentCaptureHash`:
`DocumentId id PK FK Document`, `RetainedContentHashId id FK RetainedContentHash`, `CapturedAt
instant`; non-unique index on `RetainedContentHashId`. This is a canonical hash ledger with many
capture events, not a uniqueness constraint on captures. The existing global unique
`Documents.OriginalContentHashSha256` index is incompatible. Its swap is an independently reviewed
P1-1b migration and is never folded into P1-1a's initial squashable train.

`ExtractionAttempt`: `Id id PK`, `DocumentId id FK Document`, `AttemptNumber integer`, `JobId id
NULL`, `CorrelationId varchar(128)`, model/skill/instruction/contract versions, `StartedAt instant`,
`CompletedAt instant NULL`, `Outcome varchar(24)`, `ResponseSha256 char(64) NULL`, `ResponseBytes
bigint NULL`, `ErrorCode varchar(64) NULL`, `ErrorPath text NULL`, `SanitizedError text NULL`,
`ReviewRoutingKey varchar(256) NULL`; unique `(DocumentId, AttemptNumber)`, unique `(Id,
DocumentId)`, and unique non-NULL `ReviewRoutingKey`. Invalid payloads create no ExtractionRevision; this attempt/error evidence and
its audit event persist independently and drive idempotent DOCFAI/review routing.

`ImportBatch`: `Id id PK`, `CompanyId id`, `CompanyCode varchar(128)`, `Source text`,
`StartedAt instant`, `CompletedAt instant NULL`, `RowCount bigint`, `Status varchar(24)`,
`CreatedBy short`, source SHA, declared period/year, prototype SHA, validation result, and
correlation ID. `(CompanyId, CompanyCode)` has a composite FK to CompanyRegistry and unique
`(Id, CompanyCode)` supports company-scoped import-child FKs. No source row payload is logged.
PAY/RCV headers and details use composite `(ImportBatchId,
CompanyCode)` FKs rather than an unscoped batch reference.

`ImportEvidenceRow`: `Id id PK`, `ImportBatchId id FK ImportBatch`, `CompanyCode varchar(128) FK
CompanyRegistry.CompanyCode`, `SourceTable varchar(16)`, `SourcePermanentCode varchar(128) NULL`,
`SourceNaturalKeyHash char(64)`, `SourceRowHash char(64)`, `SourceRecordKey varchar(128) NULL`,
`TargetTable varchar(16)`, `TargetPermanentCode varchar(128)`;
unique
`(ImportBatchId, SourceTable, SourcePermanentCode)` and unique `(Id, TargetTable,
TargetPermanentCode)`, plus unique `(Id, CompanyCode, TargetTable, TargetPermanentCode)` for
company-scoped target FKs. CompanyCode must equal the parent ImportBatch registry code. It anchors
imported rows to the pinned source and batch without storing
source values or pretending that a legacy row came from a current Document/ExtractionRevision.
An additional CHECK requires `SourceTable='FDCHDR'` whenever `TargetTable='FDCHDR'`; a
ReferenceOnly source row therefore cannot be repurposed as fiscal-entry evidence.
Ordinary coded rows use SourcePermanentCode. `USEROBS` rows instead require SourcePermanentCode
NULL and store the observation `key` in SourceRecordKey, with unique `(ImportBatchId, SourceTable,
SourceRecordKey)`; they set `TargetTable='UserObservation'` and TargetPermanentCode to that same
key. A check requires exactly one of SourcePermanentCode/SourceRecordKey, and only USEROBS may use
the latter. This preserves evidence for a human-authored source record that has no FDR code.

`ManualEntryEvidence`: `Id id PK`, `CompanyId id`, `Company varchar(128)`, `CommandId
varchar(128)`, `EnteredBy short`,
`EnteredAt instant`, `Reason text`, `PayloadHash char(64)`, `CorrelationId varchar(128)`; unique
`(Company, CommandId)` and unique `(Id, CompanyId, Company)`, with composite `(CompanyId, Company)`
FK to CompanyRegistry. It is append-only, authenticated,
company-scoped, audited, and stores the
exact accepted manual-entry command separately from FDCHDR. It is not an ExtractionRevision and
cannot be created by Claude.

`ArchiveNameLedger`: `Id id PK`, `CompanyId id`, `Company varchar(128)`, `DestinationContext json`,
`ContextHash char(64)`, `CaptureIdentity id`, `Version
integer`, `Filename text`, `IssuedAt instant`, `IssuedBy short`; unique `(CompanyId, ContextHash,
CaptureIdentity)`, unique `(CompanyId, ContextHash, Version)`, and case-insensitive unique `Filename` within
the destination directory. Composite FK `(CompanyId, Company)` targets CompanyRegistry and
`(CaptureIdentity, CompanyId)` targets Document `(Id, BuyerEntityId)`. It is append-only.
Per-context non-cycling sequences burn values on
rollback/concurrency loss and enforce `01..99` for fiscal/legal/procurement/other and `v0001..v9999`
for bank statements. Allocation conflict winner reads use the full `(CompanyId, ContextHash,
CaptureIdentity)` key, and version ownership checks use `(CompanyId, ContextHash, Version)`; a hash
collision or identical context hash in different companies therefore cannot couple their captures
or version streams. Overflow or a path/hash ownership mismatch fails closed before archive write.

## 8. Existing integration ledger

Decision: preserve the existing non-null `IntegrationCommit.DocumentId` and
`IntegrationCommit.ExtractionRevisionId` restrictive FKs exactly, including the existing unique
provider/document/revision key. `Document` remains the immutable intake/evidence aggregate and
`ExtractionRevision` remains the exact accepted proposal revision sent to an integration; neither
row is rewritten into an FDR row. No manual, bank-generated, or imported row fabricates an
ExtractionRevision merely to become integration-eligible.

FDCHDR's four evidence modes are exhaustive and enforced by one check over nullable anchors:

| EvidenceMode | Required | Must be NULL | Additional checks |
|---|---|---|---|
| `NativeExtracted` | `DocumentId`, `ExtractionRevisionId`, `EvidenceDocumentState='Posted'` | `ManualEntryEvidenceId`, `ImportEvidenceRowId`, `SourceKey` | revision and Posted DOCLOG disposition belong to Document; `ManualEntry=false`, `AutoEntry=false`, `DataSource='InvoiceDocument'` |
| `NativeManual` | `ManualEntryEvidenceId` | `DocumentId`, `ExtractionRevisionId`, `ImportEvidenceRowId`, `SourceKey` | authenticated evidence company matches FDCHDR company; `ManualEntry=true`, `AutoEntry=false`, `DataSource='ManualEntry'` |
| `BankGenerated` | `SourceKey` and one or more `FdcHdrBankMovement` rows | `DocumentId`, `ExtractionRevisionId`, `ManualEntryEvidenceId`, `ImportEvidenceRowId` | `FlowType IN ('P','F','O')`; `ManualEntry=false`, `AutoEntry=true`, `DataSource='BankExtract'` |
| `ImportedHistory` | `ImportEvidenceRowId` | `DocumentId`, `ExtractionRevisionId`, `ManualEntryEvidenceId` | import target is this FDCHDR row; SourceKey is allowed only for a verified imported bank-generated P/F/O row and requires its complete ordered junction rows |

The FKs are ordinary implementable restrictive constraints: unique `(Id, DocumentId)` on
ExtractionRevision plus `FOREIGN KEY (ExtractionRevisionId, DocumentId) REFERENCES
ExtractionRevision(Id, DocumentId) ON DELETE RESTRICT`; `FOREIGN KEY (DocumentId, CompanyId)
REFERENCES Document(Id, BuyerEntityId) ON DELETE RESTRICT`; `FOREIGN KEY (ManualEntryEvidenceId,
CompanyId, Company) REFERENCES ManualEntryEvidence(Id, CompanyId, Company) ON DELETE RESTRICT`;
`FOREIGN KEY (DocumentId, EvidenceDocumentState) REFERENCES DOCLOG(DocumentId, DocumentState) ON
DELETE RESTRICT`;
the two restrictive composite FKs from `FdcHdrBankMovement` to FDCHDR and BNKMOV; and `FOREIGN KEY
(ImportEvidenceRowId, Company, ImportTargetTable, EntryCode) REFERENCES ImportEvidenceRow(Id,
CompanyCode, TargetTable, TargetPermanentCode) ON DELETE RESTRICT`. Nullable composite FKs become active only when their mode
requires every component; the EvidenceMode check rejects partial anchors. Application validation
repeats the constraints with company scope before insert. During import, an existing
Document/ExtractionRevision link is not reused for FDCHDR: the row remains `ImportedHistory` and is
anchored to its exact ImportEvidenceRow. Filename, amount, date, or semantic similarity never
manufactures a link.

When integrations resume in Phase 2, a nullable `EntryCode` may be added to new commits and
backfilled only where the commit's existing DocumentId/ExtractionRevisionId resolve to exactly one
FDCHDR row. Do not repoint historical integration rows directly to FDCHDR and do not delete the
existing anchors.

## 9. Grandfathering and 2026 history import

Full 2026 DOCLOG/FDCHDR/FDCDTL and Layer 5 history plus the complete reference layer are imported;
2025 is excluded (D4). The complete-reference scope explicitly includes ENTMST, ENTALS, ITMMST,
ITMALS, ENTITM, ITMCLS, MNGACC, SNCACC, COCACC, EXCRAT, DOCTYP rules, DOCEFL rules, DOCFLG
instances, pinned `Editor/Data/user_observations.json`, and `reference_only_documents.json`.
ReferenceOnly payloads import into Document/DOCLOG/DOCARC without EntryCode. Source permanent codes
are preserved and verified before any runtime sequence is enabled.

The observations file is read as one versioned import source under the pinned prototype SHA. Its
whole-file SHA-256 and each note object's canonical source-row hash are evidence; neither note text
nor ReviewID is written to logs. One ImportEvidenceRow is created per unique note key with
`SourceTable='USEROBS'`, and its UserObservation and all ordered target memberships are created in
the same transaction. Array order and exact source ReviewID strings are retained as evidence even
through its ordinal constraint. A target that does not resolve is persisted as
Pending, not dropped or treated as permission to rewrite a ReviewID.

Each imported DOCLOG row is itself evidence of a capture event, so it creates exactly one current
Document intake/evidence aggregate linked to that DOCLOG row and its `ImportEvidenceRow`; nullable
fields with no source evidence remain NULL and no ExtractionRevision is fabricated. This does not
imply that an FDCHDR row links to that Document: duplicate-copy DOCLOG rows can share EntryCode,
and residual rows may have none. FDCHDR therefore uses the evidence-mode rule in section 8 instead
of guessing a Document link from filename or EntryCode.

Each imported DOCFLG retains its source `DetectedAt`. `EffectiveFrom` remains the FDR authoring
timestamp and grandfathers nothing. All 52 imported rules instead receive the Sibyla go-live
timestamp in `EnforcementStartsAt`; comparing each of the 2,787 imported instances to that value
therefore grandfathers all of them and leaves zero actively blocking on day one. New rules carry
their own later enforcement start. `IsGrandfathered` is derived once by comparing the instance's
timestamp to its rule's `EnforcementStartsAt`, never to EffectiveFrom or ImportBatch time. The
result and reason are persisted. `RejectDOCEFLEnforcementStartsAtMutation` prevents later comparand
drift after import or first use; no path recomputes a stored verdict. Grandfathered open instances remain visible, audited,
and reviewable; they simply do not gate execution. `blocking_open_instances()` is equivalent to:
`ItemClass=Decision AND Status in (Open, InReview) AND BlockingLevel <> Non-Blocking AND NOT
IsGrandfathered`.

Import validation is all-or-nothing and includes: authenticated CompanyRegistry resolution and
exact source Company/CompanyCode agreement; rejection of every attempted cross-company FDCHDR,
BNKACC, BNKMOV, BNKREC, PAY/RCV, manual-evidence, Document, or import-evidence link; code-ledger bijection/coverage/shuffled cold
rebuild; proven natural-key uniqueness; DOCLOG/FDCHDR resolution without deleting residual audit
rows; header-detail arithmetic and non-empty lines; entity/fiscal-number references; EXCRAT
coverage; DOCTYP coverage/treatment and one-row-per-OriginClass expansion; ENTITM source
`EntityCode` resolution to exactly ENTMST.CodeName semantics; DOCFLG rule references and detection
dates; ReferenceOnly
absence from posting tables; 2026 scope; exact source counts recorded in the batch control without
embedding row values in logs. The control record is `Initial + Expected = Result`.

Observation acceptance additionally requires: the pinned file hash and schema match; note keys are
unique and nonblank; exact UTF-8 observation text and its hash round-trip; every source DOCRQE and
RECREV array element appears exactly once by source ordinal as Resolved or Pending under the same
note; resolved targets
match the named queue and permanent ReviewID; existing queue Observations text remains byte-stable;
and a second projection appends nothing. A changed/missing note, target, or evidence anchor aborts
the import. Human observation records are append-only and cannot be overwritten by automation.

Before import commit, every imported permanent code is reserved in its bucket ledger under the
verified source natural key. Under the bootstrap-only advisory plus `ACCESS EXCLUSIVE` ledger lock
defined in `p1-0-codes-taxonomy-archive.md`, each unused bucket sequence is created (or initialized
once before use) to start strictly above the greatest verified imported suffix. The initialization
record and all imported bindings commit together before runtime allocation is enabled. Runtime
`setval()`/restart, reconstruction from `max(code)`, backward movement, and using sequence state to
infer a missing binding remain forbidden. Imported rows receive `ImportEvidenceRow` anchors;
current `Document`/`ExtractionRevision` rows are not fabricated merely to satisfy an FK. The batch
commits only after every source permanent code and natural key has exactly one target and every
target imported row has exactly one source evidence anchor.

## 10. Mapping acceptance checks

- A malformed response leaves Document and ExtractionAttempt evidence, creates attempt-keyed
  DOCFAI/Decision routing with NULL LGCode, and creates no ExtractionRevision, DOCLOG, DOCARC,
  FDCHDR, or FDCDTL. A schema-valid `NOT_A_DOCUMENT` creates the normal ExtractionRevision,
  DOCLOG/LGCode, DOCFAI, and Decision route without creating FDCHDR/FDCDTL.
- Every FDCHDR row satisfies exactly one of the four evidence-mode rows in section 8. Composite
  extraction/document, manual-evidence, import-evidence, and ordered bank-movement junction FKs
  reject deletion and cross-anchor fabrication; all seven aggregate payroll documents resolve,
  and existing IntegrationCommit FKs and unique keys are unchanged.
- Authenticated company resolution is authoritative. Every preserved Company/CompanyCode value has
  a CompanyRegistry FK, every financial child uses its declared company-scoped composite parent
  key, and same-identifier cross-company inserts fail in database integration tests.
- The source-fiscal partial semantic index and bank-generated partial SourceKeyHash index are both
  unique, mutually exclusive, and exercised by concurrency tests. SourceFile never affects either.
- Migration preflight proves `pgcrypto.digest(bytea, text)` is available before creating
  SourceKeyHash; absence aborts P1-1 without creating the generated column or continuing the seed.
- Concurrent code allocation returns one binding for one canonical key, burns losing numbers, and
  detects key/code collisions. Import bootstrap is exclusive and one-time; a second bootstrap or
  any runtime setval/restart attempt is rejected.
- Archive-name concurrency returns the same suffix for the same capture and distinct non-reused
  suffixes for distinct same-context captures. Corrections never overwrite predecessors; occupied
  unowned paths and allocation 100/10000 fail before any business-copy write.
- DOCFLG's composite rule/class FK rejects ItemClass disagreement. Only non-grandfathered
  Open/InReview Decision rows can block, using exact `Non-Blocking` semantics. The complete
  recurrence key and supersession reason/pointer checks hold, and FL codes remain permanent and
  stable—with gaps—across reruns.
- Import/export comparison accounts separately for rendered and source-internal fields:
  PriorRelatedRecordID, ResolutionEvidence, Classified, ItemDesc, and RECREV ItemKey are preserved;
  no BNKMOV Source column or rendered RECREV ItemClass/ItemKey/SuccessorReviewID is fabricated.
- D4 import preserves every pinned USEROBS note, exact text, ReviewID target, pending target, and
  evidence hash; replay is idempotent and neither AI nor a rerun can replace human observations.
- Once the proposed C20+ seed input is supplied, DOCTYP must contain all 19 exact pinned tuples plus
  `Duplicate|External` as Include/ArchiveOnly, one row per `(DocumentType, OriginClass)`, and no
  multi-valued OriginClass cell. Until then this acceptance check is blocked, not passed. Coverage
  applies to captured documents (`Source <> 'BNK'`). ENTITM imports JSON by CodeName and workbook
  rows by resolving EC to CodeName; only unresolvable or ambiguous values fail.
- The BNKMOV reconciliation-rate view reproduces 55.9% ±0.1% under its declared numerator and
  denominator; its output is never compared to the prototype's differently defined 94.6% without
  stating both definitions.
- Reject a proposal from matcher A, then submit the same Company, SourceRecordType,
  SourceRecordID, and CandidateFingerprint from matcher B: the database raises SQLSTATE `23514`
  before queue/result persistence. Changing only MatcherKind never bypasses a prior rejection.
- DOCARC accepts a NULL DocumentDate only in the review-pending, archive-ineligible state. No
  archive path/date is fabricated; an authenticated date correction makes the existing class/date
  eligibility policy evaluable.
