# Manual invoice artifact contract

## Case artifacts

Each reusable case should contain:

```text
cases/<case-id>/
├── rule.yaml
├── query-manifest.yaml
├── queries/
├── schemas/
└── calculation/
```

- `rule.yaml` is the reviewed business interpretation.
- `query-manifest.yaml` maps required facts to BigQuery SQL, O11Y PromQL/LogQL/export parameters, outputs, and schemas.
- `schemas/` defines the interface between changeable queries and stable calculations.
- `calculation/` changes only after an explicit calculation-regeneration request.

## Run artifacts

Each execution should contain:

```text
runs/<run-id>/
├── input/
├── normalized/
├── evidence/
├── result.json
├── validation.json
├── lineage.json
├── manual-invoice.md
└── calculation/
```

When a user explicitly publishes the report to an existing Feishu/Lark document, store the redacted publication receipt at `evidence/lark-publication.json` before building the reconciliation bundle. Record the report hash, target document URL, update mode, timestamp, and command outcome; never record Lark credentials or raw authentication output.

Use immutable identifiers or hashes for:

- rule version and source snapshot;
- query bundle version and executed SQL;
- normalized input schema version;
- calculation version and script hash;
- run ID and result hash.

Never overwrite an approved or previously reviewed run. Create a new run ID for every rerun.

## Reconciliation bundle

Every completed reconciliation workflow must finish with a self-contained delivery directory outside the plugin development/cache directory:

```text
manual-invoice-output/<run-id>/
├── report.md
├── result.json
├── result.csv                 # optional
├── input/                     # request and non-secret parameters, when present
├── raw/                       # copied run evidence: SQL/LogQL, API responses, Job metadata
├── normalized/
├── calculation/
├── validation.json
├── lineage.json
└── manifest.json
```

Build it with `scripts/build_reconciliation_bundle.py`. The builder rejects symlinks and likely secret-bearing files or content, applies directory mode `0700` and file mode `0600`, refuses overwrite by default, and records each payload file's relative path, byte size, and SHA-256 hash in `manifest.json`. The manifest excludes its own hash.

The report must remain short and reviewable. Include the source and exact time range, customer/billing scope, calculation process or formula, result summary, validation and exceptions, and pointers to detailed result/raw files. Copy all stated totals from `result.json`; do not independently recompute them in Markdown.

## Calculation boundary

Calculations consume normalized files only. They must not query BigQuery/O11Y or read raw Lark documents directly. A source query may change without a calculation change only when its normalized output remains compatible in names, types, granularity, uniqueness, and business meaning.
