Offline contract tools

Validate and project job data with Python

Use source-specific, dependency-free scripts to reject malformed canonical rows, validate completion summaries, produce compact display records, or derive the shared table projection.

Python standard libraryJSON and JSONLSource-specific validation

Choose the source validator first

LinkedIn and EURAXESS share the six-root nomad-agent-job-v1 envelope, but their source identity, custom extension, fields, and availability rules differ. Run the validator bundled with the matching Agent Skill instead of a generic “has title and URL” check.

  • validate_contract.py checks the closed canonical row and source-specific contract.
  • validate_run_summary.py checks the public v4 completion record.
  • parse_output.py produces a compact, human-readable representation without replacing the source record.
  • flatten_output.py creates nomad-agent-flat-job-v1 only for a table destination.

Validate and flatten EURAXESS output

python3 .agents/skills/euraxess-enrich-translate-normalize-scraper/scripts/validate_contract.py actor-output.json

python3 .agents/skills/euraxess-enrich-translate-normalize-scraper/scripts/flatten_output.py \
  actor-output.json --output flat-output.json

Use the LinkedIn skill path for LinkedIn rows. The scripts accept JSON or JSONL; flattening can optionally include a bounded serialized canonical record, but the separate original dataset remains the source of truth.

Integrate the gate into ETL

  1. Fetch a terminal exact Actor run and its RUN-SUMMARY.
  2. Validate the summary before the dataset, including the one-retry boundary for scraper v4.
  3. Fetch and paginate the exact default dataset and reconcile the complete count.
  4. Run the source-specific contract validator.
  5. Write the canonical records to durable storage if downstream evidence matters.
  6. Only then generate flat rows and upsert them by jobKey.

The Python scripts do not prove build identity or retrieve Apify state; the caller owns those checks.

Fit scoring uses a different adapter

integrations/shared/ai_job_fit_adapter.py validates the closed nomad-ai-job-fit-v1 evaluation and derives nomad-ai-job-fit-destination-v1. It omits ai_failed rows and preserves candidate-safe matchKey.

Do not pass fit evaluations to the normalized-job flat mapper. A fit row includes a complete nested canonical job, but it is a separate candidate-specific result.

Projection is explicit information loss

The flat job view emits primitive fields suitable for Airtable, Sheets, CSV, and relational staging. It cannot preserve every nested requirement, contact, custom source field, raw fact, or provenance detail. It serializes arrays so unknown null remains different from explicitly empty [].

Troubleshooting

  • Root keys mismatch: verify the exact Actor/build and that the input file contains dataset items rather than an API wrapper.
  • Source mismatch: use the skill matching identity.source; do not weaken the validator.
  • Missing jobKey: the row needs source plus an external ID or fallback URL before flattening.
  • Encoding or JSONL error: validate that each JSONL line is one complete object and retain UTF-8.