kind: signal_detection_catalog
schema_version: 1
owner: revops

# How each signal state is set. Joined to `signals.yaml` on `signal_id`, which
# that file owns and mints. This file adds no meaning and no audience guidance.
#
# `on_unknown` is what an audience filter does when the state is unknown.
# Default `include` for qualifying signals so partial coverage does not silently
# shrink a list, `exclude` for gating signals so we never assert a frame we have
# not checked. An audience may override it per requirement.
#
# `coverage` is the measured share of accounts in scope for which we currently
# resolve a state. `null` means not yet measured. A detection row missing for a
# signal in `signals.yaml` means the signal is defined but not yet collectable,
# which is the enrichment backlog.

detection:
  - signal_id: SG_001
    class: derived
    observable: >-
      No single field states this. It is computed from SG_002 and SG_003, and
      confirmed by ownership language on the about or history page.
    sources:
      - linkedin_people
      - company_website
    on_unknown: exclude
    refresh_days: 90
    collection_cost: low
    coverage: null

  - signal_id: SG_024
    class: public_event
    observable: >-
      No securities registrant record, no ticker symbol, and no investor
      relations section on the company site. A privately held subsidiary of a
      publicly traded parent makes this absent.
    sources:
      - public_filings
      - company_website
      - linkedin_company
    on_unknown: exclude
    refresh_days: 180
    collection_cost: low
    coverage: null

  - signal_id: SG_025
    class: digital_artifact
    observable: >-
      The founded year on the company profile, or the incorporation date on the
      state business registration, is more than three years before the
      enrichment date. Where the two disagree, prefer the incorporation date. A
      rebranded or re-registered entity can understate actual operating age.
    sources:
      - linkedin_company
      - company_website
      - public_filings
    on_unknown: exclude
    refresh_days: 365
    collection_cost: low
    coverage: null

  - signal_id: SG_002
    class: headcount_pattern
    observable: >-
      Two or more people on the company employee list share a surname, weighted
      higher when at least one of them holds a leadership title.
    sources:
      - linkedin_people
    on_unknown: include
    refresh_days: 90
    collection_cost: low
    coverage: null

  - signal_id: SG_003
    class: digital_artifact
    observable: >-
      The legal or trading name contains the surname of a person holding an
      owner, founder, president, or chief executive title.
    sources:
      - linkedin_company
      - company_website
    on_unknown: include
    refresh_days: 180
    collection_cost: low
    coverage: null

  - signal_id: SG_004
    class: role_presence
    observable: >-
      At least one current employee holds a chief financial officer or finance
      director title.
    sources:
      - linkedin_people
      - company_website
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_005
    class: role_presence
    observable: >-
      SG_004 is present and a separate current employee holds a controller,
      comptroller, or accounting manager title.
    sources:
      - linkedin_people
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_006
    class: role_tenure
    observable: >-
      The start date on the current chief executive role is less than six months
      before the enrichment date.
    sources:
      - linkedin_people
      - pr_wire
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_007
    class: role_tenure
    observable: >-
      The start date on the current chief financial officer role is less than
      six months before the enrichment date.
    sources:
      - linkedin_people
      - pr_wire
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_008
    class: role_tenure
    observable: >-
      The start date on the current chief sales officer or chief revenue officer
      role is less than six months before the enrichment date.
    sources:
      - linkedin_people
      - pr_wire
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_009
    class: role_presence
    observable: >-
      At least one current employee holds an information technology, systems
      administrator, or internal support title.
    sources:
      - linkedin_people
      - job_postings
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_010
    class: role_presence
    observable: >-
      At least one current employee holds a chief technology officer, vice
      president of technology, or vice president of engineering title.
    sources:
      - linkedin_people
      - company_website
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_011
    class: role_presence
    observable: >-
      At least one current employee holds a vice president of sales or head of
      sales title.
    sources:
      - linkedin_people
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_012
    class: role_presence
    observable: >-
      At least one current employee holds a marketing title, or the company
      publishes campaign, content, or brand material under a named owner.
    sources:
      - linkedin_people
      - company_website
    on_unknown: include
    refresh_days: 90
    collection_cost: low
    coverage: null

  - signal_id: SG_013
    class: headcount_pattern
    observable: >-
      At least one current employee lists a location in India, weighted higher
      when the role is administrative, accounting, or data entry.
    sources:
      - linkedin_people
      - job_postings
    on_unknown: include
    refresh_days: 90
    collection_cost: low
    coverage: null

  - signal_id: SG_014
    class: public_event
    observable: >-
      The company name appears in a release on PR Newswire, Business Wire, or a
      comparable distribution service.
    sources:
      - pr_wire
    on_unknown: include
    refresh_days: 7
    collection_cost: medium
    coverage: null

  - signal_id: SG_015
    class: digital_artifact
    observable: >-
      The public site is responsive, uses a current design pattern, and shows
      content updated within the last year.
    sources:
      - company_website
    on_unknown: include
    refresh_days: 180
    collection_cost: medium
    coverage: null

  - signal_id: SG_016
    class: hiring_activity
    observable: >-
      One or more current or recent job postings list a named enterprise
      resource planning or accounting system as a requirement or preferred
      skill. Record every uniquely named controlled system against the
      `systems_of_record` dimension, preserve any other known systems already
      established, and normalize the result to catalog order. A known system
      replaces `[unknown]`; it is never appended beside `unknown`.
    sources:
      - job_postings
      - company_website
    on_unknown: include
    refresh_days: 7
    collection_cost: medium
    coverage: null

  - signal_id: SG_017
    class: hiring_activity
    observable: >-
      One or more open postings describe manual entry, reconciliation, or order
      processing as the primary duty.
    sources:
      - job_postings
    on_unknown: include
    refresh_days: 7
    collection_cost: medium
    coverage: null

  - signal_id: SG_018
    class: public_event
    observable: >-
      A press release, filing, or company announcement dated within eighteen
      months describes a completed transaction.
    sources:
      - pr_wire
      - public_filings
      - linkedin_company
    on_unknown: include
    refresh_days: 30
    collection_cost: medium
    coverage: null

  - signal_id: SG_019
    class: headcount_pattern
    observable: >-
      The median of current role start dates across the employee list is more
      than seven years before the enrichment date.
    sources:
      - linkedin_people
    on_unknown: exclude
    refresh_days: 180
    collection_cost: medium
    coverage: null

  - signal_id: SG_020
    class: role_presence
    observable: >-
      At least one current employee holds a chief operating officer, vice
      president of operations, or director of operations title.
    sources:
      - linkedin_people
      - company_website
    on_unknown: include
    refresh_days: 30
    collection_cost: low
    coverage: null

  - signal_id: SG_021
    class: public_event
    observable: >-
      A sponsor names the company in its portfolio listing, or a release or
      filing describes a completed investment or buyout. Sponsor ownership is
      not inferred from executive tenure.
    sources:
      - pr_wire
      - public_filings
      - manual_research
    on_unknown: include
    refresh_days: 90
    collection_cost: high
    coverage: null

  - signal_id: SG_022
    class: headcount_pattern
    observable: >-
      The company lists more than one operating address, excluding registered
      agent and mailing addresses. Count sites and record the band against the
      `location_footprint` dimension, and record which address is headquarters.
    sources:
      - company_website
      - linkedin_company
      - public_filings
    on_unknown: include
    refresh_days: 90
    collection_cost: medium
    coverage: null

  - signal_id: SG_023
    class: public_event
    observable: >-
      A release, local news item, or company announcement dated within twelve
      months describes an opened, expanded, or relocated facility. A posting for
      roles at an address absent from the prior site list counts as corroboration.
    sources:
      - pr_wire
      - company_website
      - job_postings
    on_unknown: include
    refresh_days: 30
    collection_cost: high
    coverage: null
