# Guida niche-jobs example v1 Purpose: adapt the synthetic extraction exercise. All vacancies are invented. Example runs: browser or Node.js 24. Studio target: Enterprise 0.1.0-alpha.1 on a prepared installation. Keep collect({ input }) and the { items, evidence } envelope. Validate items against output.schema.json. Use the existing draft runner; do not assume Node.js host APIs exist in Studio. No model or network call is needed for this example. Do not add credentials to shared code. Missing source fields must cause a visible error, rather than an empty successful result. The rule is an illustrative English sentence match, not a general relevance classifier. Guide: https://guida-app.dev/docs/enterprise/niche-jobs/ ## README.md # Find a relevant role - example v1 Turn four synthetic vacancies into a record you can inspect and reuse. This exercise illustrates the niche-job discovery problem behind Beyond Tabs. The companies and vacancies are invented; these are not live job listings. ## Run locally Install Node.js 24, extract the ZIP and open a terminal in this directory. No npm install, Guida installation, account, API key or network access is needed to run the files after download. node run.mjs Expect job-001, Compiler engineer, and its supporting sentence. Compare the result with expected-result.json. The optional Haskell mention in job-002 is not selected by the rule. All four inputs are inspected. ## Break and repair node run.mjs --changed Expect a nonzero exit and: Job 1: missing or invalid "description". Check the feed and textField. The second feed renamed description to details. node run.mjs --changed --repair Expect the original successful result. The repair selects the actual field; it does not hide missing input by returning an empty successful result. ## Make it yours node run.mjs --language=Elixir node run.mjs --language=F# Edit collect.mjs or the fixture inputs and rerun. The small sentence rule is deliberately limited to these English examples. It is not a general job relevance classifier; real sources need reviewed extraction logic and editorial judgement. ## Use the module in Studio Guida Enterprise 0.1.0-alpha.1 uses a separate configured Windows draft runner. On a prepared installation, create a disposable API / HTTP custom source with id niche-jobs-example. Keep its generated lib/niche-jobs-example.js path and collect export. Copy collect.mjs into that file, and replace the generated input/output schemas with the supplied schemas. Keep guida.extractorResult.v1. Paste input.json as the test input, then Save and run draft. Output schema validation applies to result.items. The module makes no HTTP or model calls. Repeat with changed-input.json, then change its textField to details and rerun. The example's website and Node.js runs do not establish a live Enterprise runner connection. The Studio guide explains installation and runner requirements. Tutorial: https://guida-app.dev/docs/enterprise/niche-jobs/ Walkthrough: https://guida-app.dev/try/niche-jobs/ ## Reuse These example files are MIT licensed; see LICENSE.txt. Guida Enterprise's proprietary software license is separate and unchanged. ## collect.mjs // Niche jobs exercise v1. MIT licensed; see LICENSE.txt. // Pure JavaScript: the website, Node.js and a Studio source use this same module. const languages = ['Haskell', 'Elixir', 'F#']; export function inspect(input) { const { jobs, language = 'Haskell', textField = 'description' } = input ?? {}; if (!languages.includes(language)) throw new Error('Choose Haskell, Elixir or F#.'); if (!['description', 'details'].includes(textField)) throw new Error('Choose description or details.'); if (!Array.isArray(jobs) || jobs.length < 1 || jobs.length > 25) { throw new Error('Supply between 1 and 25 jobs.'); } const ids = new Set(); return jobs.map((job, index) => { for (const field of ['id', 'title', 'company', 'location', textField]) { if (typeof job?.[field] !== 'string' || !job[field].trim() || job[field].length > 2000) { throw new Error(`Job ${index + 1}: missing or invalid "${field}". Check the feed and textField.`); } } if (ids.has(job.id)) throw new Error(`Job ${index + 1}: duplicate id.`); ids.add(job.id); const sentences = job[textField].split(/(?<=[.!?])\s+/); // An intentionally small, inspectable rule for these English fixtures. // Optional mentions alone do not establish that a role uses the language. const escaped = language === 'F#' ? 'F#' : language; const mention = new RegExp(`\\b${escaped}(?=\\s|[.,;:!?)]|$)`, 'i'); const sentence = sentences.find((line) => mention.test(line) && /\b(build|develop|maintain|required)\b/i.test(line) && !/\b(optional|nice to have|not required)\b/i.test(line)); return { id: job.id, title: job.title.trim(), company: job.company.trim(), location: job.location.trim(), language, selected: Boolean(sentence), reason: sentence?.trim() ?? 'No explicit core-work requirement for this language.' }; }); } export async function collect({ input }) { const decisions = inspect(input); const items = decisions.filter((job) => job.selected).map(({ selected, ...record }) => record); return { items, evidence: { resultCount: items.length, inspectedCount: decisions.length } }; } ## input.json { "language": "Haskell", "textField": "description", "jobs": [ { "id": "job-001", "title": "Compiler engineer", "company": "Example Compiler Co.", "location": "Remote, Europe", "description": "Build our compiler and analysis tools in Haskell. Work with a small language engineering team." }, { "id": "job-002", "title": "Frontend engineer", "company": "Example Interface Co.", "location": "London", "description": "Build product interfaces in TypeScript. Haskell experience is optional." }, { "id": "job-003", "title": "Distributed systems engineer", "company": "Example Messaging Co.", "location": "Remote", "description": "Develop messaging services in Elixir. Help maintain reliable delivery between services." }, { "id": "job-004", "title": "Risk tooling developer", "company": "Example Analytics Co.", "location": "Warsaw", "description": "Maintain our calculation tools in F#. Work alongside Python and C# developers." } ] } ## input.schema.json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["jobs"], "properties": { "url": { "type": "string" }, "language": { "enum": ["Haskell", "Elixir", "F#"] }, "textField": { "enum": ["description", "details"] }, "jobs": { "type": "array", "minItems": 1, "maxItems": 25, "items": { "type": "object", "required": ["id", "title", "company", "location"], "properties": { "id": { "type": "string", "minLength": 1, "maxLength": 2000 }, "title": { "type": "string", "minLength": 1, "maxLength": 2000 }, "company": { "type": "string", "minLength": 1, "maxLength": 2000 }, "location": { "type": "string", "minLength": 1, "maxLength": 2000 }, "description": { "type": "string", "minLength": 1, "maxLength": 2000 }, "details": { "type": "string", "minLength": 1, "maxLength": 2000 } }, "anyOf": [{ "required": ["description"] }, { "required": ["details"] }], "additionalProperties": false } } }, "additionalProperties": false } ## output.schema.json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "array", "maxItems": 25, "items": { "type": "object", "required": ["id", "title", "company", "location", "language", "reason"], "properties": { "id": { "type": "string" }, "title": { "type": "string" }, "company": { "type": "string" }, "location": { "type": "string" }, "language": { "enum": ["Haskell", "Elixir", "F#"] }, "reason": { "type": "string" } }, "additionalProperties": false } } ## expected-result.json { "items": [{ "id": "job-001", "title": "Compiler engineer", "company": "Example Compiler Co.", "location": "Remote, Europe", "language": "Haskell", "reason": "Build our compiler and analysis tools in Haskell." }], "evidence": { "resultCount": 1, "inspectedCount": 4 } }