@rubytech/create-maxy-code 0.1.348 → 0.1.350

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-06-23-account-filesystem-schema.md +544 -0
  3. package/payload/platform/docs/superpowers/specs/2026-06-23-account-filesystem-schema-design.md +170 -0
  4. package/payload/platform/lib/graph-style/dist/index.d.ts +1 -1
  5. package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/graph-style/dist/index.js +20 -12
  7. package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
  8. package/payload/platform/lib/graph-style/src/__tests__/parity.test.ts +9 -9
  9. package/payload/platform/lib/graph-style/src/index.ts +20 -12
  10. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  11. package/payload/platform/lib/graph-write/dist/index.js +3 -2
  12. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  13. package/payload/platform/lib/graph-write/src/index.ts +3 -2
  14. package/payload/platform/neo4j/schema.cypher +110 -31
  15. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  16. package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +68 -0
  17. package/payload/platform/plugins/admin/hooks/fs-schema-guard.sh +108 -0
  18. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
  19. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +36 -3
  20. package/payload/platform/plugins/docs/references/calendar-booking.md +1 -1
  21. package/payload/platform/plugins/docs/references/joblogic.md +28 -0
  22. package/payload/platform/plugins/docs/references/memory-guide.md +1 -1
  23. package/payload/platform/plugins/joblogic/.claude-plugin/plugin.json +21 -0
  24. package/payload/platform/plugins/joblogic/PLUGIN.md +182 -0
  25. package/payload/platform/plugins/joblogic/lib/mcp-spawn-tee/index.js +193 -0
  26. package/payload/platform/plugins/joblogic/lib/mcp-spawn-tee/package.json +3 -0
  27. package/payload/platform/plugins/joblogic/mcp/dist/index.d.ts +2 -0
  28. package/payload/platform/plugins/joblogic/mcp/dist/index.d.ts.map +1 -0
  29. package/payload/platform/plugins/joblogic/mcp/dist/index.js +229 -0
  30. package/payload/platform/plugins/joblogic/mcp/dist/index.js.map +1 -0
  31. package/payload/platform/plugins/joblogic/mcp/dist/lib/auth.d.ts +31 -0
  32. package/payload/platform/plugins/joblogic/mcp/dist/lib/auth.d.ts.map +1 -0
  33. package/payload/platform/plugins/joblogic/mcp/dist/lib/auth.js +78 -0
  34. package/payload/platform/plugins/joblogic/mcp/dist/lib/auth.js.map +1 -0
  35. package/payload/platform/plugins/joblogic/mcp/dist/lib/client.d.ts +35 -0
  36. package/payload/platform/plugins/joblogic/mcp/dist/lib/client.d.ts.map +1 -0
  37. package/payload/platform/plugins/joblogic/mcp/dist/lib/client.js +106 -0
  38. package/payload/platform/plugins/joblogic/mcp/dist/lib/client.js.map +1 -0
  39. package/payload/platform/plugins/joblogic/mcp/dist/lib/idempotency.d.ts +8 -0
  40. package/payload/platform/plugins/joblogic/mcp/dist/lib/idempotency.d.ts.map +1 -0
  41. package/payload/platform/plugins/joblogic/mcp/dist/lib/idempotency.js +41 -0
  42. package/payload/platform/plugins/joblogic/mcp/dist/lib/idempotency.js.map +1 -0
  43. package/payload/platform/plugins/joblogic/mcp/dist/lib/secrets.d.ts +21 -0
  44. package/payload/platform/plugins/joblogic/mcp/dist/lib/secrets.d.ts.map +1 -0
  45. package/payload/platform/plugins/joblogic/mcp/dist/lib/secrets.js +47 -0
  46. package/payload/platform/plugins/joblogic/mcp/dist/lib/secrets.js.map +1 -0
  47. package/payload/platform/plugins/joblogic/mcp/package.json +10 -0
  48. package/payload/platform/plugins/joblogic/skills/joblogic/SKILL.md +32 -0
  49. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.js +28 -22
  50. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.js.map +1 -1
  51. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +8 -4
  52. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js.map +1 -1
  53. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-write-path-task802.test.js +39 -10
  54. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-write-path-task802.test.js.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.d.ts +1 -1
  56. package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.js +1 -1
  57. package/payload/platform/plugins/memory/references/schema-construction.md +152 -57
  58. package/payload/platform/plugins/memory/references/schema-trades.md +22 -24
  59. package/payload/platform/scripts/lib/provision-account-dir.sh +22 -3
  60. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  61. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +46 -0
  62. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  63. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  64. package/payload/platform/services/claude-session-manager/dist/http-server.js +45 -1
  65. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  66. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +11 -0
  67. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  68. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +18 -0
  69. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  70. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +1 -0
  71. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  72. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +16 -0
  73. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  74. package/payload/platform/templates/account-schema/SCHEMA.md +54 -0
  75. package/payload/server/public/assets/{AdminLoginScreens-B_5wYcR-.js → AdminLoginScreens-BEzoTk56.js} +1 -1
  76. package/payload/server/public/assets/{AdminShell-CMV3XY-s.js → AdminShell-BG7YD4UO.js} +1 -1
  77. package/payload/server/public/assets/{Checkbox-LplFUKpb.js → Checkbox-CvTFOczj.js} +1 -1
  78. package/payload/server/public/assets/{OperatorConversations-C4Cx814-.css → OperatorConversations-CnP9Y6g2.css} +1 -1
  79. package/payload/server/public/assets/{admin-3QQtd7j2.js → admin-Bhxa1HOy.js} +1 -1
  80. package/payload/server/public/assets/{browser-D5q26eZL.js → browser-BAQFuyma.js} +1 -1
  81. package/payload/server/public/assets/calendar-sSr6zUjW.js +1 -0
  82. package/payload/server/public/assets/chat-ZH4Fsyu0.js +1 -0
  83. package/payload/server/public/assets/data-CBrgiyPM.js +1 -0
  84. package/payload/server/public/assets/{graph-DFQbqmi9.js → graph-DbGjFYPS.js} +1 -1
  85. package/payload/server/public/assets/graph-labels-IGIEr-uc.js +1 -0
  86. package/payload/server/public/assets/{operator-BlCyq4q1.js → operator-Ba5afDyM.js} +1 -1
  87. package/payload/server/public/assets/{page-drg1V-yA.js → page-D7uwMUOy.js} +1 -1
  88. package/payload/server/public/assets/{public-BVjeYfsD.js → public-DikYWzOd.js} +1 -1
  89. package/payload/server/public/browser.html +4 -4
  90. package/payload/server/public/calendar.html +4 -4
  91. package/payload/server/public/chat.html +5 -5
  92. package/payload/server/public/data.html +4 -4
  93. package/payload/server/public/graph.html +6 -6
  94. package/payload/server/public/index.html +6 -6
  95. package/payload/server/public/operator.html +7 -7
  96. package/payload/server/public/public.html +5 -5
  97. package/payload/server/server.js +92 -31
  98. package/payload/server/public/assets/calendar-Csbdj7rp.js +0 -1
  99. package/payload/server/public/assets/chat-Ct86qtMI.js +0 -1
  100. package/payload/server/public/assets/data-HwT5wPmj.js +0 -1
  101. package/payload/server/public/assets/graph-labels-kCti2CaC.js +0 -1
  102. /package/payload/server/public/assets/{OperatorConversations-Bp-g80PM.js → OperatorConversations-DU8CqO-z.js} +0 -0
@@ -0,0 +1,31 @@
1
+ import type { JlStore, JlEnvironment } from "./secrets.js";
2
+ /** Strip credential-shaped substrings and cap length before an upstream error
3
+ * body is put into a thrown message that may reach a tool response/transcript. */
4
+ export declare function sanitizeErrorBody(text: string): string;
5
+ export declare function identityBaseFor(env: JlEnvironment): string;
6
+ export declare function apiBaseFor(env: JlEnvironment): string;
7
+ export type FetchLike = (url: string, init: {
8
+ method: string;
9
+ headers: Record<string, string>;
10
+ body?: string;
11
+ }) => Promise<{
12
+ ok: boolean;
13
+ status: number;
14
+ text: () => Promise<string>;
15
+ }>;
16
+ export interface Token {
17
+ accessToken: string;
18
+ expiresIn: number;
19
+ }
20
+ export declare function acquireToken(store: JlStore, opts?: {
21
+ fetchImpl?: FetchLike;
22
+ }): Promise<Token>;
23
+ /** Return a valid bearer, refreshing if cached is stale or force=true.
24
+ * reason drives the op=token-refresh line; first acquisition logs op=token-acquire only. */
25
+ export declare function getBearer(store: JlStore, accountId: string, opts?: {
26
+ force?: boolean;
27
+ fetchImpl?: FetchLike;
28
+ }): Promise<string>;
29
+ /** Test seam: clear the per-process cache. */
30
+ export declare function _resetTokenCache(): void;
31
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAa3D;mFACmF;AACnF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAE1D;AACD,wBAAgB,UAAU,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAErD;AAED,MAAM,MAAM,SAAS,GAAG,CACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KACrE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAAC;AAM3E,MAAM,WAAW,KAAK;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,SAAS,CAAA;CAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CA6BvG;AAQD;6FAC6F;AAC7F,wBAAsB,SAAS,CAC7B,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,CAAA;CAAO,GACpD,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,8CAA8C;AAC9C,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
@@ -0,0 +1,78 @@
1
+ const IDENTITY_BASE = {
2
+ uat: "https://uatidentityserver.joblogic.com",
3
+ production: "https://identityserver.joblogic.com",
4
+ };
5
+ const API_BASE = {
6
+ uat: "https://uatapi.joblogic.com",
7
+ production: "https://api.joblogic.com",
8
+ };
9
+ /** Refresh when the cached token is within this window of expiry. */
10
+ const REFRESH_THRESHOLD_MS = 120_000;
11
+ /** Strip credential-shaped substrings and cap length before an upstream error
12
+ * body is put into a thrown message that may reach a tool response/transcript. */
13
+ export function sanitizeErrorBody(text) {
14
+ return text
15
+ .replace(/client_secret=[^&\s"]*/gi, "client_secret=***")
16
+ .replace(/client_id=[^&\s"]*/gi, "client_id=***")
17
+ .replace(/Bearer\s+[A-Za-z0-9._-]+/gi, "Bearer ***")
18
+ .slice(0, 300);
19
+ }
20
+ export function identityBaseFor(env) {
21
+ return IDENTITY_BASE[env];
22
+ }
23
+ export function apiBaseFor(env) {
24
+ return API_BASE[env];
25
+ }
26
+ export async function acquireToken(store, opts = {}) {
27
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
28
+ const url = `${identityBaseFor(store.environment)}/connect/token`;
29
+ const body = new URLSearchParams({
30
+ grant_type: "client_credentials",
31
+ client_id: store.clientId,
32
+ client_secret: store.clientSecret,
33
+ }).toString();
34
+ const started = Date.now();
35
+ const res = await fetchImpl(url, {
36
+ method: "POST",
37
+ headers: { "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json" },
38
+ body,
39
+ });
40
+ const text = await res.text();
41
+ // The token request carried client_secret; some identity servers echo request
42
+ // params in a 4xx body. Strip secrets and cap length before the message can
43
+ // reach a tool response / transcript.
44
+ if (!res.ok)
45
+ throw new Error(`token endpoint ${res.status}: ${sanitizeErrorBody(text)}`);
46
+ let raw;
47
+ try {
48
+ raw = JSON.parse(text);
49
+ }
50
+ catch {
51
+ throw new Error(`token endpoint non-JSON: ${text}`);
52
+ }
53
+ if (!raw.access_token)
54
+ throw new Error(`token endpoint missing access_token: ${text}`);
55
+ const expiresIn = raw.expires_in ?? 3600;
56
+ process.stderr.write(`[joblogic] op=token-acquire ok=true expiresInS=${expiresIn} ms=${Date.now() - started}\n`);
57
+ return { accessToken: raw.access_token, expiresIn };
58
+ }
59
+ const tokenCache = new Map();
60
+ /** Return a valid bearer, refreshing if cached is stale or force=true.
61
+ * reason drives the op=token-refresh line; first acquisition logs op=token-acquire only. */
62
+ export async function getBearer(store, accountId, opts = {}) {
63
+ const cached = tokenCache.get(accountId);
64
+ const fresh = cached && cached.expiresAt - REFRESH_THRESHOLD_MS > Date.now();
65
+ if (fresh && !opts.force)
66
+ return cached.accessToken;
67
+ if (cached) {
68
+ process.stderr.write(`[joblogic] op=token-refresh reason=${opts.force ? "401" : "pre-expiry"} ok=true\n`);
69
+ }
70
+ const tok = await acquireToken(store, { fetchImpl: opts.fetchImpl });
71
+ tokenCache.set(accountId, { accessToken: tok.accessToken, expiresAt: Date.now() + tok.expiresIn * 1000 });
72
+ return tok.accessToken;
73
+ }
74
+ /** Test seam: clear the per-process cache. */
75
+ export function _resetTokenCache() {
76
+ tokenCache.clear();
77
+ }
78
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { JlStore } from "./secrets.js";
2
+ import { type FetchLike } from "./auth.js";
3
+ export type AccessFaultKind = "auth" | "whitelist" | "network";
4
+ export declare function classifyAccessFault(status: number): AccessFaultKind;
5
+ export interface BaseArgs {
6
+ store: JlStore;
7
+ accountId: string;
8
+ method: "GET" | "POST";
9
+ /** path under /api/v1/, e.g. "Customer/GetAll". */
10
+ path: string;
11
+ /** entity label for the observability line. */
12
+ entity: string;
13
+ /** tool name for the observability line. */
14
+ tool: string;
15
+ /** extra query params (merged with tenantId). */
16
+ query?: Record<string, string>;
17
+ /** request body for POST. tenantId is also injected into the body for writes. */
18
+ body?: Record<string, unknown>;
19
+ }
20
+ /** A read or generic call. Performs one silent re-acquire on a 401. */
21
+ export declare function jlRequest(args: BaseArgs, opts?: {
22
+ fetchImpl?: FetchLike;
23
+ }): Promise<unknown>;
24
+ /** A create with idempotency. idemKey lookup short-circuits a duplicate send.
25
+ * platformRoot is threaded explicitly (the ledger lives under the account secrets dir). */
26
+ export interface JlCreateArgs extends Omit<BaseArgs, "method"> {
27
+ platformRoot: string;
28
+ idemKey: string;
29
+ /** field on the response carrying the new record id (default "id"). */
30
+ idField?: string;
31
+ }
32
+ export declare function jlCreate(args: JlCreateArgs, opts?: {
33
+ fetchImpl?: FetchLike;
34
+ }): Promise<unknown>;
35
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAA4C,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAGrF,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;AAE/D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAInE;AAMD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AA2BD,uEAAuE;AACvE,wBAAsB,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,SAAS,CAAA;CAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAoCtG;AAED;4FAC4F;AAC5F,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,SAAS,CAAA;CAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAoCzG"}
@@ -0,0 +1,106 @@
1
+ // JobLogic REST wrapper. Injects Bearer + tenantId on every call, emits the
2
+ // observability lifeline, classifies any 401/403/network failure as an
3
+ // access-fault, and performs exactly one silent token re-acquire on a 401.
4
+ import { randomBytes } from "node:crypto";
5
+ import { apiBaseFor, getBearer, sanitizeErrorBody } from "./auth.js";
6
+ import { lookupIdem, recordIdem } from "./idempotency.js";
7
+ export function classifyAccessFault(status) {
8
+ if (status === 401)
9
+ return "auth";
10
+ if (status === 403)
11
+ return "whitelist";
12
+ return "network"; // status 0 / connect failure
13
+ }
14
+ function reqId() {
15
+ return randomBytes(6).toString("hex");
16
+ }
17
+ async function doFetch(args, accessToken, fetchImpl) {
18
+ const params = new URLSearchParams({ tenantId: args.store.tenantId, ...(args.query ?? {}) });
19
+ const url = `${apiBaseFor(args.store.environment)}/api/v1/${args.path}?${params.toString()}`;
20
+ const headers = { Authorization: `Bearer ${accessToken}`, Accept: "application/json" };
21
+ let bodyStr;
22
+ if (args.body !== undefined) {
23
+ headers["Content-Type"] = "application/json";
24
+ bodyStr = JSON.stringify({ tenantId: args.store.tenantId, ...args.body });
25
+ }
26
+ try {
27
+ const res = await fetchImpl(url, { method: args.method, headers, body: bodyStr });
28
+ return { ok: res.ok, status: res.status, text: await res.text() };
29
+ }
30
+ catch (err) {
31
+ // connect-level failure → network access-fault
32
+ process.stderr.write(`[joblogic] op=access-fault kind=network tool=${args.tool} entity=${args.entity} detail=${err.message}\n`);
33
+ throw new Error(`access-fault kind=network: ${err.message}`);
34
+ }
35
+ }
36
+ /** A read or generic call. Performs one silent re-acquire on a 401. */
37
+ export async function jlRequest(args, opts = {}) {
38
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
39
+ const id = reqId();
40
+ const isWrite = args.method === "POST";
41
+ process.stderr.write(isWrite
42
+ ? `[joblogic] op=mutation reqId=${id} tool=${args.tool} entity=${args.entity} verb=update\n`
43
+ : `[joblogic] op=request reqId=${id} tool=${args.tool} entity=${args.entity}\n`);
44
+ let bearer = await getBearer(args.store, args.accountId, { fetchImpl });
45
+ const started = Date.now();
46
+ let res = await doFetch(args, bearer, fetchImpl);
47
+ if (res.status === 401) {
48
+ bearer = await getBearer(args.store, args.accountId, { force: true, fetchImpl });
49
+ res = await doFetch(args, bearer, fetchImpl);
50
+ }
51
+ const ms = Date.now() - started;
52
+ if (!res.ok) {
53
+ if (res.status === 401 || res.status === 403) {
54
+ process.stderr.write(`[joblogic] op=access-fault kind=${classifyAccessFault(res.status)} tool=${args.tool} entity=${args.entity}\n`);
55
+ }
56
+ process.stderr.write(`[joblogic] op=response reqId=${id} status=${res.status} ms=${ms} rows=-\n`);
57
+ throw new Error(`JobLogic ${args.method} ${args.path} ${res.status}: ${sanitizeErrorBody(res.text)}`);
58
+ }
59
+ let parsed;
60
+ try {
61
+ parsed = JSON.parse(res.text);
62
+ }
63
+ catch {
64
+ throw new Error(`JobLogic ${args.path} non-JSON: ${res.text}`);
65
+ }
66
+ const data = parsed.data;
67
+ const rows = Array.isArray(data) ? data.length : "-";
68
+ process.stderr.write(`[joblogic] op=response reqId=${id} status=${res.status} ms=${ms} rows=${rows}\n`);
69
+ return parsed;
70
+ }
71
+ export async function jlCreate(args, opts = {}) {
72
+ const id = reqId();
73
+ const hit = lookupIdem(args.platformRoot, args.accountId, args.idemKey);
74
+ if (hit) {
75
+ process.stderr.write(`[joblogic] op=idem-hit reqId=${id} tool=${args.tool} existingId=${hit.recordId}\n`);
76
+ return { idempotent: true, id: hit.recordId, entity: hit.entity };
77
+ }
78
+ const result = await jlRequest({
79
+ store: args.store,
80
+ accountId: args.accountId,
81
+ method: "POST",
82
+ path: args.path,
83
+ entity: args.entity,
84
+ tool: args.tool,
85
+ query: args.query,
86
+ body: args.body,
87
+ }, opts);
88
+ const writtenId = extractId(result, args.idField);
89
+ if (!writtenId) {
90
+ // A 2xx with no extractable record id means the ledger cannot be written, so
91
+ // a retry of the same idemKey would duplicate. Fail loudly (the response
92
+ // shape / idField is provisional until UAT-confirmed) rather than silently
93
+ // leaving an idempotency hole.
94
+ process.stderr.write(`[joblogic] op=idem-record-fail reqId=${id} tool=${args.tool} entity=${args.entity} reason=id-unextractable\n`);
95
+ throw new Error(`${args.tool}: create returned success but no '${args.idField ?? "id"}' could be extracted from the response — a retry would NOT be idempotent. Confirm the response shape / idField against UAT.`);
96
+ }
97
+ recordIdem(args.platformRoot, args.accountId, args.idemKey, args.entity, writtenId);
98
+ process.stderr.write(`[joblogic] op=response reqId=${id} status=created id=${writtenId}\n`);
99
+ return result;
100
+ }
101
+ function extractId(result, idField = "id") {
102
+ const r = result;
103
+ const v = r?.[idField] ?? r?.data?.[idField];
104
+ return v == null ? null : String(v);
105
+ }
106
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface IdemRecord {
2
+ entity: string;
3
+ recordId: string;
4
+ ts: number;
5
+ }
6
+ export declare function lookupIdem(platformRoot: string, accountId: string, idemKey: string): IdemRecord | null;
7
+ export declare function recordIdem(platformRoot: string, accountId: string, idemKey: string, entity: string, recordId: string): void;
8
+ //# sourceMappingURL=idempotency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/lib/idempotency.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AAUD,wBAAgB,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAatG;AAED,wBAAgB,UAAU,CACxB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,IAAI,CAKN"}
@@ -0,0 +1,41 @@
1
+ // Local idempotency ledger. JobLogic exposes no idempotency header, so a create
2
+ // retry (tool retry, or a write whose response was lost to a timeout) would
3
+ // duplicate. Each create computes/accepts an idemKey; the ledger maps it to the
4
+ // record id JobLogic returned. A hit short-circuits the send entirely.
5
+ import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { accountSecretsDir } from "./secrets.js";
8
+ /** idemKey filename safety — no path fragments. */
9
+ const KEY_RE = /^[A-Za-z0-9_.:-]+$/;
10
+ function ledgerDir(platformRoot, accountId) {
11
+ return join(accountSecretsDir(platformRoot, accountId), "joblogic-idem");
12
+ }
13
+ function ledgerPath(platformRoot, accountId, idemKey) {
14
+ if (!KEY_RE.test(idemKey))
15
+ throw new Error(`invalid idemKey: ${JSON.stringify(idemKey)}`);
16
+ return join(ledgerDir(platformRoot, accountId), `${idemKey}.json`);
17
+ }
18
+ export function lookupIdem(platformRoot, accountId, idemKey) {
19
+ let file;
20
+ try {
21
+ file = ledgerPath(platformRoot, accountId, idemKey);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ if (!existsSync(file))
27
+ return null;
28
+ try {
29
+ return JSON.parse(readFileSync(file, "utf-8"));
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ export function recordIdem(platformRoot, accountId, idemKey, entity, recordId) {
36
+ const file = ledgerPath(platformRoot, accountId, idemKey);
37
+ mkdirSync(ledgerDir(platformRoot, accountId), { recursive: true });
38
+ const rec = { entity, recordId, ts: Date.now() };
39
+ writeFileSync(file, JSON.stringify(rec), { mode: 0o600 });
40
+ }
41
+ //# sourceMappingURL=idempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../src/lib/idempotency.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAChF,uEAAuE;AACvE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,mDAAmD;AACnD,MAAM,MAAM,GAAG,oBAAoB,CAAC;AAQpC,SAAS,SAAS,CAAC,YAAoB,EAAE,SAAiB;IACxD,OAAO,IAAI,CAAC,iBAAiB,CAAC,YAAY,EAAE,SAAS,CAAC,EAAE,eAAe,CAAC,CAAC;AAC3E,CAAC;AACD,SAAS,UAAU,CAAC,YAAoB,EAAE,SAAiB,EAAE,OAAe;IAC1E,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1F,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,SAAS,CAAC,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,YAAoB,EAAE,SAAiB,EAAE,OAAe;IACjF,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,UAAU,CAAC,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAe,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,YAAoB,EACpB,SAAiB,EACjB,OAAe,EACf,MAAc,EACd,QAAgB;IAEhB,MAAM,IAAI,GAAG,UAAU,CAAC,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC1D,SAAS,CAAC,SAAS,CAAC,YAAY,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,MAAM,GAAG,GAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAC7D,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,21 @@
1
+ export type JlEnvironment = "uat" | "production";
2
+ export interface JlStore {
3
+ clientId: string;
4
+ clientSecret: string;
5
+ /** Company tenant id — required on every JobLogic API call (tenant-scoped credential). */
6
+ tenantId: string;
7
+ environment: JlEnvironment;
8
+ /** Sender phone (E.164) → JobLogic engineer id, for write attribution. Config, not a secret. */
9
+ engineerMap: Record<string, string>;
10
+ }
11
+ export declare function accountSecretsDir(platformRoot: string, accountId: string): string;
12
+ export declare function readStore(platformRoot: string, accountId: string): JlStore | null;
13
+ export declare function setCredentials(platformRoot: string, accountId: string, creds: {
14
+ clientId: string;
15
+ clientSecret: string;
16
+ tenantId: string;
17
+ environment: JlEnvironment;
18
+ }): void;
19
+ export declare function setEngineerMap(platformRoot: string, accountId: string, entries: Record<string, string>): void;
20
+ export declare function resolveEngineer(store: JlStore, senderPhone: string): string | null;
21
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,YAAY,CAAC;AAEjD,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,aAAa,CAAC;IAC3B,gGAAgG;IAChG,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAKD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjF;AAKD,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAUjF;AAOD,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,aAAa,CAAA;CAAE,GAC9F,IAAI,CAGN;AAED,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,IAAI,CAIN;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAElF"}
@@ -0,0 +1,47 @@
1
+ // JobLogic per-account secret store. Plain 0600 JSON in the brand-isolated,
2
+ // delete-protected secrets/ dir (the quickbooks + cloudflare precedent). Never
3
+ // thrown from on read — a missing/corrupt store yields null so module init and
4
+ // tool enumeration survive a no-creds account.
5
+ import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
6
+ import { resolve, join } from "node:path";
7
+ function accountsDir(platformRoot) {
8
+ return resolve(platformRoot, "..", "data/accounts");
9
+ }
10
+ export function accountSecretsDir(platformRoot, accountId) {
11
+ return join(accountsDir(platformRoot), accountId, "secrets");
12
+ }
13
+ function storePath(platformRoot, accountId) {
14
+ return join(accountSecretsDir(platformRoot, accountId), "joblogic.json");
15
+ }
16
+ export function readStore(platformRoot, accountId) {
17
+ const file = storePath(platformRoot, accountId);
18
+ if (!existsSync(file))
19
+ return null;
20
+ try {
21
+ const parsed = JSON.parse(readFileSync(file, "utf-8"));
22
+ if (!parsed.clientId || !parsed.clientSecret || !parsed.tenantId || !parsed.environment)
23
+ return null;
24
+ return { engineerMap: {}, ...parsed };
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ function writeStore(platformRoot, accountId, store) {
31
+ mkdirSync(accountSecretsDir(platformRoot, accountId), { recursive: true });
32
+ writeFileSync(storePath(platformRoot, accountId), JSON.stringify(store, null, 2), { mode: 0o600 });
33
+ }
34
+ export function setCredentials(platformRoot, accountId, creds) {
35
+ const prev = readStore(platformRoot, accountId);
36
+ writeStore(platformRoot, accountId, { ...creds, engineerMap: prev?.engineerMap ?? {} });
37
+ }
38
+ export function setEngineerMap(platformRoot, accountId, entries) {
39
+ const prev = readStore(platformRoot, accountId);
40
+ if (!prev)
41
+ throw new Error("cannot set engineer map before credentials are set");
42
+ writeStore(platformRoot, accountId, { ...prev, engineerMap: { ...prev.engineerMap, ...entries } });
43
+ }
44
+ export function resolveEngineer(store, senderPhone) {
45
+ return store.engineerMap[senderPhone] ?? null;
46
+ }
47
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,+EAA+E;AAC/E,+EAA+E;AAC/E,+CAA+C;AAC/C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAc1C,SAAS,WAAW,CAAC,YAAoB;IACvC,OAAO,OAAO,CAAC,YAAY,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;AACtD,CAAC;AACD,MAAM,UAAU,iBAAiB,CAAC,YAAoB,EAAE,SAAiB;IACvE,OAAO,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;AAC/D,CAAC;AACD,SAAS,SAAS,CAAC,YAAoB,EAAE,SAAiB;IACxD,OAAO,IAAI,CAAC,iBAAiB,CAAC,YAAY,EAAE,SAAS,CAAC,EAAE,eAAe,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,YAAoB,EAAE,SAAiB;IAC/D,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAqB,CAAC;QAC3E,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QACrG,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,GAAG,MAAM,EAAa,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,YAAoB,EAAE,SAAiB,EAAE,KAAc;IACzE,SAAS,CAAC,iBAAiB,CAAC,YAAY,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,aAAa,CAAC,SAAS,CAAC,YAAY,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACrG,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,YAAoB,EACpB,SAAiB,EACjB,KAA+F;IAE/F,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IAChD,UAAU,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,YAAoB,EACpB,SAAiB,EACjB,OAA+B;IAE/B,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACjF,UAAU,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC;AACrG,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc,EAAE,WAAmB;IACjE,OAAO,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC;AAChD,CAAC"}
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@maxy/joblogic",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": { "build": "tsc", "start": "node dist/index.js" },
8
+ "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", "zod": "^3.24.0" },
9
+ "devDependencies": { "typescript": "^5.7.0", "@types/node": "^22.0.0" }
10
+ }
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: joblogic
3
+ description: Connect JobLogic and drive the field-service job lifecycle. Use when the operator wants to link JobLogic, read customers/sites/jobs/visits/quotes/invoices/assets/timesheets, or make writes (create a visit, log job costs, set a job status, raise an invoice) from instructions sent by field staff. Covers the one-time JobLogic Support onboarding and source-IP whitelisting, then the agent-driven read/write surface.
4
+ ---
5
+
6
+ # JobLogic connector
7
+
8
+ JobLogic is the operator's field-service system of record. This connector is bespoke to one account: it does nothing on any other install. The agent reads and writes across the job lifecycle under one company credential. Field staff never call JobLogic themselves; they send a voice note or message to this install, the agent interprets it, and this install makes the write. So only this install's outbound IP ever touches JobLogic.
9
+
10
+ Success is: the credentials are stored, `joblogic-connection-status` acquires a token, and a read such as `joblogic-customer-list` returns the company's data from UAT.
11
+
12
+ ## What the operator does once (relay this; the agent cannot do it)
13
+
14
+ The agent cannot raise a support ticket or whitelist an IP. The operator does this once with JobLogic.
15
+
16
+ - **Raise a JobLogic Support ticket** for API access. Ask for a UAT (test) account first, then production. JobLogic Support issues a `client_id` and `client_secret` (OAuth client_credentials) and tells the operator the company `tenantId`.
17
+ - **Whitelist the install's outbound source IP.** JobLogic filters API calls by the public IP they originate from. The install's inbound tunnel does not fix the outbound IP, and a laptop's public IP is usually dynamic. The operator resolves one of: run the install from one location with a stable ISP IP and register that IP; route the connector's outbound through a fixed-egress proxy with a static IP and register that; or confirm with JobLogic that production drops the per-IP filter. Until one holds, calls fail and the logs show `op=access-fault kind=whitelist`.
18
+
19
+ ## What the agent does after that
20
+
21
+ - **Store the credentials.** Run `joblogic-credentials-set` with the `client_id`, `client_secret`, `tenantId`, and `environment` (`uat` first). They are written only to this account's secrets file, never echoed.
22
+ - **Confirm it works.** Run `joblogic-connection-status`. It acquires a token and reports the environment and tenant. If it fails, the logs distinguish an auth problem from a whitelist problem from a network blip.
23
+ - **Set up attribution.** The company credential is not per-user, so JobLogic will not auto-attribute a write to the engineer who reported it. Run `joblogic-engineer-map-set` to map each field staffer's phone number to their JobLogic engineer id. When a write comes from a message, pass the sender's phone so the connector stamps the right engineer on it.
24
+ - **Read and write.** Reads cover customers, sites, jobs, engineers, visits, quotes, invoices, assets, and timesheets. Writes cover creating and updating those records and the lifecycle transitions: deploy a visit, log job costs, set a job status, approve a quote, raise an invoice, add a note or attachment.
25
+
26
+ ## Authorization and safety
27
+
28
+ A write is authorized by the person who messaged the install asking for it. That instruction is the authorization; the agent does not interpose a second confirmation step. The one write safety that always applies is idempotency: every create takes an idempotency key, so a retry or a write whose response was lost does not create a duplicate record. Derive the key deterministically from the write's natural details so the same instruction maps to the same key.
29
+
30
+ ## Use UAT for all testing
31
+
32
+ Every read and write test runs against the UAT environment, never production. Switch `environment` to `production` only once the operator confirms UAT behaves correctly and production credentials and IP whitelisting are in place.
@@ -11,11 +11,11 @@ Some table here.
11
11
 
12
12
  \`\`\`
13
13
  (:Job)-[:HAS_VALUATION]->(:Valuation)
14
- (:Job)-[:HAS_QUOTE]->(:QuoteDocument)
15
- (:QuoteDocument)-[:HAS_LINE_ITEM]->(:LineItem)
16
- (:Valuation)-[:VALUES]->(:LineItem)
17
- (:Job)-[:FOR_CLIENT]->(:Person)
18
- (:InboundInvoice)-[:ASSIGNED_TO]->(:Job)
14
+ (:Job)-[:HAS_QUOTE]->(:Quote)
15
+ (:Quote)-[:HAS_LINE]->(:QuoteLine)
16
+ (:Valuation)-[:VALUES]->(:QuoteLine)
17
+ (:Job)-[:FOR_CUSTOMER]->(:Customer)
18
+ (:Invoice)-[:ON_PURCHASE_ORDER]->(:PurchaseOrder)
19
19
  \`\`\`
20
20
 
21
21
  Some trailing prose with an arrow (:Job)-[:NOT_A_PATTERN]->(:Thing) outside the block.
@@ -37,15 +37,15 @@ describe("parseRelationshipPatterns", () => {
37
37
  sourceFile: "schema-construction.md",
38
38
  });
39
39
  expect(out).toContainEqual({
40
- source: "QuoteDocument",
41
- edgeType: "HAS_LINE_ITEM",
42
- target: "LineItem",
40
+ source: "Quote",
41
+ edgeType: "HAS_LINE",
42
+ target: "QuoteLine",
43
43
  sourceFile: "schema-construction.md",
44
44
  });
45
45
  expect(out).toContainEqual({
46
46
  source: "Valuation",
47
47
  edgeType: "VALUES",
48
- target: "LineItem",
48
+ target: "QuoteLine",
49
49
  sourceFile: "schema-construction.md",
50
50
  });
51
51
  // The arrow outside the fenced block must NOT be parsed.
@@ -88,7 +88,7 @@ describe("checkEdgeAgainstPatterns (warn-only semantics)", () => {
88
88
  governed: true,
89
89
  allowed: true,
90
90
  });
91
- expect(checkEdgeAgainstPatterns(["QuoteDocument"], "HAS_LINE_ITEM", patterns)).toEqual({
91
+ expect(checkEdgeAgainstPatterns(["Quote"], "HAS_LINE", patterns)).toEqual({
92
92
  governed: true,
93
93
  allowed: true,
94
94
  });
@@ -110,20 +110,20 @@ describe("checkEdgeAgainstPatterns (warn-only semantics)", () => {
110
110
  describe("real schema-construction.md (Phase 1 + Phase 2 integration)", () => {
111
111
  const md = readFileSync(resolve(import.meta.dirname, "../../../../references/schema-construction.md"), "utf-8");
112
112
  const patterns = parseRelationshipPatterns(md, "schema-construction.md");
113
- it("owns line items on the QuoteDocument + adds VALUES, never (:Job)-[:HAS_LINE_ITEM]", () => {
113
+ it("owns lines on the Quote + adds VALUES, never (:Job)-[:HAS_LINE]", () => {
114
114
  expect(patterns).toContainEqual({
115
- source: "QuoteDocument",
116
- edgeType: "HAS_LINE_ITEM",
117
- target: "LineItem",
115
+ source: "Quote",
116
+ edgeType: "HAS_LINE",
117
+ target: "QuoteLine",
118
118
  sourceFile: "schema-construction.md",
119
119
  });
120
120
  expect(patterns).toContainEqual({
121
121
  source: "Valuation",
122
122
  edgeType: "VALUES",
123
- target: "LineItem",
123
+ target: "QuoteLine",
124
124
  sourceFile: "schema-construction.md",
125
125
  });
126
- expect(patterns.find((p) => p.source === "Job" && p.edgeType === "HAS_LINE_ITEM")).toBeUndefined();
126
+ expect(patterns.find((p) => p.source === "Job" && p.edgeType === "HAS_LINE")).toBeUndefined();
127
127
  });
128
128
  it("the observed drift edges are off-allowlist, the canonical ones pass", () => {
129
129
  expect(checkEdgeAgainstPatterns(["Job"], "JOB_FOR", patterns).allowed).toBe(false);
@@ -131,15 +131,21 @@ describe("real schema-construction.md (Phase 1 + Phase 2 integration)", () => {
131
131
  expect(checkEdgeAgainstPatterns(["Job"], "HAS_VALUATION", patterns).allowed).toBe(true);
132
132
  expect(checkEdgeAgainstPatterns(["Job"], "HAS_QUOTE", patterns).allowed).toBe(true);
133
133
  });
134
- it("adopts the timesheet field-worker ontology: (:Worker)-[:LOGGED]->(:CheckIn)", () => {
134
+ it("adopts the JobLogic cost ontology: (:Job)-[:HAS_COST]->(:JobCost)-[:INCURRED_BY]->(:Engineer)", () => {
135
135
  expect(patterns).toContainEqual({
136
- source: "Worker",
137
- edgeType: "LOGGED",
138
- target: "CheckIn",
136
+ source: "Job",
137
+ edgeType: "HAS_COST",
138
+ target: "JobCost",
139
+ sourceFile: "schema-construction.md",
140
+ });
141
+ expect(patterns).toContainEqual({
142
+ source: "JobCost",
143
+ edgeType: "INCURRED_BY",
144
+ target: "Engineer",
139
145
  sourceFile: "schema-construction.md",
140
146
  });
141
- // The edge is anchored on the construction-only Worker label, so a base
142
- // :Person write is never newly governed by it.
147
+ // :Engineer is an additional label on :Person; the construction edges are
148
+ // anchored on :Engineer, so a base :Person write is never newly governed.
143
149
  expect(patterns.find((p) => p.source === "Person")).toBeUndefined();
144
150
  });
145
151
  });
@@ -1 +1 @@
1
- {"version":3,"file":"relationship-patterns.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/relationship-patterns.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relationship-patterns.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/relationship-patterns.test.ts"],"names":[],"mappings":"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"}
@@ -23,10 +23,14 @@ const PROSE_ONLY_LABELS = new Set([
23
23
  // schema-estate-agent.md treats Applicant as an additional label on a
24
24
  // base :Person node; there is no standalone :Applicant.
25
25
  "Applicant",
26
- // schema-construction.md treats Worker as an additional config label on a
27
- // base :Person node (the timesheet field-worker model); there is no
28
- // standalone :Worker, so it carries no constraint/index of its own.
29
- "Worker",
26
+ // schema-construction.md treats Engineer as an additional label on a base
27
+ // :Person node (the field-engineer / CIS worker-pay model); there is no
28
+ // standalone :Engineer, so it carries no constraint/index of its own.
29
+ "Engineer",
30
+ // schema-construction.md treats Supplier as an additional label on a base
31
+ // :Organization node (the supplier-invoice model); there is no standalone
32
+ // :Supplier, so it carries no constraint/index of its own.
33
+ "Supplier",
30
34
  // Email is retired: Task 321 folded threads onto :KnowledgeDocument
31
35
  // {source:'email'}, Task 426 then moved them onto :ConversationArchive
32
36
  // {source:'email'}. The :Email label never reaches the graph; it is