{
  "title": "Hacker News Mentions Scraper - Stories, Comments & Evidence",
  "type": "object",
  "schemaVersion": 1,
  "properties": {
    "queries": {
      "title": "Words or phrases to monitor",
      "type": "array",
      "editor": "stringList",
      "items": {
        "type": "string"
      },
      "description": "Up to 10 literal case-insensitive phrases, at most 100 characters each. Empty/omitted uses OpenAI. A story must match its title, text or linked URL; a comment must match its own text. Fuzzy search and parent-only matches are rejected.",
      "prefill": [
        "OpenAI"
      ],
      "sectionCaption": "1 · Choose phrases and item types",
      "sectionDescription": "Matching is a literal case-insensitive substring, verified against the source text."
    },
    "itemType": {
      "title": "Item type",
      "type": "string",
      "editor": "select",
      "description": "Search stories, comments, or both. Comment titles provide thread context, while literal match evidence must be in the comment text.",
      "enum": [
        "all",
        "story",
        "comment"
      ],
      "default": "all"
    },
    "sort": {
      "title": "Search order",
      "type": "string",
      "editor": "select",
      "description": "Recent uses the newest indexed matches first; relevance uses Algolia search ranking. Results from multiple queries are processed in sorted query order, not globally ranked.",
      "enum": [
        "recent",
        "relevance"
      ],
      "default": "recent"
    },
    "storyTag": {
      "title": "Story category",
      "type": "string",
      "editor": "select",
      "description": "Optional documented story tag. ask_hn and show_hn require itemType story; tags are also checked locally.",
      "enum": [
        "any",
        "ask_hn",
        "show_hn"
      ],
      "default": "any"
    },
    "author": {
      "title": "Exact public author handle",
      "type": "string",
      "editor": "textfield",
      "description": "Optional exact Hacker News username, 1-50 letters, digits, underscores or hyphens. This uses the public search author tag and a local exact check.",
      "sectionCaption": "2 · Narrow author and publication dates",
      "sectionDescription": "Optional exact author and inclusive UTC date filters."
    },
    "startDate": {
      "title": "Published on or after",
      "type": "string",
      "editor": "textfield",
      "description": "Optional real YYYY-MM-DD date, inclusive at 00:00 UTC. Filters the item publication time, not the index update time."
    },
    "endDate": {
      "title": "Published on or before",
      "type": "string",
      "editor": "textfield",
      "description": "Optional real YYYY-MM-DD date, inclusive through the end of that UTC day."
    },
    "maxCandidatesPerQuery": {
      "title": "Search candidate window per phrase",
      "type": "integer",
      "editor": "number",
      "description": "Inspect this many indexed candidates per query before local literal checks and deduplication. Algolia only exposes up to 1,000 hits for a search; this is not complete historical coverage.",
      "default": 100,
      "minimum": 1,
      "maximum": 1000,
      "sectionCaption": "3 · Bound candidates and saved mentions",
      "sectionDescription": "Candidate windows and saved-result caps are separate; not every candidate qualifies."
    },
    "pageSize": {
      "title": "Search page size",
      "type": "integer",
      "editor": "number",
      "description": "Number of indexed candidates requested per page. Small pages help bounded pagination checks but require more requests.",
      "default": 100,
      "minimum": 1,
      "maximum": 100
    },
    "maxResults": {
      "title": "Maximum saved mentions",
      "type": "integer",
      "editor": "number",
      "description": "Maximum unique matching rows saved across the whole run. Normal requested caps are bounded coverage, not source failures.",
      "default": 20,
      "minimum": 1,
      "maximum": 1000
    },
    "comparePrevious": {
      "title": "Compare with prior saved observations",
      "type": "boolean",
      "editor": "checkbox",
      "default": false,
      "description": "Return matching rows with new, changed or unchanged comparison status. Uses a named persistent store. Only confirmed saved rows advance history. The first run establishes observations, and SUMMARY confirms whether history was committed.",
      "sectionCaption": "4 · Optional saved mention comparisons",
      "sectionDescription": "Metric changes can count as changes. Only confirmed saved rows advance history."
    },
    "onlyChanges": {
      "title": "Save only new or changed observations",
      "type": "boolean",
      "editor": "checkbox",
      "default": false,
      "description": "Implies comparison. Unchanged matching observations are skipped without a dataset charge. A first run saves matching observations; subsequent unchanged runs can correctly save zero. No disappearance/deletion inference."
    },
    "snapshotName": {
      "title": "History name",
      "type": "string",
      "editor": "textfield",
      "description": "Use 1-80 letters, digits, underscores or hyphens. History also includes the sorted sources/queries and content filters. Run only one writer at a time for each scope. Start a new name for an incompatible saved baseline.",
      "default": "default"
    },
    "maxRunSeconds": {
      "title": "Run time budget (seconds)",
      "type": "integer",
      "editor": "number",
      "description": "Requests use this budget and the platform deadline minus 25 seconds. Each request is capped at 25 seconds. Partial coverage and spending stops are explicit in SUMMARY.",
      "default": 240,
      "minimum": 30,
      "maximum": 3600,
      "sectionCaption": "5 · Run budget",
      "sectionDescription": "Source failures and time/spending stops are reported in SUMMARY."
    }
  },
  "description": "Choose keywords and supported Hacker News search filters, then bound the candidate window and saved results. Matched text is literal evidence, not inferred intent or sentiment."
}
