Skip to content

Develop - #227

Open
alex-struk wants to merge 193 commits into
mainfrom
develop
Open

Develop#227
alex-struk wants to merge 193 commits into
mainfrom
develop

Conversation

@alex-struk

Copy link
Copy Markdown
Collaborator

Summary

AI-###

Changes

Testing

Checklist

By submitting this pull request, I acknowledge that I have attempted to meet the following:

  • a self-review of my code
  • commented code particularly in hard-to-understand areas
  • corresponding changes to the documentation where required
  • changes tested to the best of my ability
  • no new errors or non-functional code

kmandryk and others added 30 commits June 17, 2026 14:26
Introduce a repo-focused wiki under docs-md/wiki with topic pages (index, sources, log, system overview, graph-workflows, HITL, auth-and-groups, deployment-and-ops, open-questions) and guidelines (README). Add scripts/scripts: build-docs-wiki.js and validate-wiki.js plus package.json updates to expose the docs:wiki:check script. Add a GitHub Actions workflow (.github/workflows/wiki-check.yml) to run npm run docs:wiki:check on PRs. Integrate generated wiki into the site: update docs build (docs/build.sh) to run the wiki renderer, add nav link and page entries in docs/* HTML, and include generated wiki HTML files (docs/wiki*.html). Also update CLAUDE.md with wiki usage rules. This enables validation, generation, and navigation of the repo wiki from CI and the static docs site.
Add a GitHub Pages workflow and tighten wiki CI checks; introduce AGENTS.md and ARCHIVE.md to canonize repo wiki rules and archival guidance. Rework many docs and READMEs to rename/replace labeling/training with template-models, document graph-workflow and workflow-builder changes, and surface group-scoped API key behavior. Strengthen contributor/copilot guidance (avoid leaking secrets, curl usage with env API_KEY) and update CLAUDE.md accordingly. Ignore generated wiki HTML in .gitignore, remove stale generated wiki HTML files, and update docs build/validation scripts and package.json to support the new pages/wiki workflows.
The canonical Workflow Builder Guide lives under docs-md/workflow-builder/.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace haproxy.router.openshift.io/ip_whitelist with haproxy.router.openshift.io/ip_allowlist in base Route manifests (frontend & backend) and update overlays. Renamed patch files route-whitelist-patch.yml -> route-allowlist-patch.yml and updated kustomization.yml references in prod and test overlays, plus adjusted the JSON patch paths. Updated documentation (KUSTOMIZE_INSTANCE_TEMPLATE.md) to reflect the new annotation name. This aligns the annotation naming from "whitelist" to "allowlist".
Sets up the parent branch (feature/extraction-experiments) for a 5-engine
extraction comparison: neural DI + post-processing, Mistral Doc AI on Azure,
Azure Content Understanding, VLM-direct, and VLM+OCR hybrid. Defines parent-branch
scaffolding (brief library, deployment-selection plumbing, local-folder dataset
seed convention) and per-experiment branch deliverables. Stacks on PR #134's
neural training capability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ting-engine audit, dev-loop, future candidates

Updates the extraction-experiments design spec per user feedback:
- Replaces the AI-feedback 12-item checklist with one derived from this
  codebase (file paths, types, conventions actually present here).
- Documents the dev loop as "implement → run on real document → run benchmark
  via API → write mock tests when stable", not an accuracy-threshold gate.
- Adds existing-engine audit (Azure DI + Mistral) against the new checklist
  to the provider-architecture doc on the parent branch.
- Adds vendor-doc citations confirming Mistral and CU use OCR-first → LLM
  annotation, which E05 recreates explicitly.
- Adds "Candidates for future rounds" section listing trends not in the
  core 5 experiments (confidence-based routing, per-field calibration,
  multi-engine voting, agentic correction, open-source VLMs, etc.) so
  they can be re-pitched after E01–E05 land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… scaffolding

Removes:
- Duplicate "Cross-engine audit" section (already in deliverable #3)
- "Expected gaps to surface" speculation
- "Open questions / assumptions" section (was for pre-approval review)
- Goals section (overlaps Context + deliverables)
- Brief-list file-content descriptions
- Experiment-branch deliverables list (overlaps the integration checklist)
- "Why deferred" column from future-candidates table

Compresses:
- Three Azure resource tables into one
- Per-experiment sections to elevator-pitch length
- Sequence of work (drops "commit spec" step which is done)
- TODO-callout section moved into the E01 brief description

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gpt-5.5 GlobalStandard quota is 0 in this subscription. gpt-5 (vanilla,
2025-08-07) has 1000 TPM quota in westus, on the same account as gpt-4o,
so deployment succeeded immediately and the plumbing stays simple
(single endpoint, deployment-name as the only switch).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ure audit

Parent-branch deliverables for the extraction-experiments suite:

- experiments/briefs/_shared-rules.md — codebase-derived 12-item engine-integration
  checklist and dev loop discipline (no accuracy threshold gating; "implement →
  run on real document → benchmark via API → mock-replay tests once stable")
- experiments/briefs/01..05-*.md — per-experiment briefs for neural DI, Mistral
  on Azure, Content Understanding, VLM-direct, VLM+OCR hybrid
- docs-md/EXTRACTION_EXPERIMENTS.md — hub doc with per-experiment status table
  and engine-integration checklist scaffolds
- docs-md/EXTRACTION_PROVIDER_ARCHITECTURE.md — current state + decision to defer
  formal OcrProvider interface + audit of existing Azure DI and Mistral providers
  against the 12-item checklist (gaps captured as scoped TODOs in the affected
  experiment briefs)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…convention

- Append new env vars to apps/{backend-services,temporal}/.env.sample for the
  experiments not yet wired (E02 Mistral on Foundry, E03 Content Understanding,
  E04/E05 selectable Azure OpenAI deployments). User redirects values to their
  personal Azure via the existing override file at
  ~/.config/bcgov-di/{backend-services,temporal}.env (Claude does not read it).
- AZURE_OPENAI_DEPLOYMENT remains the default fallback for backward compatibility;
  AZURE_OPENAI_DEPLOYMENTS is the new comma-separated allow-list for per-node
  selection.
- Add data/datasets/ as a tracked path with .gitkeep; gitignore
  data/datasets/*/private/** so users can drop personal/test docs without
  committing them. Convention documented in the design spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-folder dataset seed

Two pieces of parent-branch scaffolding that all 5 experiments depend on:

1. Per-workflow-node Azure OpenAI deployment selection
   - apps/temporal/src/activities/enrich-results.ts: read params.azureOpenAiDeployment
     with fall-through to AZURE_OPENAI_DEPLOYMENT env var.
   - apps/backend-services/src/azure/azure-openai.controller.ts: GET /api/azure-openai/deployments
     parses AZURE_OPENAI_DEPLOYMENTS (comma-separated). Mirrors GET /api/models.
     Falls back to AZURE_OPENAI_DEPLOYMENT singleton, then empty array.
   - DTO + Swagger decorators, registered in AzureModule.
   - Tests: 7 controller cases + 2 enrich-results override cases.

2. Local-folder dataset seed extension
   - apps/backend-services/src/seed/local-datasets.ts: pure parser scanning
     data/datasets/<name>/{public,private}/manifest.json. Validates JSON +
     samples array; warns and skips on errors.
   - apps/shared/prisma/seed.ts: new seedLocalDatasets() called after the
     existing benchmark-data seed. Idempotent upsert of Dataset + DatasetVersion
     rows. Skips quietly when data/datasets/ is empty.
   - Test: 10 cases covering parser edge cases + id-helper coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the local-folder dataset seed to handle the natural drop pattern of
<name>.<image-ext> + <name>.json pairs without a manifest.json. When no
manifest exists, scan for pairs and auto-derive one, writing it to disk with
metadata.autoGenerated = true so subsequent re-seeds refresh it. User-curated
manifests (no auto-gen marker) are never touched.

Validated end-to-end on a 33-sample drop (samples-mix/private).

- New deriveManifestFromFlatPairs(folderPath, folder, log) — pure helper.
- Accepts: jpg/jpeg/png/pdf/tif/tiff/webp/bmp/gif as document extensions.
- Warns about unpaired documents (missing JSON) and unpaired ground-truth
  (missing document).
- 7 new tests covering flat-pair, regen-on-rerun, user-curated takes precedence,
  unpaired warnings, empty folders, mixed-case extensions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the quota-request flow + the deploy command + a fallback (run E04
with gpt-4o and gpt-5 first if approval is delayed). Also notes that gpt-5.5
will live in eastus2 (different endpoint than the existing westus account),
so the workflow node may need azureOpenAiEndpoint/Key params threaded — current
parent-branch plumbing only threads deployment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ev only)

The seed creates Dataset/DatasetVersion DB records, but the existing benchmark
infrastructure (Sample Preview, BenchmarkRun execution) pulls files from blob
storage. Without uploading the actual files, dropped local datasets show up
in the DB but not in the UI and aren't usable for benchmarks.

Adds LocalDatasetSyncService that runs at backend startup (NestJS
onApplicationBootstrap) and uploads each local dataset's files to blob storage
following the existing convention used by dataset.service.ts uploadFiles:
  groups/{groupId}/benchmark/datasets/{datasetId}/{versionId}/inputs/{file}
  groups/{groupId}/benchmark/datasets/{datasetId}/{versionId}/ground-truth/{file}
  groups/{groupId}/benchmark/datasets/{datasetId}/{versionId}/dataset-manifest.json

The blob dataset-manifest.json follows the schemaVersion=1.0 shape the existing
runtime expects (with `inputs/` / `ground-truth/` path prefixes baked in).

- Idempotent: skips files already in blob storage; always rewrites the manifest
  (cheap, lets it pick up local manifest edits between runs).
- Updates DatasetVersion.storagePrefix and manifestPath to the blob paths so
  the runtime finds the synced files.
- Gated to NODE_ENV !== production; can be turned off via
  SYNC_LOCAL_DATASETS_ON_START=false.
- Pure helper syncLocalDatasetToBlobStorage exported for unit testing — 4 cases
  covering first-sync, idempotent re-sync, partial-resync after files added,
  and blob-key convention check.

Also updates seedLocalDatasets to write the eventual blob-convention paths
into the DB records (storagePath, storagePrefix, manifestPath) so the records
are correct from the moment the seed completes, even before the sync service
runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tself

PrismaService is a wrapper that exposes the actual PrismaClient via
`prismaService.prisma` (see apps/backend-services/src/database/prisma.service.ts).
The sync service was casting the wrapper directly to DatasetVersionUpdater,
so this.prisma.datasetVersion was undefined and the storagePrefix update
threw at runtime.

The unit tests passed because they bypass NestJS DI and call the pure
syncLocalDatasetToBlobStorage helper with a hand-rolled stub already shaped
like a PrismaClient.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… land

Closes two readiness gaps so the user has zero hand-holding once experiment
implementation completes:

1. Seed scaffolding for the benchmark suite, on the parent branch:
   - Adds BenchmarkProject id 'seed-experiments-project' ("Extraction
     Experiments") that all 5 experiment branches add their BenchmarkDefinition
     rows to.
   - Adds a Split per local dataset version with id pattern
     'seed-local-{folder}-{visibility}-split', type=test, populated with all
     sample IDs from the (auto-generated or curated) manifest. Verified: 33
     sample IDs land on the split for the user's samples-mix/private drop.

2. Codifies the per-experiment seeding requirement in _shared-rules.md (item 11
   Benchmark integration) — each experiment must seed: a WorkflowLineage +
   WorkflowVersion, plus a BenchmarkDefinition with the deterministic id
   'seed-experiment-{slug}-definition' referencing the parent-seeded project
   and split. Once landed, the definition is invokable via the standard API
   endpoint POST /api/benchmark/projects/.../definitions/.../runs.

3. Adds scripts/run-experiment-benchmarks.sh — triggers all 5 experiment runs
   (or a subset by leading number) using TEST_API_KEY from the override file.
   Tags runs with experiment-{slug} for cross-experiment comparison.

4. Updates docs-md/EXTRACTION_EXPERIMENTS.md with a "Run all benchmarks"
   section and the on-demand curl pattern.

After E01-E05 land and seed runs:
  ./scripts/run-experiment-benchmarks.sh

…fires all 5 runs against the user's seeded dataset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ll dataset

Two refinements after auditing the existing workflow conventions:

1. Drop the Split seeding for local datasets. BenchmarkDefinition.splitId is
   optional in the Prisma schema, and benchmarks should run on the full
   user-dropped dataset (33 samples), not a subset. Each experiment's
   BenchmarkDefinition will reference the full DatasetVersion directly via
   datasetVersionId.

2. Align _shared-rules.md with the existing OCR-provider conventions in the
   codebase, which the prior version partially missed:

   - Add "Required reading" pointing at ADDING_OCR_PROVIDERS.md,
     ADDING_GRAPH_NODES_AND_ACTIVITIES.md, DAG_WORKFLOW_ENGINE.md,
     GRAPH_TYPES.md, and the templates folder. These are authoritative.
   - Update the "files you may edit" list to call out all THREE activity
     registries (apps/temporal/src/activity-registry.ts,
     apps/temporal/src/activity-types.ts, AND
     apps/backend-services/src/workflow/activity-registry.ts) — missing any
     one breaks workflow validation or worker resolution.
   - Update item 2 (activity registration): three registries, naming
     conventions <provider>Ocr.process or <provider>Ocr.{submit,poll,extract}.
   - Update item 8 (workflow graph): templates live at
     docs-md/graph-workflows/templates/<slug>-workflow.json, validated by
     graph-schema-validator.test.ts. Standard node sequence per the docs.
     Provider-specific options via initialCtx, not new global env vars.
   - Update item 11 (benchmark integration): no Split — reference full
     DatasetVersion directly. Pointer to seedLineageVersion pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Seed.ts now scans docs-md/graph-workflows/templates/ for files matching
'experiment-*-workflow.json'. For each file found, idempotently creates:

  - WorkflowLineage  seed-experiment-{slug}-workflow
  - WorkflowVersion  wv_seed-experiment-{slug}-workflow  (full JSON config)
  - BenchmarkDefinition  seed-experiment-{slug}-definition  in
    seed-experiments-project, targeting the (single) local DatasetVersion

Slug derived from filename: experiment-04-vlm-direct-workflow.json → "04-vlm-direct".

If multiple local datasets exist, workflows can target a specific one via
metadata.targetLocalDataset = "{folder}-{visibility}". Defaults to first.

Verified end-to-end with a smoke-test template (experiment-99-smoke-workflow.json):
seed picked it up, created lineage/version/definition targeting
seed-local-samples-mix-private-v1. Removed file after — clean state.

Implication for experiment AIs: drop a workflow JSON, get the full benchmark
plumbing for free. No edits to seed.ts per experiment.

Updates the briefs accordingly:
  - _shared-rules.md item 11: explains the auto-discovery contract.
  - E01: copy standard-ocr-workflow-with-corrections.json (already wires
    cleanup/normalize/character-confusion/spellcheck), change modelId default
    to neural model id, optionally add ocr.enrich.
  - E02: copy mistral-standard-ocr-workflow.json, swap mistralOcr.process →
    mistralAzureOcr.process.
  - E03: base on standard-ocr-workflow.json (async pattern: submit/pollUntil/
    extract), replace Azure DI nodes with azureContentUnderstanding.deployAnalyzer
    + azureContentUnderstanding.analyze.
  - E04: base on mistral-standard-ocr-workflow.json (sync), insert
    pdf.renderToImages + vlmDirect.extract.
  - E05: base on standard-ocr-workflow.json, replace extraction with
    pdf.renderToImages + azureOcr.readPlain + vlmOcrHybrid.extract.

Each variant in E04/E05 can have its own JSON file (one per variant) — each
gets its own BenchmarkDefinition automatically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… report

Three changes so the user has a populated final-tip branch ready to run all
benchmarks and produce a cross-experiment report:

1. Chained branches (was hub-and-spoke).
   - E01 branches from feature/extraction-experiments
   - E02 branches from E01
   - E03 branches from E02
   - E04 branches from E03
   - E05 branches from E04
   By the time E05 lands, its tip contains every workflow JSON, every
   provider folder, and every seed change from E01-E05. Updates the spec
   diagram, hub doc, _shared-rules.md, and each per-experiment brief's
   "Branch" line to reflect this. Also enumerates the exact `git checkout -b`
   commands per experiment in the hub doc.

2. E01 brief simplification (per user).
   - modelId default = "test" (the existing trained neural model — no
     training step needed in this experiment).
   - Drops spellcheck node (`ocr.spellcheck`) from the workflow.
   - Drops LLM enrichment (`ocr.enrich`) — out of scope for E01.
   - Drops cross-field validation (`document-validate-fields`) — out of scope.
   - Final node sequence: file.prepare → azureOcr.submit/poll/extract →
     ocr.cleanup → ocr.normalizeFields → ocr.characterConfusion →
     ocr.checkConfidence → reviewSwitch → humanReview/storeResults.
   - Adds an explicit "Out of scope" section so future readers don't add
     these activities back unintentionally.

3. scripts/compare-experiment-benchmarks.sh.
   - Lists runs in seed-experiments-project, filters by tag pattern
     experiment-*, picks the most recent COMPLETED run per tag.
   - Downloads each via the existing GET /runs/:runId/download endpoint to
     /tmp/extraction-experiments-YYYYMMDD-HHMMSS/<slug>/run.json.
   - Writes COMPARISON.md — a markdown table with status, field/character/
     word accuracy, duration, cost, run id per experiment.
   - Intentionally simple ("start with something simple" per user); easy to
     extend with per-field-class metrics, P95 latency, etc.
   - Hub doc updated with the full two-command flow:
       ./scripts/run-experiment-benchmarks.sh
       (wait for runs)
       ./scripts/compare-experiment-benchmarks.sh

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three references in the brief updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ult)

The existing standard-ocr-workflow-with-corrections.json template already
sets ctx.modelId.defaultValue to sdpr_synth_test, so the experiment AI just
copies the template and leaves modelId untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ob sync)

Add docs-md/local-datasets-seeding.md covering the
data/datasets/<name>/{public,private}/ convention, manifest-driven and
flat-pair layouts, seedLocalDatasets() DB seeding, and the dev-only
LocalDatasetSyncService blob upload (SYNC_LOCAL_DATASETS_ON_START /
FORCE_RESYNC_LOCAL_DATASETS).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drops in `docs-md/graph-workflows/templates/experiment-01-neural-doc-intelligence-workflow.json`,
auto-discovered by `seedExperimentWorkflows()` and benched against the user's
`seed-local-samples-mix-private-v1` dataset (33 samples) using the trained
neural model `sdpr_synth_test`.

Chain: file.prepare → azureOcr.submit/poll/extract → ocr.cleanup →
ocr.normalizeFields → ocr.characterConfusion → ocr.checkConfidence →
reviewSwitch → (humanReview)? → ocr.storeResults. Spellcheck dropped per the
brief; LLM enrichment and cross-field validation out of scope.

Real-API benchmark run id 2295feed-1c99-493e-ae20-546499b5d685: pass_rate
0.515, F1 median 0.806, precision 0.90, recall 0.59 over 33 samples in ~83s.
Workflow chain executed end-to-end on every sample.

Tests: 17 static + structural assertions on the template and recorded neural
OCR fixture (apps/temporal/src/experiment-01-neural-doc-intelligence.test.ts).
TestWorkflowEnvironment runtime tests skipped — same temporal.download TLS
blocker as the existing graph-workflow.test.ts; the live benchmark is the
runtime integration coverage.

Parent-shared infra fixes (user-authorized to keep seed-driven and
manual-create dataset paths consistent):
  * local-dataset-sync.service.ts: store relative `dataset-manifest.json`
    instead of full blob key, matching dataset.service.ts createVersion.
  * seed.ts seedLocalDatasets: same relative manifestPath.
  * seed.ts seedExperimentWorkflows: switch evaluatorType from the
    unregistered `field-accuracy` to `schema-aware`, with a fuzzy default
    rule and 0.8 pass threshold.

Outstanding parent gap (NOT fixed here, flagged in SUMMARY):
  scripts/run-experiment-benchmarks.sh sends `tags` as an array but
  CreateRunDto requires an object — one-line fix in the script.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mark export

Two unrelated fixes bundled because both surfaced while finishing E01:

1. ts-node-dev hot reload was stopping the worker without respawning.
   Root causes:
     a. The Rust-backed `NativeConnection` was never explicitly closed,
        so its tokio runtime kept the Node event loop alive after
        `worker.run()` resolved. ts-node-dev's `--respawn` waits for the
        child to exit before starting a new one, so the new instance
        never came up. Fixed by `await connection.close()` and an
        explicit `process.exit(0)` after the workers stop.
     b. `--watch src` told chokidar to watch every file under src/
        including JSON fixtures (e.g. the 290 KB neural-OCR poll fixture).
        Touching a fixture forced a worker drain. ts-node-dev's
        `--ignore-watch` only filters require-loaded files, not
        explicitly watched paths, so the only practical fix is to
        narrow the watch glob — switched to `'src/**/*.ts'` so JSON
        fixture edits no longer trigger a reload.

   Verified live:
     - touch src/__fixtures__/.../neural-ocr-response-1-81.json → no restart
     - touch src/logger.ts → drain → "Worker stopped" → ~600ms later
       "Worker initializing" → RUNNING

2. Archive the full benchmark export for run
   `2295feed-1c99-493e-ae20-546499b5d685` (the canonical E01 result) at
   `experiments/results/01-neural-doc-intelligence/benchmark-run.json`.
   Includes per-sample ground truth + prediction + evaluation details
   + per-field results + error-detection analysis. Pulled from
   `GET /api/benchmark/projects/.../runs/{runId}/download`. Set up so
   each experiment in the stack can drop its `benchmark-run.json`
   alongside `SUMMARY.md` and the cross-experiment comparison can read
   them locally without re-querying the backend.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three things, all driven by E01 retrospective:

1. Workflow-level runtime tests against the local dev-stack Temporal
   (`localhost:7233`). Each test loads the actual JSON template, runs the
   real `graphWorkflow` workflow, and replays the recorded neural-OCR
   fixture through mocked activities. Covers both `reviewSwitch` branches
   — high-confidence skips humanReview; low-confidence routes through
   humanReview, gets a `humanApproval` signal, then completes via
   `storeResults`. Both tests patch `pollOcrResults` interval/initialDelay
   to `1ms` so they finish in ~2s each. 19/19 pass in ~7s. Sidesteps
   `TestWorkflowEnvironment.createTimeSkipping()` which TLS-fails on the
   binary download; documented as the canonical pattern.

2. `scripts/run-experiment-benchmarks.sh`: send `tags` as an object (not
   an array) per the `CreateRunDto` `@IsObject()` validation, and pass
   `persistOcrCache: true` by default so each run captures engine
   responses for the test fixture.

3. `experiments/briefs/_shared-rules.md` overhaul, incorporating E01
   retrospective:
     * **No upstream backporting** — fix shared infra bugs in your
       experiment branch and let the chained stack carry them upstream.
       Replaces the old "stop and raise it back" guidance.
     * **Authentication** subsection — how to get and use TEST_API_KEY,
       the relevant API endpoints, that the seed-time key is dev-only.
     * **Common bugs runbook** — the five footguns E01 hit (script tags
       shape, manifestPath value, evaluator name, confidence-threshold
       recalibration, hot-reload watcher).
     * **Dev loop** rewritten — benchmark-with-persistOcrCache IS the
       real-API run, fixture-capture is now an explicit step, full
       benchmark export is a deliverable at
       `experiments/results/<slug>/benchmark-run.json`.
     * **Test layout** — pin per-experiment tests at
       `apps/temporal/src/experiment-<slug>.test.ts` with fixtures at
       `apps/temporal/src/__fixtures__/experiment-<slug>/`.
     * **`metadata.targetLocalDataset` is now required**, not just an
       override, so benchmark-target choice is deterministic.
     * **Done criteria** — `benchmark-run.json` saved as a deliverable;
       runtime tests required, not just static.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI's temporal-qa.yml runs `npm test` without a Temporal sidecar, so the
runtime layer fails to connect to localhost:7233. Gate the runtime
`describe` block on `process.env.CI` (set automatically by GitHub Actions).
Static suite continues to run on every CI build.

Verified:
  - local: 19 passed
  - CI=true: 17 passed, 2 skipped

Pattern documented in `_shared-rules.md` so subsequent experiments use the
same gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TODO asked E01 to verify Azure DI activities don't have APIM-specific
path manipulation that would break under direct access. E01 ran the chain
end-to-end against direct Azure DI without issue, so the concern is
empirically resolved — no path-prefix surgery was needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates from the E01 retrospective + open audit items:

- Task 2: clarify it's a single HTTP call (no two-call orchestration in our
  code). Mistral's API handles the OCR-then-annotation chain server-side
  via `document_annotation_format` + `document_annotation_prompt` in the
  body, mirroring how `mistral-ocr-process.ts` already works. (Was an
  ambiguity that surfaced in the handoff review.)
- Task 3: explicit list of all THREE activity registries + activities.ts
  export, so the next session doesn't miss one and ship a worker that
  fails save-time validation.
- Task 5: require `metadata.targetLocalDataset = "samples-mix-private"`
  per the updated _shared-rules.md (was "override if needed", now required
  for deterministic dataset binding).
- New Task 6: capture the cross-engine audit Item 5 ⚠️ (Mistral mapper
  sets `polygon: []`). Populate per-word polygons from Mistral's bbox
  data so E05's VLM+OCR hybrid has spatial info. Update existing mapper
  test. Benefits both public-API and Foundry paths since they share the
  mapper.
- New Task 7: capture the cross-engine audit Item 9 ⚠️ (Mistral internal
  preprocessing not documented). Resolve from Mistral docs and capture
  in the per-engine OCR doc; same doc covers Foundry-vs-public diffs.
- Task 8: align with the post-E01 trigger (`./scripts/run-experiment-benchmarks.sh
  02`, which now sends `tags` as object and `persistOcrCache: true` by
  default). Tag is now `{"experiment":"02-mistral-doc-ai-azure"}` (full
  slug) — supersedes the old `experiment-02-mistral-azure` short form.
- New Tasks 9, 10: explicit deliverables — `benchmark-run.json` saved
  from the download endpoint; one OCR fixture captured to
  `__fixtures__/experiment-02/` for the test replay.
- Task 11: replaces the old "Mock-based tests" line with the canonical
  two-layer pattern (static + runtime) and the `process.env.CI` gate
  for the runtime suite, pointing at `experiment-01-neural-doc-intelligence.test.ts`
  as the reference implementation.
- Task 12: SUMMARY.md fields explicit — deployment id, run id, headline
  metrics, comparison row vs public-API, confidence-distribution
  observations, infra-fix log, bbox-fix verification.
- Cross-engine audit follow-through section: list both ⚠️ items
  (5 and 9) explicitly with the tasks that address them.
- Header pointer to `_shared-rules.md` so the canonical patterns and
  "fix forward" rule are surfaced up front.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dbarkowsky and others added 5 commits July 13, 2026 11:00
Update both PostgresCluster base manifests to expire pgBackRest full backups after 14 days, and document the new retention policy in the environment and instance template docs.
AI-1479 Header Spacing Fix
alex-struk and others added 2 commits July 20, 2026 09:23
Resolve conflicts:
- .gitignore: union of both sides' ignore rules
- apps/temporal/package.json: keep PR-branch config; drop dead
  build:graph-workflow-config script (package removed on develop)
- docs-md/workflows/: adopt develop's graph-workflows -> workflows rename
  for the experiment OCR docs + templates added on this branch
Extraction experiments — cumulative merge (E01–E08 + improvements + review fixes + harness)
if isinstance(v, bool):
return None # bool is int in Python; we don't treat True/False as numbers
if isinstance(v, (int, float)):
if v != v or v in (float("inf"), float("-inf")): # NaN / Inf
# Use the full A+B-aware locator so verdicts mirror production behavior.
located_full = locate_table(tables, analyze_result, cfg)
located = (located_full[0], located_full[1]) if located_full else None
strategy_label = located_full[4] if located_full else "not-found"
"""

import json
import os
import csv
import json
import os
import shutil
# Build a synthesised benchmark-run.json so the comparison report can
# include E06 alongside the others.
per_sample = strategy_per_sample[best_name]
eval_blob = []
rows = sweep_thresholds(preds, fine_thresholds, e.docs_count)
xs = [r["non_blank_flagged_per_100_docs"] for r in rows]
ys = [r["recall"] * 100 for r in rows]
ceiling = rows[-1]["catchable_errors"] / rows[-1]["errors_total"] * 100 if rows[-1]["errors_total"] else 100
if located is None:
continue
table, table_index, column_map, row_map, strategy = located
rule_label = STRATEGY_RULES[strategy]

# ---------- Estimate the no-fuzzy accuracy ----------
# Strict accuracies (from V2 report Appendix 11.2, neural V2 strict):
strict_acc = {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants