GraphCentric developer view

https://graphcentric.one/kingdoms/job

https://graphcentric.one/kingdoms/job

The kingdom of the jobs: resolution and rendering defaults for every job record.

Navigation

Methods

MethodStatusBasis
GETavailablerepresentation candidates are configured
HEADavailableGET representation headers
OPTIONSavailableHTTP capability discovery

Resource Details

URI
https://graphcentric.one/kingdoms/job
Graph
https://graphcentric.one/graphs/public-resources
Query
s3://graphcentric.one/job-detail.sparql

Resource Configuration Model

Turtle

Server rendered
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX gcs:     <https://graphcentric.com/schema/>
PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
PREFIX schema:  <https://schema.org/>
PREFIX sh:      <http://www.w3.org/ns/shacl#>
PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>

<https://graphcentric.one/kingdoms/job/.links/alternate-html>
        rdf:type     gcs:Link;
        gcs:rel      "alternate";
        gcs:target   <https://graphcentric.one/kingdoms/job.html>;
        gcs:type     "text/html";
        schema:name  "HTML" .

<https://graphcentric.one/kingdoms/job>
        rdf:type            gcs:WebResource;
        gcs:graph           <https://graphcentric.one/graphs/public-resources>;
        gcs:link            <https://graphcentric.one/kingdoms/job/.links/alternate-jsonld> , <https://graphcentric.one/kingdoms/job/.links/alternate-html>;
        gcs:query           <s3://graphcentric.one/job-detail.sparql>;
        schema:description  "The kingdom of the jobs: resolution and rendering defaults for every job record.";
        schema:name         "Job" .

<https://graphcentric.one/kingdoms/job/.links/alternate-jsonld>
        rdf:type     gcs:Link;
        gcs:rel      "alternate";
        gcs:target   <https://graphcentric.one/kingdoms/job.jsonld>;
        gcs:type     "application/ld+json;profile=\"http://www.w3.org/ns/json-ld#framed\"";
        schema:name  "JSON-LD (framed)" .

<https://graphcentric.one/kingdoms/job.html>
        rdf:type           gcs:WebResource;
        gcs:contentType    "text/html;charset=utf-8";
        gcs:frame          <s3://graphcentric.one/job-detail-frame.jsonld>;
        gcs:graph          <https://graphcentric.one/graphs/public-resources>;
        gcs:outerTemplate  <s3://graphcentric.one/templates/one-root-v2.html>;
        gcs:template       <s3://graphcentric.one/job-detail.html>;
        schema:name        "Job HTML" .

SPARQL Query

s3://graphcentric.one/job-detail.sparql
BASE <https://graphcentric.one/>

PREFIX schema: <https://schema.org/>
PREFIX one: <https://graphcentric.info/ns/one#>
PREFIX gcs: <https://graphcentric.com/schema/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

# Job detail = design screen 4c (plan 30 Stage 6). KINGDOM RESOLUTION
# (ISSUE-PLATFORM-33): a job record carries gcs:kingdom </kingdoms/job> and the
# platform binds ?_subject to the record's own IRI — the /jobs/{jobKey}
# uriTemplate family is tombstoned, and so is the .job-detail shadow stream
# family (ISSUE-UI-6): each record match below keys on ?x = ?_subject alone.
# ?_subject substitutes lexically, so sub-SELECTs see it too. The POSTING is
# read from /graphs/one-jobs (company-visible); the pipeline CARDS from
# /graphs/one-hiring (ACL-scoped — admins/managers/recruiters resolve the
# board; a role that cannot read one-hiring gets counts of 0 and no cards).
# Typed BOTH schema:JobPosting (for /jobs/JOB-1.jsonld) and one:JobDetail
# (the frame @type). Person-spine: candidate display resolves from one-hiring
# (pure candidate) OR one-org (an employee applicant — Priya/Jonas).

CONSTRUCT {
  ?job a schema:JobPosting, one:JobDetail ;
    # RING 1 page stamp (§1.3a).
    one:pageAbout ?pageAbout ;
    one:pageTypeof "schema:JobPosting" ;
    one:pageKeyProp "one:jobKey" ;
    one:pageKey ?jobKeyLit ;
    schema:name ?title ;
    schema:title ?title ;
    schema:description ?jobDescription ;
    one:workplaceType ?req_workplaceType ;
    schema:employmentType ?req_employmentType ;
    one:contractType ?req_contractType ;
    schema:workHours ?req_workHours ;
    schema:qualifications ?req_qualifications ;
    schema:skills ?req_requiredSkills ;
    one:preferredSkills ?req_preferredSkills ;
    schema:experienceRequirements ?req_experienceRequirements ;
    one:languageRequirements ?req_languageRequirements ;
    one:screeningCriteria ?req_screeningCriteria ;
    one:isPublic "true" ;
    one:jobKey ?jobKeyLit ;
    one:statusName ?statusName ;
    one:statusSlug ?statusSlug ;
    one:openings ?openings ;
    one:department ?department ;
    one:jobLocation ?location ;
    one:openedDate ?openedDate ;
    one:canManage ?canManage ;
    one:canSetStatus ?canSetStatus ;
    one:hmName ?hmName ; one:hmInitials ?hmInitials ; one:hmColor ?hmColor ; one:hmHandle ?hmHandle ; one:hmProfileUrl ?hmProfileUrl ;
    one:recName ?recName ; one:recInitials ?recInitials ; one:recColor ?recColor ; one:recHandle ?recHandle ; one:recProfileUrl ?recProfileUrl ;
    one:stageColumn ?colNode ;
    one:appCard ?app .

  ?colNode a one:StageColumn ;
    one:colSlug ?slug ;
    one:colLabel ?label ;
    one:sortWeight ?colW ;
    one:count ?colCount .

  ?app a one:AppCard ;
    one:stageSlug ?stageSlug ;
    one:appUri ?appUri ;
    one:sortWeight ?cardSort ;
    one:candidateName ?candName ;
    one:candidateInitials ?candInitials ;
    one:avatarColor ?candColor ;
    one:cardSubtitle ?cardSubtitle ;
    one:cardPart1 ?cardPart1 ;
    one:cardPart2 ?cardPart2 ;
    one:cardPart3 ?cardPart3 ;
    one:badgeText ?badgeOut ;
    one:badgeKind ?badgeKindOut ;
    one:ivNodeIri ?ivNodeIri ;
    one:slotStartIso ?slotStartIso ;
    one:slotEndIso ?slotEndIso ;
    one:slotMeta ?slotMeta ;
    one:ivType ?ivType ;
    one:ivTypeLabel ?ivTypeLabel ;
    one:ivStateSlug ?ivStateSlug ;
    one:ivStateLabel ?ivStateLabel ;
    one:ivInterviewer ?ivInterviewer ;
    one:ivInterviewerIri ?ivInterviewerIri ;
    one:scorecardLabel ?scLabel ;
    one:scorecardVerdict ?scVerdictName ;
    one:scorecardAuthor ?scAuthorGiven ;
    one:scorecardAuthorHandle ?scAuthorHandle ;
    one:scorecardAuthorProfileUrl ?scAuthorProfileUrl ;
    one:scorecardMoreCount ?scMoreCount ;
    one:profileUrl ?profileUrl ;
    one:active ?active .
}
WHERE {
  BIND(IF(EXISTS { GRAPH </graphs/org-chart> { ?_user gcs:memberOf </teams/one-admins> } }, "Admin",
       IF(EXISTS { GRAPH </graphs/org-chart> { ?_user gcs:memberOf </teams/one-managers> } }, "Manager",
       IF(EXISTS { GRAPH </graphs/org-chart> { ?_user gcs:memberOf </teams/one-recruiters> } }, "Recruiter",
       IF(EXISTS { GRAPH </graphs/org-chart> { ?_user gcs:memberOf </teams/one-employees> } }, "Employee", "")))) AS ?userRole)
       # Display-only role label (plan 37 walkthrough): contractors read "Contractor" in
       # the shell footer; ?userRole keeps its trim semantics untouched.
       BIND(IF(EXISTS { GRAPH </graphs/one-org> { ?_user one:workerType </worker-types/contractor> } }, "Contractor", ?userRole) AS ?userRoleLabel)

  # Force /graphs/org-chart into this query's dataset (platform quirk, DEVLOG #90):
  # mothership builds a query's dataset from graphs referenced in POSITIVE patterns
  # only, so org-chart referenced solely inside the userRole BIND's EXISTS is never
  # loaded and every such EXISTS silently returns false -> userRole collapses to ""
  # (a trimmed shell rail). This scalar sub-SELECT reads it positively (one row, no
  # fan-out) so the userRole/board EXISTS above see the real memberships.
  { SELECT (COUNT(*) AS ?orgChartLoaded) WHERE { GRAPH </graphs/org-chart> { ?ocS ?ocP ?ocO } } }

  # --- the posting (one-jobs) ---
  GRAPH </graphs/one-jobs> {
    ?job a schema:JobPosting ;
      one:jobKey ?jobKeyLit ;
      schema:title ?title ;
      one:jobStatus ?status .
    FILTER(?job = ?_subject)
    BIND(STR(?job) AS ?pageAbout)   # RING 1 page stamp (§1.3a)
    OPTIONAL { ?job one:department ?department }
    OPTIONAL { ?job one:jobLocation ?location }
    # The day the job opened is DATA (schema:datePosted); the page renders it
    # through <one-date>. The seed's one:openedLabel ("Opened Jun 2") is words
    # with no year in the viewer's locale, so the page does not read it.
    OPTIONAL { ?job schema:datePosted ?datePostedRaw }
    OPTIONAL { ?job one:openings ?storedOpenings }
    OPTIONAL { ?job one:hiringManager ?hm }
    OPTIONAL { ?job one:recruiter ?rec }
    OPTIONAL { ?job schema:description ?jobDescription }
    OPTIONAL { ?job one:workplaceType ?req_workplaceType }
    OPTIONAL { ?job schema:employmentType ?req_employmentType }
    OPTIONAL { ?job one:contractType ?req_contractType }
    OPTIONAL { ?job schema:workHours ?req_workHours }
    OPTIONAL { ?job schema:qualifications ?req_qualifications }
    OPTIONAL { ?job schema:skills ?req_requiredSkills }
    OPTIONAL { ?job one:preferredSkills ?req_preferredSkills }
    OPTIONAL { ?job schema:experienceRequirements ?req_experienceRequirements }
    OPTIONAL { ?job one:languageRequirements ?req_languageRequirements }
    OPTIONAL { ?job one:screeningCriteria ?req_screeningCriteria }
    OPTIONAL { ?job one:requisition ?requisition }
  }
  # A visitor sees only an open posting. A signed-in member sees every posting
  # they can read (the hiring board keeps drafts and filled roles).
  FILTER(?userRole != "" || ?status = </job-statuses/open>)
  BIND(STR(COALESCE(?storedOpenings,
    IF(?status IN (</job-statuses/filled>, </job-statuses/closed>), 0, 1))) AS ?openings)
  BIND(STR(?datePostedRaw) AS ?openedDate)
  OPTIONAL { GRAPH </graphs/one-internal> { ?status schema:name ?statusNameRaw ; schema:identifier ?statusSlugRaw } }
  BIND(COALESCE(?statusNameRaw, REPLACE(STR(?status), "^.*/", "")) AS ?statusName)
  BIND(COALESCE(?statusSlugRaw, REPLACE(STR(?status), "^.*/", "")) AS ?statusSlug)

  # Hiring manager and recruiter displays use current Directory data. Profile URLs use UUIDs.
  # THE LOOKUP IS ANCHORED ON A SENTINEL: a posting with no one:hiringManager or
  # one:recruiter edge (createJob by /system, a CV-intake posting) leaves ?hm or
  # ?rec unbound, and an unbound subject in the OPTIONAL below would bind EVERY
  # Person of the directory, so the page printed the whole roster as one link
  # (2026-09-30, JOB-1 on production). The sentinel IRI matches no Person, so
  # the OPTIONAL yields nothing and the template omits the meta part.
  BIND(COALESCE(?hm, IRI("urn:one:no-person")) AS ?hmRef)
  BIND(COALESCE(?rec, IRI("urn:one:no-person")) AS ?recRef)
  OPTIONAL {
    GRAPH </graphs/one-org> {
      ?hmRef a schema:Person ; schema:identifier ?hmHandle .
      OPTIONAL { ?hmRef schema:name ?hmNameRaw . }
      OPTIONAL { ?hmRef schema:alternateName ?hmInitials . }
      OPTIONAL { ?hmRef one:avatarColor ?hmColor . }
    }
    BIND(COALESCE(STR(?hmNameRaw), "Person") AS ?hmName)
    BIND(CONCAT("/people/", ENCODE_FOR_URI(STR(?hmHandle))) AS ?hmProfileUrl)
  }
  OPTIONAL {
    GRAPH </graphs/one-org> {
      ?recRef a schema:Person ; schema:identifier ?recHandle .
      OPTIONAL { ?recRef schema:name ?recNameRaw . }
      OPTIONAL { ?recRef schema:alternateName ?recInitials . }
      OPTIONAL { ?recRef one:avatarColor ?recColor . }
    }
    BIND(COALESCE(STR(?recNameRaw), "Person") AS ?recName)
    BIND(CONCAT("/people/", ENCODE_FOR_URI(STR(?recHandle))) AS ?recProfileUrl)
  }

  # Edit job / Add candidate — admins, recruiters, managers.
  BIND(IF(
    EXISTS { GRAPH </graphs/org-chart> { ?_user gcs:memberOf </teams/one-admins> } } ||
    EXISTS { GRAPH </graphs/org-chart> { ?_user gcs:memberOf </teams/one-recruiters> } } ||
    EXISTS { GRAPH </graphs/org-chart> { ?_user gcs:memberOf </teams/one-managers> } },
    "true", "") AS ?canManage)

  # Job-status changes are admin/recruiter only (setJobStatus / editJob status gate,
  # open question #17) — so a manager, who can Edit job, does not get the Status field.
  BIND(IF(?userRole = "Admin" || ?userRole = "Recruiter", "true", "") AS ?canSetStatus)

  # Manager board-scoping (plan 30 Stage 6 ACL, design §2): admins + recruiters see
  # every job's pipeline; a MANAGER only sees the board of a job they run
  # (one:hiringManager = the caller). A manager viewing a job they do not run gets
  # empty columns (counts 0, no cards) — "manager sees only own job's candidates".
  BIND((?userRole = "Admin" || ?userRole = "Recruiter" || (BOUND(?hm) && ?hm = ?_user)) AS ?boardVisible)

  # --- five ordered stage columns with per-stage counts (?_subject is a constant) ---
  # The fixed 4c board columns; each carries its count, or 0 when empty. The
  # GROUP-BY sub-select joins to the VALUES rows on the shared ?stageConcept, so
  # every column emits even with no candidates in it.
  VALUES (?colNode ?slug ?label ?colWNum ?stageConcept) {
    (<#col-applied>   "applied"   "Applied"   0 </pipeline-stages/applied>)
    (<#col-screen>    "screen"    "Screen"    1 </pipeline-stages/screen>)
    (<#col-interview> "interview" "Interview" 2 </pipeline-stages/interview>)
    (<#col-offer>     "offer"     "Offer"     3 </pipeline-stages/offer>)
    (<#col-hired>     "hired"     "Hired"     4 </pipeline-stages/hired>)
  }
  BIND(STR(?colWNum) AS ?colW)
  OPTIONAL {
    SELECT ?stageConcept (COUNT(DISTINCT ?ca) AS ?cntRaw) WHERE {
      GRAPH </graphs/one-hiring> { ?ca a one:Application ; one:job ?cj ; one:pipelineStage ?stageConcept . }
      GRAPH </graphs/one-jobs> { ?cj one:jobKey ?ck . }
      FILTER(?cj = ?_subject)
    } GROUP BY ?stageConcept
  }
  BIND(IF(?boardVisible, STR(COALESCE(?cntRaw, 0)), "0") AS ?colCount)

  # --- pipeline cards ---
  OPTIONAL {
    FILTER(?boardVisible)
    GRAPH </graphs/one-hiring> {
      ?app a one:Application ;
        one:job ?jobMain ;
        one:pipelineStage ?stage ;
        one:cardSubtitle ?cardSubtitle ;
        schema:agent ?cand .
      OPTIONAL { ?app schema:position ?cardSortRaw }
      OPTIONAL { ?app one:badgeText ?badgeStored ; one:badgeKind ?badgeKindStored }
    }
    BIND(STR(?app) AS ?appUri)
    # THE SUBTITLE IS A LINE OF PARTS, NOT ONE STRING. The stored literal holds
    # at most three facts with a typed " · " between them ("Referral — Nina · 5d
    # in stage", "Offer pending approval · EUR 92,000 · expires 2026-07-20").
    # The card draws each fact in its own `.meta-part`, so the line draws the
    # separators (docs/design-system/chips.md, place 6): a typed dot in a card
    # is the tight-dot line above a wide-dot line that the owner refused. A
    # part that does not exist is "", which the template reads as absent.
    BIND(STR(?cardSubtitle) AS ?subAll)
    BIND(IF(CONTAINS(?subAll, " · "), STRBEFORE(?subAll, " · "), ?subAll) AS ?cardPart1)
    BIND(IF(CONTAINS(?subAll, " · "), STRAFTER(?subAll, " · "), "") AS ?subRest)
    BIND(IF(CONTAINS(?subRest, " · "), STRBEFORE(?subRest, " · "), ?subRest) AS ?cardPart2)
    BIND(IF(CONTAINS(?subRest, " · "), STRAFTER(?subRest, " · "), "") AS ?cardPart3)
    GRAPH </graphs/one-jobs> { ?jobMain one:jobKey ?jkMain . }
    FILTER(?jobMain = ?_subject)
    OPTIONAL { GRAPH </graphs/one-internal> { ?stage schema:identifier ?stageSlug ; one:sortWeight ?stageWeight } }
    BIND(STR(COALESCE(?cardSortRaw, 0)) AS ?cardSort)

    # Candidate display (person-spine): one-hiring (pure) OR one-org (employee).
    OPTIONAL { GRAPH </graphs/one-hiring> { ?cand schema:name ?cnH . OPTIONAL { ?cand schema:alternateName ?ciH } OPTIONAL { ?cand one:avatarColor ?ccH } } }
    OPTIONAL { GRAPH </graphs/one-org>    { ?cand schema:name ?cnO . OPTIONAL { ?cand schema:alternateName ?ciO } OPTIONAL { ?cand one:avatarColor ?ccO } } }
    BIND(COALESCE(?cnH, ?cnO, "Candidate") AS ?candName)
    BIND(COALESCE(?ciH, ?ciO, "?") AS ?candInitials)
    BIND(COALESCE(?ccH, ?ccO, "#5c6d84") AS ?candColor)

    # --- upcoming interview → accent SLOT CHIP + "Edit scheduled" quick-action ---
    # ONLY on an interview-stage card (design 4c: Maya, in the Interview column).
    # Gated on the app's CURRENT stage (same FILTER-in-OPTIONAL idiom as the
    # ?boardVisible gate above) so moving a candidate OUT of Interview never drags a
    # stale slot chip onto the new column even if the interview node still reads
    # interviewUpcoming.
    #
    # Pick the SOONEST upcoming interview: an app with >1 upcoming interview would
    # otherwise bind ?ivNode multi-valued → the frame serializes the whole set and
    # EVERY scalar below becomes a bracketed array (footgun #1 — the same class of
    # bug as the scorecard line). slotLabel is ISO-prefixed for live-scheduled
    # interviews ("YYYY-MM-DDTHH:MM · …"), so a lexical MIN per app is the
    # chronological earliest; re-join the interview whose slotLabel == that MIN to
    # re-identify the single winning node, then read its scalar props (a label
    # collision — same minute+type+interviewer — is the only re-fan risk, accepted
    # exactly as the scorecard MAX join below accepts a same-instant tie).
    #
    # #3 / Contract A+C: the old code piped the ISO-prefixed slotLabel straight into
    # the badge — THAT is the "2026-07-13T09:00 · screen · Olivia" raw-ISO leak this
    # fix removes. Emit instead the raw schema:startDate (?slotStartIso, rendered by
    # <one-date> so NO ISO reaches the chip) + the non-date remainder
    # (?slotMeta = "type · interviewer"), plus the one:Interview IRI + end + type
    # + interviewer so the card's "Edit scheduled" button can open the reschedule
    # dialog pre-filled (Contract A).
    OPTIONAL {
      FILTER(COALESCE(?stageSlug, "") = "interview")
      # Pick the SOONEST upcoming interview for this application, ROBUSTLY. The prior
      # form re-joined the winner by one:slotLabel AND required schema:startDate +
      # one:interviewType + schema:attendee ALL present on that node — so a single
      # missing or differently-shaped triple on the soonest interview collapsed the
      # WHOLE arm to nothing, and the slot chip + "Edit scheduled" button vanished for
      # any candidate with >1 upcoming interview (Maya: a seed technical slot AND a
      # live-scheduled screen slot). This is the fix-pack regression this replaces.
      #
      # Now: anchor ?ivNode on the upcoming interview that has NO EARLIER upcoming one
      # for the same app, ordered by (startDate, IRI) — a TOTAL order, so exactly ONE
      # wins even on a same-instant tie (no multi-value re-fan, footgun #1). Only
      # schema:startDate is required (it is the sort key AND the <one-date> chip);
      # type / attendee / booking-end are OPTIONAL, so the arm binds whenever ANY
      # upcoming interview exists, whatever else that node does or doesn't carry.
      GRAPH </graphs/one-hiring> {
        ?ivNode one:application ?app ; one:interviewUpcoming true ; schema:startDate ?ivStartRaw .
      }
      FILTER NOT EXISTS {
        GRAPH </graphs/one-hiring> {
          ?ivEarlier one:application ?app ; one:interviewUpcoming true ; schema:startDate ?ivEarlierStart .
          FILTER(STR(?ivEarlierStart) < STR(?ivStartRaw)
              || (STR(?ivEarlierStart) = STR(?ivStartRaw) && STR(?ivEarlier) < STR(?ivNode)))
        }
      }
      BIND(STR(?ivNode) AS ?ivNodeIri)
      BIND(STR(?ivStartRaw) AS ?slotStartIso)
      OPTIONAL { GRAPH </graphs/one-hiring> { ?ivNode one:interviewType ?ivTypeRaw } }
      BIND(COALESCE(?ivTypeRaw, "") AS ?ivType)
      # THE TYPE HAS ONE SPELLING. ?ivType is the raw value that the reschedule
      # dialog reads (data-iv-type). The card shows ?ivTypeLabel: the first
      # letter upper and the rest lower, which is what /interviews draws
      # (capitalizeType in one-hiring-grids.js). The type is free text that a
      # caller wrote, so the card does not translate it.
      BIND(IF(STRLEN(?ivType) > 0, CONCAT(UCASE(SUBSTR(?ivType, 1, 1)), LCASE(SUBSTR(?ivType, 2))), "") AS ?ivTypeLabel)
      # THE STATUS OF THE CARD IS THE STATE OF THE INTERVIEW, not the name of
      # the lane 40px above it. one:interviewState is the hiring-side source of
      # truth (scheduled, completed, no-show, cancelled); a seed interview that
      # carries only one:interviewUpcoming true is scheduled by definition.
      # THE NAME LOOKUP IS INSIDE THE STATE OPTIONAL. As a sibling OPTIONAL, an
      # interview with no one:interviewState left ?ivStateRes unbound, and the
      # lookup then matched EVERY named node of one-internal: the card printed
      # the raw list ["Timing" "Interview" "No priority" …] and it spilled
      # across three board columns (design system v3 audit-visual, /jobs/JOB-1).
      OPTIONAL {
        GRAPH </graphs/one-hiring> { ?ivNode one:interviewState ?ivStateRes }
        OPTIONAL { GRAPH </graphs/one-internal> { ?ivStateRes schema:name ?ivStateNameRaw ; schema:identifier ?ivStateSlugRaw } }
      }
      BIND(COALESCE(?ivStateSlugRaw, "scheduled") AS ?ivStateSlug)
      BIND(COALESCE(?ivStateNameRaw, "Scheduled") AS ?ivStateLabel)
      # Interviewer display from the Person spine (one-org): the given name feeds
      # the compact slot meta, the full name the dialog's "current interviewer" line.
      OPTIONAL {
        GRAPH </graphs/one-hiring> { ?ivNode schema:attendee ?ivAttendee . }
        OPTIONAL { GRAPH </graphs/one-org> { ?ivAttendee schema:givenName ?ivIntGiven } }
        OPTIONAL { GRAPH </graphs/one-org> { ?ivAttendee schema:name ?ivIntFull } }
        OPTIONAL { GRAPH </graphs/one-org> { ?ivAttendee a schema:Person } BIND(STR(?ivAttendee) AS ?ivIntIriRaw) }
      }
      BIND(COALESCE(?ivIntFull, ?ivIntGiven, "") AS ?ivInterviewer)
      BIND(COALESCE(?ivIntIriRaw, "") AS ?ivInterviewerIri)
      # slotMeta = "type · interviewer", degrading gracefully if either is missing.
      BIND(IF(STRLEN(?ivType) > 0 && BOUND(?ivIntGiven), CONCAT(?ivType, " · ", ?ivIntGiven),
           IF(STRLEN(?ivType) > 0, ?ivType, COALESCE(?ivIntGiven, ""))) AS ?slotMeta)
      # End instant from the linked plan-47 booking (company-readable, a FIXED graph
      # → no fan-out). The interview node itself carries only schema:startDate, and
      # seed-demo interviews have no booking, so this stays OPTIONAL — ?slotEndIso is
      # simply "" for them and the reschedule dialog derives the span from the type
      # duration. Feeds data-iv-end for pre-selecting the current slot.
      OPTIONAL {
        GRAPH </graphs/one-hiring> { ?ivNode one:booking ?ivBooking . }
        GRAPH </graphs/one-scheduling> { ?ivBooking schema:endDate ?ivEndRaw . }
      }
      BIND(COALESCE(STR(?ivEndRaw), "") AS ?slotEndIso)
    }
    # The stored state (e.g. "Declined") is the status tag of a card that has no
    # upcoming interview; the interview slot is emitted above as raw ISO + parts
    # so no ISO reaches the card (#3).
    BIND(?badgeStored AS ?badgeOut)
    BIND(COALESCE(?badgeKindStored, "attention") AS ?badgeKindOut)

    # Scorecard summary line (design 4c: "✓ Screen scorecard — Strong yes (Olivia)").
    # FOOTGUN #1 (THE broken-card bug): the prior form bound ?sc/?scLabel/
    # ?scVerdictName/?scAuthorGiven to EVERY scorecard of the app, so an app with
    # several rounds (JOB-3/Marcus has three) serialized each field as a JSON array
    # and the card printed the bracketed
    # ["Technical scorecard" "System design scorecard" …] "Yes" (["Marco" …]) mess.
    # An app accrues one scorecard per round, so pick exactly ONE — the EARLIEST by
    # schema:dateCreated — with a MIN sub-SELECT keyed on ?app, then join the
    # scorecard row whose dateCreated == that MIN to re-identify the single winning
    # node. Every emitted field is then a scalar. EARLIEST (not latest): the design's
    # headline is the "Screen scorecard" — the first/entry evaluation that got the
    # candidate into the pipeline (4c shows exactly "✓ Screen scorecard — Strong yes")
    # — and pinning the earliest keeps that headline STABLE as later rounds land
    # (later feedback rolls into "(+N more)"; the full set lives on the candidate
    # detail page). When the app carries >1 scorecard we emit one:scorecardMoreCount.
    OPTIONAL {
      { SELECT ?app (MIN(?scDateAll) AS ?scMin) (COUNT(DISTINCT ?scAll) AS ?scCountRaw) WHERE {
          GRAPH </graphs/one-hiring> { ?scAll one:application ?app ; one:scorecardLabel ?scLabelAll ; schema:dateCreated ?scDateAll . }
      } GROUP BY ?app }
      GRAPH </graphs/one-hiring> { ?sc one:application ?app ; one:scorecardLabel ?scLabel ; one:verdict ?scVerdictRes ; schema:author ?scAuthorRes ; schema:dateCreated ?scMin . }
      OPTIONAL { GRAPH </graphs/one-internal> { ?scVerdictRes schema:name ?scVerdictName } }
      OPTIONAL {
        GRAPH </graphs/one-org> {
          ?scAuthorRes a schema:Person ; schema:identifier ?scAuthorHandle .
          OPTIONAL { ?scAuthorRes schema:givenName ?scAuthorGivenRaw . }
        }
        BIND(COALESCE(STR(?scAuthorGivenRaw), "Person") AS ?scAuthorGiven)
        BIND(CONCAT("/people/", ENCODE_FOR_URI(STR(?scAuthorHandle))) AS ?scAuthorProfileUrl)
      }
      # Unbound sentinel (?scNoMore is never otherwise bound): with a single
      # scorecard the IF yields the unbound var → one:scorecardMoreCount is simply
      # omitted (the never-bound-sentinel idiom scheduleInterview uses for its kit).
      #
      # LOCALISATION: this used to CONCAT "(+", N, " more)" here. A string built
      # in the query occurs in no source file, so no catalog key could ever reach
      # it and the marker was permanently English (docs/localisation.md). The
      # query now emits the COUNT of the further scorecards and the card writes
      # the marker in one {% t %} block. Nothing else read the assembled string.
      BIND(IF(?scCountRaw > 1, STR(?scCountRaw - 1), ?scNoMore) AS ?scMoreCount)
    }

    # Only a canonical Directory Person gets a profile link. Candidate routes stay separate.
    OPTIONAL { GRAPH </graphs/one-org> { ?cand a schema:Person ; schema:identifier ?candProfileId . } }
    BIND(IF(COALESCE(?stageSlug, "") = "hired" && BOUND(?candProfileId),
            CONCAT("/people/", ENCODE_FOR_URI(STR(?candProfileId))), "") AS ?profileUrl)
    # Interview-stage card is highlighted (design 4c).
    BIND(IF(COALESCE(?stageSlug, "") = "interview", "true", "") AS ?active)
  }
}

Query Scope

Named Graphs

17 graphs

Resource State

Turtle

Server rendered
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX gcs:     <https://graphcentric.com/schema/>
PREFIX one:     <https://graphcentric.info/ns/one#>
PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
PREFIX schema:  <https://schema.org/>
PREFIX sh:      <http://www.w3.org/ns/shacl#>
PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>

SSE Status

Stream link relations
streamstatussubscriberslast idstate
No stream resources are linked from this resource.