GraphCentric developer view
https://graphcentric.one/JB-1
The kingdom of the jobs: resolution and rendering defaults for every job record.
Navigation
Methods
| Method | Status | Basis |
|---|---|---|
| GET | available | representation candidates are configured |
| HEAD | available | GET representation headers |
| OPTIONS | available | HTTP capability discovery |
Resource Details
- Configuration
- https://graphcentric.one/kingdoms/job
- URI
- https://graphcentric.one/JB-1
- Graph
- https://graphcentric.one/graphs/public-resources
- Query
- s3://graphcentric.one/job-detail.sparql
Link Relations
2 links| rel | title | type | href |
|---|---|---|---|
| alternate | HTML | text/html;charset=utf-8 | https://graphcentric.one/kingdoms/job.html |
| alternate | JSON-LD (framed) | https://graphcentric.one/kingdoms/job.jsonld |
Resource Configuration Model
Turtle
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-html> , <https://graphcentric.one/kingdoms/job/.links/alternate-jsonld>;
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.sparqlBASE <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
Resource State
Turtle
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| stream | status | subscribers | last id | state |
|---|---|---|---|---|
| No stream resources are linked from this resource. | ||||