Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ jobs:
run: npm run check:cost-model
- name: Install dependencies
run: npm ci --ignore-scripts --no-audit --no-fund
- name: Check out the canonical university registry
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: GapwiseHQ/gapwise
ref: main
path: .canonical-gapwise
sparse-checkout: universities.json
persist-credentials: false
- name: Verify supported university parity
run: npm run check:universities
- name: Verify generated MCP contract
run: npm run contract:check
- name: Typecheck
Expand Down
2 changes: 1 addition & 1 deletion CAMPUS_DATA_OWNERSHIP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Campus data ownership

Canonical public University of Toronto campus facts and geometry live in `GapwiseHQ/data`. UTM's reviewed entrance/routing dataset lives under `data/utm`; UTSG and UTSC identities and geometry live in their own campus directories.
Canonical public multi-university campus facts and geometry live in `GapwiseHQ/data`. University datasets retain source provenance and uncertainty; UTM's specialized reviewed entrance/routing artifacts live under `data/utm`.

`gapwise` consumes a validated build-time snapshot of that dataset and remains responsible for deterministic routing, gap planning, public API behavior, and SDK contracts. `ai` should continue exposing those deterministic Gapwise semantics through MCP rather than becoming a second campus-data or routing engine.

Expand Down
4 changes: 2 additions & 2 deletions ECOSYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`ai` is the OAuth/MCP trust boundary of the seven-repository Gapwise product ecosystem. It exposes stateless public multi-university campus intelligence plus explicitly delegated, minimized student context and bounded actions to compatible AI clients. It does not replace deterministic Gapwise product logic, the public campus API, or the public SDKs.

All seven first-party product repositories are owned by the **Gapwise** GitHub organization (`GapwiseHQ`). Organization-wide GitHub defaults live in the separate `.github` repository. Andrew Muratov remains the creator and primary maintainer.
All first-party product repositories are owned by the **Gapwise** GitHub organization (`GapwiseHQ`). Organization-wide GitHub defaults live in the separate `.github` repository. Andrew Muratov remains the creator and primary maintainer.

## Connected surfaces

Expand All @@ -23,7 +23,7 @@ All seven first-party product repositories are owned by the **Gapwise** GitHub o

## Product scope

Gapwise delegated timetable context supports all supported institutions and campuses. The stateless public tools expose multi-university discovery, building, and routing queries across all 11 supported universities (13 campus models), with legacy UTM-specific tools providing dedicated UTM place catalog and gap-window planning tools. AI must preserve institution and campus identity and must not extrapolate campus-specific place or routing tools beyond their supported coverage.
Gapwise delegated timetable context supports all supported institutions and campuses. The stateless public tools expose multi-university discovery, building, and routing queries across all 13 supported universities (15 campus models), with legacy UTM-specific tools providing dedicated UTM place catalog and gap-window planning tools. AI must preserve institution and campus identity and must not extrapolate campus-specific place or routing tools beyond their supported coverage.

## Public SDK state

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

## What Gapwise AI is

Gapwise AI is the provider-neutral AI integration layer of **Gapwise**, a free and open-source timetable and campus-intelligence platform for university students across 11 Canadian universities (University of Toronto, Carleton University, Toronto Metropolitan University, Queen's University, Wilfrid Laurier University, York University, McMaster University, Western University, University of Guelph, University of Ottawa, and Brock University).
Gapwise AI is the provider-neutral AI integration layer of **Gapwise**, a free and open-source timetable and campus-intelligence platform for university students across 13 Canadian universities, including the University of British Columbia and University of Waterloo editions.

The main [`gapwise`](https://github.com/GapwiseHQ/gapwise) platform owns canonical student state and deterministic timetable/campus calculations. [`android`](https://github.com/GapwiseHQ/android) and [`ios`](https://github.com/GapwiseHQ/ios) provide native clients. Gapwise AI exposes a narrow remote MCP interface to bounded context rather than becoming a second timetable, routing, or planning engine.

Expand Down Expand Up @@ -187,7 +187,7 @@ The JavaScript/TypeScript SDK is published on npm and JSR; the Python SDK is pub
| **[`docs`](https://github.com/GapwiseHQ/docs)** | Canonical public developer documentation | [docs.gapwise.ca](https://docs.gapwise.ca) |
| **[`status`](https://github.com/GapwiseHQ/status)** | Independent service-health monitoring and incident communication | [status.gapwise.ca](https://status.gapwise.ca) |

All seven first-party product repositories are owned by the **Gapwise** GitHub organization (`GapwiseHQ`). Organization-wide GitHub defaults live in [`.github`](https://github.com/GapwiseHQ/.github). Andrew Muratov remains the creator and primary maintainer.
All first-party product repositories are owned by the **Gapwise** GitHub organization (`GapwiseHQ`). Organization-wide GitHub defaults live in [`.github`](https://github.com/GapwiseHQ/.github). Andrew Muratov remains the creator and primary maintainer.

---

Expand Down
6 changes: 3 additions & 3 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import "./globals.css";

export const metadata: Metadata = {
title: "Gapwise AI",
description: "Permissioned, provider-neutral MCP access to Gapwise campus intelligence and explicitly delegated student context across 11 Canadian universities.",
description: "Permissioned, provider-neutral MCP access to Gapwise campus intelligence and explicitly delegated student context across 13 Canadian universities.",
icons: { icon: "/favicon.svg" },
robots: { index: false, follow: false },
openGraph: {
title: "Gapwise AI",
description: "Permissioned, provider-neutral MCP access to Gapwise campus intelligence and explicitly delegated student context across 11 Canadian universities.",
description: "Permissioned, provider-neutral MCP access to Gapwise campus intelligence and explicitly delegated student context across 13 Canadian universities.",
url: "https://ai.gapwise.ca",
siteName: "Gapwise AI",
type: "website",
Expand All @@ -25,7 +25,7 @@ export const metadata: Metadata = {
twitter: {
card: "summary_large_image",
title: "Gapwise AI",
description: "Permissioned, provider-neutral MCP access to Gapwise campus intelligence and explicitly delegated student context across 11 Canadian universities.",
description: "Permissioned, provider-neutral MCP access to Gapwise campus intelligence and explicitly delegated student context across 13 Canadian universities.",
images: ["https://ai.gapwise.ca/og-card.png"],
},
};
Expand Down
15 changes: 15 additions & 0 deletions contracts/supported-universities.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"uoft": { "defaultCampus": "utm", "campuses": ["utm", "utsg", "utsc"] },
"carleton": { "defaultCampus": "carleton", "campuses": ["carleton"] },
"tmu": { "defaultCampus": "tmu", "campuses": ["tmu"] },
"queens": { "defaultCampus": "queens", "campuses": ["queens"] },
"laurier": { "defaultCampus": "waterloo", "campuses": ["waterloo"] },
"york": { "defaultCampus": "keele", "campuses": ["keele"] },
"mcmaster": { "defaultCampus": "mcmaster", "campuses": ["mcmaster"] },
"western": { "defaultCampus": "western", "campuses": ["western"] },
"guelph": { "defaultCampus": "guelph", "campuses": ["guelph"] },
"uottawa": { "defaultCampus": "uottawa", "campuses": ["uottawa"] },
"brock": { "defaultCampus": "brock", "campuses": ["brock"] },
"ubc": { "defaultCampus": "ubc-vancouver", "campuses": ["ubc-vancouver"] },
"waterloo": { "defaultCampus": "waterloo-main", "campuses": ["waterloo-main"] }
}
2 changes: 1 addition & 1 deletion docs/CURRENT_LIMITATIONS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Current integration limitations

- The MCP surface now includes 12 stateless public campus-intelligence tools (supporting multi-university university/campus discovery, building search, and routing across 11 universities, plus dedicated UTM place and gap-planning tools) alongside the permissioned private student-context tools. Public tools do not authenticate a Gapwise account and must not be treated as access to a student's private timetable, friends, location, or sync state.
- The MCP surface includes 17 stateless public campus-intelligence tools (10 canonical multi-university tools supporting discovery, building search, places, routing, and gap planning across 13 universities, plus 7 deprecated UTM compatibility aliases) alongside the permissioned private student-context tools. Public tools do not authenticate a Gapwise account and must not be treated as access to a student's private timetable, friends, location, or sync state.
- MCP write actions are applied by Gapwise through the queued-action channel; they do not remotely rewrite the primary encrypted private payload.
- Broad ChatGPT/Claude support is still a release-validation target rather than a universal support claim. Real OAuth/read/write/revoke behavior depends on the current client/product capabilities and must pass the repository's compatibility matrix before a client is represented as supported.
2 changes: 1 addition & 1 deletion docs/TOOL_CONTRACT.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ MCP text `content` is intentionally compact. Exact recurrence, exclusions, rich

## Public campus tools

These tools use public deterministic Gapwise campus data across all 11 supported universities. They do not authenticate a Gapwise account and do not read a student's private timetable, friends, precise location, or private sync state.
These tools use public deterministic Gapwise campus data across all 13 supported universities and 15 campus models. They do not authenticate a Gapwise account and do not read a student's private timetable, friends, precise location, or private sync state.

### `list_supported_universities`
Lists all supported universities and campus editions across the Gapwise platform with capabilities and status.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
"test": "vitest run",
"scan:secrets": "node scripts/scan-secrets.mjs",
"check:cost-model": "node scripts/check-cost-model.mjs",
"check:universities": "node scripts/check-university-registry.mjs",
"audit:production": "node scripts/audit-production-dependencies.mjs",
"check": "npm run scan:secrets && npm run check:cost-model && npm run contract:check && npm run typecheck && npm test && npm run build",
"check": "npm run scan:secrets && npm run check:cost-model && npm run check:universities && npm run contract:check && npm run typecheck && npm test && npm run build",
"contract:generate": "node scripts/mcp-surface-contract.mjs --write",
"contract:check": "node scripts/mcp-surface-contract.mjs --check"
},
Expand Down
29 changes: 29 additions & 0 deletions scripts/check-university-registry.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { readFile } from "node:fs/promises";
import { existsSync } from "node:fs";

const sourceArg = process.argv.find((argument) => argument.startsWith("--source="));
const source =
sourceArg?.slice("--source=".length) ??
(existsSync(".canonical-gapwise/universities.json")
? ".canonical-gapwise/universities.json"
: "../gapwise/universities.json");
const canonical = JSON.parse(await readFile(source, "utf8"));
const local = JSON.parse(await readFile("contracts/supported-universities.json", "utf8"));
const expected = Object.fromEntries(
canonical.universities
.filter((university) => university.status === "supported")
.map((university) => [
university.id,
{ defaultCampus: university.defaultCampus, campuses: university.campuses },
]),
);

if (JSON.stringify(local) !== JSON.stringify(expected)) {
throw new Error(
"contracts/supported-universities.json is out of sync with GapwiseHQ/gapwise universities.json",
);
}

console.log(
`Verified AI university registry for ${Object.keys(local).length} supported universities.`,
);
31 changes: 5 additions & 26 deletions src/domain/public-campus.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,18 @@
import { z } from "zod";
import { getRuntimeConfig } from "@/src/config";
import { GapPreferencesSchema, TermSchema, WeekdaySchema } from "@/src/domain/schemas";
import universityRegistry from "@/contracts/supported-universities.json" with { type: "json" };

export const CANONICAL_UNIVERSITIES = [
"uoft",
"carleton",
"tmu",
"queens",
"laurier",
"york",
"mcmaster",
"western",
"guelph",
"uottawa",
"brock",
] as const;
export const CANONICAL_UNIVERSITIES = Object.keys(universityRegistry) as Array<
keyof typeof universityRegistry
>;

export type CanonicalUniversity = (typeof CANONICAL_UNIVERSITIES)[number];

export const CANONICAL_UNIVERSITY_CAMPUSES: Record<
string,
{ defaultCampus: string; campuses: readonly string[] }
> = {
uoft: { defaultCampus: "utm", campuses: ["utm", "utsg", "utsc"] },
carleton: { defaultCampus: "carleton", campuses: ["carleton"] },
tmu: { defaultCampus: "tmu", campuses: ["tmu"] },
queens: { defaultCampus: "queens", campuses: ["queens"] },
laurier: { defaultCampus: "waterloo", campuses: ["waterloo"] },
york: { defaultCampus: "keele", campuses: ["keele"] },
mcmaster: { defaultCampus: "mcmaster", campuses: ["mcmaster"] },
western: { defaultCampus: "western", campuses: ["western"] },
guelph: { defaultCampus: "guelph", campuses: ["guelph"] },
uottawa: { defaultCampus: "uottawa", campuses: ["uottawa"] },
brock: { defaultCampus: "brock", campuses: ["brock"] },
};
> = universityRegistry;

const VerificationStatusSchema = z.enum(["verified", "inferred", "unknown"]);
const AccessibilitySchema = z.enum(["accessible", "not_accessible", "unknown"]);
Expand Down
4 changes: 2 additions & 2 deletions tests/public-campus.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ describe("public campus intelligence adapter", () => {
});

describe("canonical multi-university programmatic matrix", () => {
it("covers all 11 supported universities in the canonical registry", () => {
expect(CANONICAL_UNIVERSITIES).toHaveLength(11);
it("covers all 13 supported universities in the canonical registry", () => {
expect(CANONICAL_UNIVERSITIES).toHaveLength(13);
for (const u of CANONICAL_UNIVERSITIES) {
const config = CANONICAL_UNIVERSITY_CAMPUSES[u];
expect(config).toBeDefined();
Expand Down
Loading