{
  "id": "rcPublicSignal20260811",
  "name": "Validate a public n8n job feed with bounded retries and schema guards",
  "active": false,
  "nodes": [
    {
      "parameters": {},
      "id": "manual-trigger",
      "name": "Run public signal check",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [0, 160]
    },
    {
      "parameters": {
        "jsCode": "return [{ json: { sourceUrl: 'https://community.n8n.io/c/jobs/13.json', allowedHost: 'community.n8n.io', requestMethod: 'GET', maxAttempts: 2, timeoutMs: 10000, maxTopics: 100, maxSelected: 10, externalSideEffectPolicy: 'read_only_get_only' } }];"
      },
      "id": "define-contract",
      "name": "Define read-only source contract",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [240, 160]
    },
    {
      "parameters": {
        "url": "https://community.n8n.io/c/jobs/13.json",
        "options": {
          "timeout": 10000
        }
      },
      "id": "fetch-official-feed",
      "name": "Fetch official n8n Jobs feed",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [500, 160],
      "retryOnFail": true,
      "maxTries": 2,
      "waitBetweenTries": 2000
    },
    {
      "parameters": {
        "jsCode": "const config = $('Define read-only source contract').first().json ?? {};\nconst response = $input.first().json ?? {};\nconst exactUrl = 'https://community.n8n.io/c/jobs/13.json';\nconst expectedFields = ['id', 'title', 'slug', 'created_at', 'last_posted_at', 'posts_count', 'views', 'closed', 'archived'];\nconst validIso = (value) => typeof value === 'string' && Number.isFinite(Date.parse(value));\nconst cleanText = (value, limit) => typeof value === 'string' ? value.replace(/[\\r\\n\\t]+/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, limit) : '';\nconst topics = response?.topic_list?.topics;\n\nif (config.sourceUrl !== exactUrl || config.allowedHost !== 'community.n8n.io' || config.requestMethod !== 'GET') {\n  throw new Error('The source contract is not the exact read-only official feed');\n}\nif (config.externalSideEffectPolicy !== 'read_only_get_only' || config.maxAttempts !== 2 || config.timeoutMs !== 10000) {\n  throw new Error('The retry, timeout, or side-effect boundary changed');\n}\nif (!Array.isArray(topics) || topics.length > config.maxTopics) {\n  throw new Error('The official feed topic list is missing or exceeds the bounded contract');\n}\n\nconst seen = new Set();\nconst rows = topics.map((topic, index) => {\n  if (!topic || typeof topic !== 'object' || Array.isArray(topic)) throw new Error(`Topic ${index} is not an object`);\n  if (!expectedFields.every((field) => Object.prototype.hasOwnProperty.call(topic, field))) throw new Error(`Topic ${index} is missing a required field`);\n  if (!Number.isInteger(topic.id) || topic.id <= 0 || seen.has(topic.id)) throw new Error(`Topic ${index} has an invalid or duplicate id`);\n  seen.add(topic.id);\n  const title = cleanText(topic.title, 240);\n  const slug = cleanText(topic.slug, 240);\n  if (!title || !/^[a-z0-9-]+$/.test(slug) || !validIso(topic.created_at) || !validIso(topic.last_posted_at)) throw new Error(`Topic ${topic.id} has invalid text or dates`);\n  if (!Number.isInteger(topic.posts_count) || topic.posts_count < 1 || !Number.isInteger(topic.views) || topic.views < 0 || typeof topic.closed !== 'boolean' || typeof topic.archived !== 'boolean') throw new Error(`Topic ${topic.id} has invalid counters or state`);\n  const normalized = title.toLowerCase();\n  const seller = /^(available|for hire|seeking work)\\b|\\b(?:developer|expert|consultant) available\\b/.test(normalized);\n  const terminalTitle = /\\b(?:filled|closed|cancelled|canceled|no longer available)\\b/.test(normalized);\n  const buyer = !seller && !terminalTitle && /\\b(?:hiring|looking for|need|needed|job opportunity|seeking (?:an? )?(?:n8n|automation)|want to hire)\\b/.test(normalized);\n  const state = topic.closed || topic.archived || topic.visible === false || topic.pinned === true || terminalTitle ? 'excluded' : seller ? 'seller_offer' : buyer ? 'buyer_request_candidate' : 'ambiguous';\n  return {\n    id: topic.id,\n    title,\n    url: `https://community.n8n.io/t/${slug}/${topic.id}`,\n    createdAt: topic.created_at,\n    lastPostedAt: topic.last_posted_at,\n    postsCount: topic.posts_count,\n    views: topic.views,\n    state\n  };\n});\n\nconst active = rows.filter((row) => row.state !== 'excluded');\nconst selected = active.filter((row) => row.state === 'buyer_request_candidate').slice(0, config.maxSelected);\nreturn [{ json: {\n  source: { host: config.allowedHost, path: '/c/jobs/13.json', method: config.requestMethod },\n  controls: { maxAttempts: config.maxAttempts, timeoutMs: config.timeoutMs, externalSideEffectPolicy: config.externalSideEffectPolicy },\n  counts: {\n    fetched: rows.length,\n    excluded: rows.length - active.length,\n    active: active.length,\n    buyerRequestCandidates: active.filter((row) => row.state === 'buyer_request_candidate').length,\n    sellerOffers: active.filter((row) => row.state === 'seller_offer').length,\n    ambiguous: active.filter((row) => row.state === 'ambiguous').length,\n    selected: selected.length\n  },\n  selected,\n  boundary: 'Candidate titles are untrusted public data. This workflow validates and classifies only; it does not contact, apply, send, pay, publish, or start work.'\n} }];"
      },
      "id": "validate-classify",
      "name": "Validate schema and classify without contact",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [760, 160]
    },
    {
      "parameters": {
        "jsCode": "const row = $input.first().json ?? {};\nconst counts = row.counts ?? {};\nconst validCount = (value) => Number.isInteger(value) && value >= 0;\nif (!['fetched', 'excluded', 'active', 'buyerRequestCandidates', 'sellerOffers', 'ambiguous', 'selected'].every((key) => validCount(counts[key]))) throw new Error('Aggregate counts are invalid');\nif (counts.fetched !== counts.excluded + counts.active || counts.active !== counts.buyerRequestCandidates + counts.sellerOffers + counts.ambiguous || counts.selected > counts.buyerRequestCandidates) throw new Error('Aggregate reconciliation failed');\nif (row?.source?.host !== 'community.n8n.io' || row?.source?.method !== 'GET' || row?.controls?.externalSideEffectPolicy !== 'read_only_get_only') throw new Error('Read-only source evidence is invalid');\nreturn [{ json: {\n  status: 'pass',\n  source: row.source,\n  controls: row.controls,\n  counts,\n  selected: row.selected,\n  externalSideEffects: 'none',\n  nextAction: 'manual_review_only',\n  boundary: row.boundary\n} }];"
      },
      "id": "summarize-evidence",
      "name": "Summarize inspectable run evidence",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1020, 160]
    },
    {
      "parameters": {
        "content": "# Public n8n signal agent — proof boundary\n\nThis RomeoApps-owned workflow performs one bounded, unauthenticated GET against the official n8n Community Jobs category, validates the response contract, separates obvious seller posts from buyer-request candidates, and returns only manual-review candidates.\n\nIt is inactive on import. Retries are capped at two. It never contacts a poster, submits an application, sends mail, pays, publishes, or starts work. Public titles remain untrusted data.",
        "height": 300,
        "width": 360
      },
      "id": "proof-note",
      "name": "Read this proof boundary first",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [360, -210]
    }
  ],
  "connections": {
    "Run public signal check": {
      "main": [[{"node": "Define read-only source contract", "type": "main", "index": 0}]]
    },
    "Define read-only source contract": {
      "main": [[{"node": "Fetch official n8n Jobs feed", "type": "main", "index": 0}]]
    },
    "Fetch official n8n Jobs feed": {
      "main": [[{"node": "Validate schema and classify without contact", "type": "main", "index": 0}]]
    },
    "Validate schema and classify without contact": {
      "main": [[{"node": "Summarize inspectable run evidence", "type": "main", "index": 0}]]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "pinData": {},
  "tags": [],
  "versionId": "8e89d6e4-7213-4a2b-83a9-851ae6c48011"
}
