You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Import the authorized private PDF archives for 2024, 2025, and 2026 into the
bookkeeping document store delivered by #107. The migration must be an
operator-run script that defaults to dry-run, talks only to authenticated
bookkeeping APIs, is safe to resume, and proves that every accepted document and
relationship was reconciled. It must never use DynamoDB, S3 SDK, table, or bucket
credentials directly. A presigned upload obtained from the bookkeeping API is
part of the API upload lifecycle and is allowed.
The archives and all derived manifests/evidence are private operational data.
Keep them in their supplied local attachment location or project-local ignored .tmp/; never commit, attach, or copy them into tracked/public paths. Public
console output, tests, issue comments, CI logs, and CloudWatch logs may contain
aggregate counts and reason codes only—not document names, paths, hashes,
contents, transaction values, contacts, account details, signed URLs, or other
finance context.
Safe archive inventory
Add a dedicated document importer under backend/scripts/ with an npm command.
It accepts one or more ZIP archives plus a private mapping manifest and defaults
to dry-run. Dry-run may use authenticated read-only bookkeeping APIs to resolve
accounts, transactions, existing documents, and links, but it performs no
upload, create, update, delete, report snapshot, or archive-generation call.
Inspect ZIP central-directory metadata before reading members and enforce
configurable conservative limits for archive bytes, member count, per-member
compressed and uncompressed bytes, total uncompressed bytes, and compression
ratio. Structural and resource safety validation applies to every member before
manifest dispositions are considered. Reject the entire run before writes for
an encrypted member, absolute or traversal path, NUL/control character,
symlink or other non-regular member, duplicate normalized member path,
malformed ZIP, or exceeded limit; a manifest exclusion can never waive or hide
one of these failures.
After that validation, the private manifest may explicitly mark a safe regular
non-PDF member or safe nested-archive member as excluded, with an allow-listed
reason. Excluded members are counted but never opened, extracted, recursively
inspected, hashed as import candidates, uploaded, or imported. An unsupported or
nested member without an explicit exclusion still fails closed before writes.
Ignore only documented packaging metadata entries. Accepted import members must
have a .pdf extension and valid %PDF- magic bytes; never trust extension or
archive MIME metadata alone. Stream/hash accepted PDFs without extracting them
to a shared filesystem path.
Compute SHA-256 locally for identity, then ask the API whether that content is
already active. Content identity, not filename or archive location, controls
deduplication. Repeated content within/between the three archives and content
already stored by #107 resolves to one document record. The API must enforce
hash uniqueness atomically under concurrent requests and return a safe created/existing result; a duplicate must not leave a pending document or
orphan object. Idempotency keys must be deterministic and bounded. The API must
re-verify uploaded size, PDF magic, and SHA-256 before activation and clean up
failed/incomplete upload state.
Explicit mapping, statements, and links
Automatic OCR, PDF parsing, classification, account detection, and transaction
matching remain deferred as planned in #107. For this migration, a private
manifest supplies the operator-confirmed mapping. Identify each document in the
manifest by SHA-256 so renaming or moving an archive does not change identity.
Each mapping declares:
document type: invoice, receipt, bank statement, private-account statement,
or other evidence;
statement month and account ID for every business/private statement;
zero or more existing transaction references using stable bookkeeping sourceKey values (transaction IDs may be accepted only as an explicit
fallback and must be resolved through the API);
coverage type for each relationship (evidence or statement-coverage).
The importer must fail closed before writing when a manifest has duplicate
hash entries with conflicting metadata, an unknown account/transaction,
invalid type/month/coverage, an ambiguous transaction reference, a statement
without account/month, a non-statement with statement-only metadata, or a
non-import member without an explicit allowed exclusion. Documents intentionally
retained without a transaction link must be explicitly marked as such; omission
is not implicit approval. Exclusion entries identify an outer archive member
without exposing it publicly and include an allow-listed reason such as unsupported-image or nested-packaging-archive; they cannot target an
accepted PDF or suppress a structural/resource validation error.
Create links through the API after document activation. One statement or other
PDF can cover multiple ledger transactions, and one transaction can link to
multiple PDFs. Link uniqueness is the tuple of document, transaction, and
coverage type; retries return the existing link and create no duplicate.
Business statements use the configured business account IDs and statement-coverage; occasional private-account statements use an existing or
API-created private account, their statement month, and explicit covered
transactions. Do not infer coverage merely because a transaction falls in the
same month.
Controlled write, resume, reconciliation, and rollback
A write requires --write, an explicit target-environment confirmation, valid
portal/operator authentication, and a successful dry-run evidence file whose
archive/manifest fingerprints still match. The script must refuse a mismatched
or stale dry-run. Use bounded concurrency, retry only throttling/transient
failures with backoff, never retry permanent auth/validation failures blindly,
and checkpoint after each API result so an interrupted run resumes without
re-uploading or relinking completed work.
Assign every execution a non-sensitive migration run ID. Persist run ownership
and deterministic source provenance on newly created document/link metadata,
without exposing local paths. Pre-existing deduplicated documents/links are
recorded as reused, never claimed by the run. The private run evidence under .tmp/bookkeeping-document-import/<run-id>/ includes inventory counts, rejected
reason codes, manifest fingerprint, API-created/reused IDs, link outcomes,
checkpoints, and reconciliation/rollback results. It may contain private detail
needed by the operator but must be ignored by Git and excluded from public logs.
After writes, query the authenticated API and reconcile exactly:
accepted unique hashes = newly created documents + reused active documents;
every accepted manifest entry resolves to one active document with matching
size/hash/type/statement metadata;
requested unique links = newly created links + reused links;
every requested document/transaction/coverage tuple exists exactly once;
counts by archive year, document type, statement account/month, unlinked
approval, explicit exclusion reason, rejected reason, and create/reuse outcome
match the dry-run plan;
a second write run reports zero new documents and zero new links.
Any mismatch exits non-zero and does not claim success. Do not create immutable
monthly report snapshots as part of migration; reconciliation may use report
coverage/read APIs, while the operator creates a monthly package after reviewing
the imported evidence.
Provide API-only rollback for a named run with dry-run as the default and --rollback --write plus target confirmation for mutation. It deletes only
links and documents created and owned by that run, links first, using
authenticated APIs. It must preserve every pre-existing/reused record, refuse
to delete a document referenced by a report or non-run link, delete the private
S3 object as well as metadata for eligible run-owned documents, tolerate an
already-rolled-back retry, and reconcile that no run-owned resources remain.
Rollback failures are checkpointed and reported without private values.
Acceptance Criteria
The importer defaults to no-write dry-run, accepts all three archive years
plus a private manifest, uses authenticated bookkeeping API reads only during
preview, and requires --write, matching environment confirmation, and a
fingerprint-matching dry-run artifact before mutation.
ZIP handling rejects traversal/absolute paths, symlinks/non-regular
entries, encrypted or malformed members, normalized-path collisions, invalid
PDF magic, ZIP bombs, and configured count/size/ratio limits before any write.
Only safe regular non-PDF or nested-archive members explicitly excluded by the
private manifest may be skipped; they are never opened/extracted/imported,
while unmanifested unsupported/nested members fail closed. Accepted PDFs are
streamed rather than extracted into a shared path.
SHA-256 content identity deduplicates within one archive, across archive
years, against existing active documents, and under concurrent upload
requests. Duplicate/failed completion leaves no pending metadata or orphan
object.
The document API supports an idempotent prepare/upload/complete lifecycle
using existing portal/operator authentication, short-lived KMS-encrypted
presigned PUTs, server-side size/magic/hash verification, bounded requests,
safe responses, and no direct importer access to DynamoDB or S3 credentials.
Manifest validation is fail-closed and supports invoices, receipts,
business statements for either configured account, occasional private-account
statements, explicitly approved unlinked documents, and deterministic
transaction resolution through API-visible sourceKey values.
API link creation is idempotent for the document/transaction/coverage
tuple and supports one PDF covering multiple transactions plus multiple PDFs
linked to one transaction. Statement coverage is explicit, not inferred by
month.
Write execution uses bounded concurrency/backoff and durable private
checkpoints; interruption and rerun resume safely without duplicate uploads,
documents, links, or lost successful work.
Post-write reconciliation proves exact document and link equations,
metadata/link correspondence, categorized inclusion/exclusion/rejection
counts, and zero creates on a second run. A mismatch exits non-zero and
retains private diagnostic evidence.
API-only rollback defaults to preview, removes only run-owned links and
eligible run-owned document objects/metadata after explicit confirmation,
preserves reused or externally referenced data, is retry-safe, and verifies
the result.
Unauthorized document inventory, hash lookup, prepare, complete, link,
rollback, and reconciliation requests are rejected without leaking whether a
private hash, document, transaction, account, or link exists.
Public output and application logs contain only aggregate counts,
non-sensitive run/credential IDs, and allow-listed reason codes. Signed URLs,
document names/paths/hashes, PDF content, transaction values, contacts,
account details, and manifest rows never enter GitHub, CI, or CloudWatch logs.
Automated tests use generated synthetic ZIPs/PDFs/manifests only and
exercise real HTTP/Lambda route boundaries. Tests fail if the importer writes
directly through storage clients.
The full backend workflow passes: cd backend && npm test, npm run typecheck, and npm run build. If SAM/IAM/environment configuration
changes, also run sam validate --lint --template-file infra/template.full.yaml
and sam build --template-file infra/template.full.yaml.
A tracked-file/privacy scan and git status confirm the supplied archives,
manifests, fingerprints, checkpoints, PDFs, and reconciliation/rollback
artifacts remain only in ignored/private locations.
[HUMAN] With the AWS sandbox gate open, an authorized operator reviews the
private mapping manifest, runs dry-run then confirmed write for 2024–2026,
retains private pre/post evidence, confirms exact reconciliation, spot-checks
representative document/link/statement behavior in the authenticated portal,
and confirms the second write creates zero documents and zero links.
Test Scenarios
Scenario: Dry-run plans without mutation
Given: Synthetic year archives, an explicit manifest, and authenticated API
fixtures containing transactions, accounts, documents, and links
When: The importer runs without --write
Then: It validates and reconciles a deterministic plan, makes no mutating API
call, and emits only aggregate public output and private ignored evidence
Given: A synthetic archive has regular image and nested-archive members that
pass outer archive structural/resource checks
When: The private manifest explicitly excludes them with allowed reasons
Then: Dry-run counts the exclusions, never opens/extracts/imports them, and plans
only accepted PDFs; removing either exclusion makes the run fail closed
Scenario: Reject a hostile or oversized archive atomically
Given: ZIP fixtures containing traversal, symlink, encrypted, malformed,
invalid-PDF, duplicate-normalized-path, extreme-compression, and exceeded-limit
members, including variants also marked excluded in the manifest
When: Dry-run inventories each fixture
Then: It rejects the whole run before applying exclusions, returns allow-listed
reason codes, and performs no upload or metadata write
Scenario: Deduplicate content safely
Given: The same synthetic PDF appears under different names/years, already
exists remotely, or is prepared concurrently by two callers
When: The upload lifecycle runs or reruns
Then: Exactly one active document owns the hash, callers receive a safe
created/existing outcome, and no pending/orphan upload remains
Scenario: Model many-to-many evidence and statements
Given: A manifest where one statement covers several transactions, one
transaction has several evidence PDFs, both business accounts have statements,
and one private statement has explicit coverage
When: The confirmed import creates links
Then: Every requested tuple exists once with the declared account, month, type,
and coverage; no same-month relationship is inferred
Scenario: Resume after partial failure
Given: A confirmed write stops after some documents and links succeed
When: The same fingerprinted run resumes
Then: Checkpoints and API idempotency reuse completed work and create only the
missing resources
Scenario: Reject changed input after approval
Given: A successful dry-run evidence file
When: An archive or manifest changes before --write
Then: The importer refuses to mutate and requires a new dry-run
Scenario: Reconcile and prove a no-op rerun
Given: A completed migration
When: The importer queries API state and then repeats the confirmed write
Then: All document/link equations match and the rerun reports zero creates
Scenario: Roll back only run-owned data
Given: A run containing new and reused documents/links, with one run-owned
document referenced outside the run
When: Rollback is previewed, confirmed, interrupted, and retried
Then: Eligible run-owned links/objects/metadata are removed, reused and
externally referenced data are preserved, the refusal is explicit, and the
final reconciliation is accurate
Scenario: Deny unauthorized probing
Given: Missing or invalid operator authentication
When: A caller attempts hash lookup, upload, completion, linking, inventory, or
rollback
Then: The API returns an authorization error without disclosing resource
existence or private metadata
Out of Scope
OCR, PDF text extraction, automatic classification, candidate scoring, or
automatic document-to-transaction/account matching.
Parsing bank-statement lines or creating/updating ledger transactions from
PDFs; the already imported bookkeeping ledger remains authoritative.
Creating historical monthly report snapshots/ZIPs automatically during this
migration, emailing an accountant, or exposing public/permanent download
links.
Importing or recursively inspecting non-PDF attachments or nested archives.
Safe regular members of those types may only be explicitly excluded; encrypted
archives, corrupted documents, unsafe members, and files without explicit
manifest disposition remain rejected.
Changing or deleting the source Google Sheet/workbook or the supplied source
archives.
Changes to source repositories outside DataTalksClub/dataops or adding
operational documents to this public repository.
Real archives, mapping manifests, dry-run approvals, checkpoints,
reconciliation outputs, and rollback evidence are private operator artifacts
and must remain in ignored .tmp/ or the separate private knowledge boundary.
The authorized aggregate source baseline is 445 top-level PDFs, three
top-level non-PDF images, and two top-level nested packaging archives. Eight
top-level PDF copies are content duplicates, leaving 437 unique top-level PDFs.
The nested packaging archives contain 28 PDFs already byte-identical to
top-level PDFs; those archives are explicitly excluded and never
extracted/imported. Dry-run and post-write reconciliation must account for
these aggregate inclusion, deduplication, and exclusion counts exactly.
The existing create-only transaction ingestion credential intentionally has
no file permissions. This migration uses portal/operator authentication for
document APIs; it must not broaden the webhook credential into document read,
download, delete, report, or admin access.
Migrate 2024–2026 bookkeeping PDFs through an idempotent document API
Status: pending
Tags:
enhancement,migration,backend,data,testing,P1Depends on: #107
Blocks: None
Scope
Import the authorized private PDF archives for 2024, 2025, and 2026 into the
bookkeeping document store delivered by #107. The migration must be an
operator-run script that defaults to dry-run, talks only to authenticated
bookkeeping APIs, is safe to resume, and proves that every accepted document and
relationship was reconciled. It must never use DynamoDB, S3 SDK, table, or bucket
credentials directly. A presigned upload obtained from the bookkeeping API is
part of the API upload lifecycle and is allowed.
The archives and all derived manifests/evidence are private operational data.
Keep them in their supplied local attachment location or project-local ignored
.tmp/; never commit, attach, or copy them into tracked/public paths. Publicconsole output, tests, issue comments, CI logs, and CloudWatch logs may contain
aggregate counts and reason codes only—not document names, paths, hashes,
contents, transaction values, contacts, account details, signed URLs, or other
finance context.
Safe archive inventory
Add a dedicated document importer under
backend/scripts/with an npm command.It accepts one or more ZIP archives plus a private mapping manifest and defaults
to dry-run. Dry-run may use authenticated read-only bookkeeping APIs to resolve
accounts, transactions, existing documents, and links, but it performs no
upload, create, update, delete, report snapshot, or archive-generation call.
Inspect ZIP central-directory metadata before reading members and enforce
configurable conservative limits for archive bytes, member count, per-member
compressed and uncompressed bytes, total uncompressed bytes, and compression
ratio. Structural and resource safety validation applies to every member before
manifest dispositions are considered. Reject the entire run before writes for
an encrypted member, absolute or traversal path, NUL/control character,
symlink or other non-regular member, duplicate normalized member path,
malformed ZIP, or exceeded limit; a manifest exclusion can never waive or hide
one of these failures.
After that validation, the private manifest may explicitly mark a safe regular
non-PDF member or safe nested-archive member as
excluded, with an allow-listedreason. Excluded members are counted but never opened, extracted, recursively
inspected, hashed as import candidates, uploaded, or imported. An unsupported or
nested member without an explicit exclusion still fails closed before writes.
Ignore only documented packaging metadata entries. Accepted import members must
have a
.pdfextension and valid%PDF-magic bytes; never trust extension orarchive MIME metadata alone. Stream/hash accepted PDFs without extracting them
to a shared filesystem path.
Compute SHA-256 locally for identity, then ask the API whether that content is
already active. Content identity, not filename or archive location, controls
deduplication. Repeated content within/between the three archives and content
already stored by #107 resolves to one document record. The API must enforce
hash uniqueness atomically under concurrent requests and return a safe
created/existingresult; a duplicate must not leave a pending document ororphan object. Idempotency keys must be deterministic and bounded. The API must
re-verify uploaded size, PDF magic, and SHA-256 before activation and clean up
failed/incomplete upload state.
Explicit mapping, statements, and links
Automatic OCR, PDF parsing, classification, account detection, and transaction
matching remain deferred as planned in #107. For this migration, a private
manifest supplies the operator-confirmed mapping. Identify each document in the
manifest by SHA-256 so renaming or moving an archive does not change identity.
Each mapping declares:
or other evidence;
sourceKeyvalues (transaction IDs may be accepted only as an explicitfallback and must be resolved through the API);
evidenceorstatement-coverage).The importer must fail closed before writing when a manifest has duplicate
hash entries with conflicting metadata, an unknown account/transaction,
invalid type/month/coverage, an ambiguous transaction reference, a statement
without account/month, a non-statement with statement-only metadata, or a
non-import member without an explicit allowed exclusion. Documents intentionally
retained without a transaction link must be explicitly marked as such; omission
is not implicit approval. Exclusion entries identify an outer archive member
without exposing it publicly and include an allow-listed reason such as
unsupported-imageornested-packaging-archive; they cannot target anaccepted PDF or suppress a structural/resource validation error.
Create links through the API after document activation. One statement or other
PDF can cover multiple ledger transactions, and one transaction can link to
multiple PDFs. Link uniqueness is the tuple of document, transaction, and
coverage type; retries return the existing link and create no duplicate.
Business statements use the configured business account IDs and
statement-coverage; occasional private-account statements use an existing orAPI-created private account, their statement month, and explicit covered
transactions. Do not infer coverage merely because a transaction falls in the
same month.
Controlled write, resume, reconciliation, and rollback
A write requires
--write, an explicit target-environment confirmation, validportal/operator authentication, and a successful dry-run evidence file whose
archive/manifest fingerprints still match. The script must refuse a mismatched
or stale dry-run. Use bounded concurrency, retry only throttling/transient
failures with backoff, never retry permanent auth/validation failures blindly,
and checkpoint after each API result so an interrupted run resumes without
re-uploading or relinking completed work.
Assign every execution a non-sensitive migration run ID. Persist run ownership
and deterministic source provenance on newly created document/link metadata,
without exposing local paths. Pre-existing deduplicated documents/links are
recorded as reused, never claimed by the run. The private run evidence under
.tmp/bookkeeping-document-import/<run-id>/includes inventory counts, rejectedreason codes, manifest fingerprint, API-created/reused IDs, link outcomes,
checkpoints, and reconciliation/rollback results. It may contain private detail
needed by the operator but must be ignored by Git and excluded from public logs.
After writes, query the authenticated API and reconcile exactly:
size/hash/type/statement metadata;
approval, explicit exclusion reason, rejected reason, and create/reuse outcome
match the dry-run plan;
Any mismatch exits non-zero and does not claim success. Do not create immutable
monthly report snapshots as part of migration; reconciliation may use report
coverage/read APIs, while the operator creates a monthly package after reviewing
the imported evidence.
Provide API-only rollback for a named run with dry-run as the default and
--rollback --writeplus target confirmation for mutation. It deletes onlylinks and documents created and owned by that run, links first, using
authenticated APIs. It must preserve every pre-existing/reused record, refuse
to delete a document referenced by a report or non-run link, delete the private
S3 object as well as metadata for eligible run-owned documents, tolerate an
already-rolled-back retry, and reconcile that no run-owned resources remain.
Rollback failures are checkpointed and reported without private values.
Acceptance Criteria
plus a private manifest, uses authenticated bookkeeping API reads only during
preview, and requires
--write, matching environment confirmation, and afingerprint-matching dry-run artifact before mutation.
entries, encrypted or malformed members, normalized-path collisions, invalid
PDF magic, ZIP bombs, and configured count/size/ratio limits before any write.
Only safe regular non-PDF or nested-archive members explicitly excluded by the
private manifest may be skipped; they are never opened/extracted/imported,
while unmanifested unsupported/nested members fail closed. Accepted PDFs are
streamed rather than extracted into a shared path.
years, against existing active documents, and under concurrent upload
requests. Duplicate/failed completion leaves no pending metadata or orphan
object.
using existing portal/operator authentication, short-lived KMS-encrypted
presigned PUTs, server-side size/magic/hash verification, bounded requests,
safe responses, and no direct importer access to DynamoDB or S3 credentials.
business statements for either configured account, occasional private-account
statements, explicitly approved unlinked documents, and deterministic
transaction resolution through API-visible
sourceKeyvalues.tuple and supports one PDF covering multiple transactions plus multiple PDFs
linked to one transaction. Statement coverage is explicit, not inferred by
month.
checkpoints; interruption and rerun resume safely without duplicate uploads,
documents, links, or lost successful work.
metadata/link correspondence, categorized inclusion/exclusion/rejection
counts, and zero creates on a second run. A mismatch exits non-zero and
retains private diagnostic evidence.
eligible run-owned document objects/metadata after explicit confirmation,
preserves reused or externally referenced data, is retry-safe, and verifies
the result.
rollback, and reconciliation requests are rejected without leaking whether a
private hash, document, transaction, account, or link exists.
non-sensitive run/credential IDs, and allow-listed reason codes. Signed URLs,
document names/paths/hashes, PDF content, transaction values, contacts,
account details, and manifest rows never enter GitHub, CI, or CloudWatch logs.
exercise real HTTP/Lambda route boundaries. Tests fail if the importer writes
directly through storage clients.
cd backend && npm test,npm run typecheck, andnpm run build. If SAM/IAM/environment configurationchanges, also run
sam validate --lint --template-file infra/template.full.yamland
sam build --template-file infra/template.full.yaml.git statusconfirm the supplied archives,manifests, fingerprints, checkpoints, PDFs, and reconciliation/rollback
artifacts remain only in ignored/private locations.
private mapping manifest, runs dry-run then confirmed write for 2024–2026,
retains private pre/post evidence, confirms exact reconciliation, spot-checks
representative document/link/statement behavior in the authenticated portal,
and confirms the second write creates zero documents and zero links.
Test Scenarios
Scenario: Dry-run plans without mutation
Given: Synthetic year archives, an explicit manifest, and authenticated API
fixtures containing transactions, accounts, documents, and links
When: The importer runs without
--writeThen: It validates and reconciles a deterministic plan, makes no mutating API
call, and emits only aggregate public output and private ignored evidence
Scenario: Exclude supported packaging exceptions safely
Given: A synthetic archive has regular image and nested-archive members that
pass outer archive structural/resource checks
When: The private manifest explicitly excludes them with allowed reasons
Then: Dry-run counts the exclusions, never opens/extracts/imports them, and plans
only accepted PDFs; removing either exclusion makes the run fail closed
Scenario: Reject a hostile or oversized archive atomically
Given: ZIP fixtures containing traversal, symlink, encrypted, malformed,
invalid-PDF, duplicate-normalized-path, extreme-compression, and exceeded-limit
members, including variants also marked excluded in the manifest
When: Dry-run inventories each fixture
Then: It rejects the whole run before applying exclusions, returns allow-listed
reason codes, and performs no upload or metadata write
Scenario: Deduplicate content safely
Given: The same synthetic PDF appears under different names/years, already
exists remotely, or is prepared concurrently by two callers
When: The upload lifecycle runs or reruns
Then: Exactly one active document owns the hash, callers receive a safe
created/existing outcome, and no pending/orphan upload remains
Scenario: Model many-to-many evidence and statements
Given: A manifest where one statement covers several transactions, one
transaction has several evidence PDFs, both business accounts have statements,
and one private statement has explicit coverage
When: The confirmed import creates links
Then: Every requested tuple exists once with the declared account, month, type,
and coverage; no same-month relationship is inferred
Scenario: Resume after partial failure
Given: A confirmed write stops after some documents and links succeed
When: The same fingerprinted run resumes
Then: Checkpoints and API idempotency reuse completed work and create only the
missing resources
Scenario: Reject changed input after approval
Given: A successful dry-run evidence file
When: An archive or manifest changes before
--writeThen: The importer refuses to mutate and requires a new dry-run
Scenario: Reconcile and prove a no-op rerun
Given: A completed migration
When: The importer queries API state and then repeats the confirmed write
Then: All document/link equations match and the rerun reports zero creates
Scenario: Roll back only run-owned data
Given: A run containing new and reused documents/links, with one run-owned
document referenced outside the run
When: Rollback is previewed, confirmed, interrupted, and retried
Then: Eligible run-owned links/objects/metadata are removed, reused and
externally referenced data are preserved, the refusal is explicit, and the
final reconciliation is accurate
Scenario: Deny unauthorized probing
Given: Missing or invalid operator authentication
When: A caller attempts hash lookup, upload, completion, linking, inventory, or
rollback
Then: The API returns an authorization error without disclosing resource
existence or private metadata
Out of Scope
automatic document-to-transaction/account matching.
PDFs; the already imported bookkeeping ledger remains authoritative.
migration, emailing an accountant, or exposing public/permanent download
links.
Safe regular members of those types may only be explicitly excluded; encrypted
archives, corrupted documents, unsafe members, and files without explicit
manifest disposition remain rejected.
archives.
DataTalksClub/dataopsor addingoperational documents to this public repository.
Dependencies
accounts, authenticated API, many-to-many links, and monthly package flow.
reconciliation outputs, and rollback evidence are private operator artifacts
and must remain in ignored
.tmp/or the separate private knowledge boundary.top-level non-PDF images, and two top-level nested packaging archives. Eight
top-level PDF copies are content duplicates, leaving 437 unique top-level PDFs.
The nested packaging archives contain 28 PDFs already byte-identical to
top-level PDFs; those archives are explicitly excluded and never
extracted/imported. Dry-run and post-write reconciliation must account for
these aggregate inclusion, deduplication, and exclusion counts exactly.
no file permissions. This migration uses portal/operator authentication for
document APIs; it must not broaden the webhook credential into document read,
download, delete, report, or admin access.