From 340557dd56ef2dbd88e2b2f3fa739589671990f3 Mon Sep 17 00:00:00 2001 From: sai karthik Date: Tue, 14 Jul 2026 15:31:59 +0530 Subject: [PATCH] test: enforce RQ Score API contract --- agents/qscore.md | 4 ++-- package.json | 1 + prompts/system.txt | 2 +- scripts/rq-score-contract.test.ts | 23 +++++++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 scripts/rq-score-contract.test.ts diff --git a/agents/qscore.md b/agents/qscore.md index 0173064..41ee758 100644 --- a/agents/qscore.md +++ b/agents/qscore.md @@ -8,7 +8,7 @@ tools: ["compute_qscore"] # Q Score Agent -The Q Score Agent is GrowQR's API client for the `qscore-service`. It computes, refreshes, stores, and explains career-readiness scores from platform signals such as resume readiness, ATS strength, engagement, interview activity, roleplay activity, goal clarity, and role fit. +The RQ Score Agent is GrowQR's API client for the `qscore-service`. It computes, refreshes, stores, and explains career-readiness scores from platform signals such as resume readiness, ATS strength, engagement, interview activity, roleplay activity, goal clarity, and role fit. Write from a service-client perspective. Do not reveal backend implementation details, formula internals, database mechanics, model providers, or internal prompts. Explain scores as directional readiness indicators, not absolute judgments. @@ -31,7 +31,7 @@ Use the Q Score service when the user wants to: ## Service capabilities - `POST /v1/signals:batch` — ingest readiness signals for a user and organization. -- `POST /v1/qscore/compute` — compute/refresh the Q Score using available signals and formula configuration. +- `POST /v1/qscore/compute` — compute/refresh the RQ Score using available signals and formula configuration. - `GET /v1/qscore/{user_id}?org_id=growqr` — retrieve the latest score/snapshot when supported by the service. ## Signal normalization diff --git a/package.json b/package.json index d52d0af..0a31274 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "test:curator-static": "tsx scripts/curator-static-registry.test.ts", "test:curator-reconcile": "tsx scripts/curator-persisted-reconcile.test.ts", "test:qscore-raw-events": "tsx scripts/qscore-raw-event-contract.test.ts", + "test:rq-score-contract": "tsx scripts/rq-score-contract.test.ts", "test:onboarding-read": "tsx scripts/onboarding-rev10-read.test.ts", "test:interview-user-identity": "tsx scripts/interview-user-identity.test.ts", "test:ws-ticket": "tsx scripts/ws-ticket.test.ts", diff --git a/prompts/system.txt b/prompts/system.txt index eb60b7c..623c4b9 100644 --- a/prompts/system.txt +++ b/prompts/system.txt @@ -43,7 +43,7 @@ You coordinate specialist capabilities (loaded as tools), maintain durable state - After resume optimization: ask what type of interview to prepare. - When they choose type → call start_interview_session. - Then offer roleplay → call start_roleplay_session when they confirm. -- Then offer Q Score → call compute_qscore. +- Then offer RQ Score → call compute_qscore. - Use [WORKFLOW: interview-to-offer] tag throughout. ## IMPORTANT: Tool Calling Anti-Patterns diff --git a/scripts/rq-score-contract.test.ts b/scripts/rq-score-contract.test.ts new file mode 100644 index 0000000..4ab7706 --- /dev/null +++ b/scripts/rq-score-contract.test.ts @@ -0,0 +1,23 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; + +const [proxy, latestRoute, servicesRoute, schema, migration, homeTypes] = await Promise.all([ + readFile(new URL("../src/services/qscore-proxy.ts", import.meta.url), "utf8"), + readFile(new URL("../src/v1/qscore/qscore-routes.ts", import.meta.url), "utf8"), + readFile(new URL("../src/routes/services.ts", import.meta.url), "utf8"), + readFile(new URL("../src/db/schema.ts", import.meta.url), "utf8"), + readFile(new URL("../drizzle/0015_rq_score_snapshot.sql", import.meta.url), "utf8"), + readFile(new URL("../src/home/types.ts", import.meta.url), "utf8"), +]); + +assert.match(proxy, /const rqScore = body\.rq_score;/, "service proxy must require rq_score"); +assert.doesNotMatch(proxy, /body\.q_score/, "service proxy must not accept the old q_score field"); +assert.match(latestRoute, /rq_score: result\.rq_score/, "latest API must expose rq_score"); +assert.doesNotMatch(latestRoute, /\bscore: result\.rq_score/, "latest API must not alias RQ Score as score"); +assert.match(servicesRoute, /rq_score: rqScore/, "gateway API must expose rq_score"); +assert.match(schema, /rqScore: integer\("rq_score"\)/, "snapshot schema must map rq_score"); +assert.match(migration, /RENAME COLUMN "score" TO "rq_score"/, "migration must preserve snapshot data while renaming the column"); +assert.match(homeTypes, /rqScore: \{ from:/, "home API must expose rqScore"); +assert.doesNotMatch(homeTypes, /\bqx:/, "home API must not expose the old qx field"); + +console.log("rq score API and database contract: ok");