@rigour-labs/core 6.2.4 → 6.3.0
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.
- package/README.md +2 -0
- package/dist/deep/code-context.d.ts +28 -0
- package/dist/deep/code-context.js +93 -0
- package/dist/deep/code-context.test.d.ts +1 -0
- package/dist/deep/code-context.test.js +48 -0
- package/dist/deep/code-pass.d.ts +15 -0
- package/dist/deep/code-pass.js +44 -0
- package/dist/deep/code-review-prompt.d.ts +17 -0
- package/dist/deep/code-review-prompt.js +30 -0
- package/dist/deep/code-review-prompt.test.d.ts +1 -0
- package/dist/deep/code-review-prompt.test.js +20 -0
- package/dist/deep/code-verifier.d.ts +19 -0
- package/dist/deep/code-verifier.js +48 -0
- package/dist/deep/code-verifier.test.d.ts +1 -0
- package/dist/deep/code-verifier.test.js +46 -0
- package/dist/deep/fact-extractor.d.ts +7 -1
- package/dist/deep/fact-extractor.js +16 -8
- package/dist/deep/fact-extractor.test.js +34 -1
- package/dist/deep/facts-pass.d.ts +24 -0
- package/dist/deep/facts-pass.js +69 -0
- package/dist/deep/index.d.ts +6 -1
- package/dist/deep/index.js +5 -1
- package/dist/deep/parse-findings.d.ts +12 -0
- package/dist/deep/parse-findings.js +73 -0
- package/dist/deep/parse-findings.test.d.ts +1 -0
- package/dist/deep/parse-findings.test.js +25 -0
- package/dist/deep/prompts.d.ts +1 -1
- package/dist/deep/prompts.js +7 -8
- package/dist/deep/prompts.test.js +15 -0
- package/dist/discovery-signals.d.ts +27 -0
- package/dist/discovery-signals.js +96 -0
- package/dist/discovery.d.ts +1 -2
- package/dist/discovery.js +36 -49
- package/dist/discovery.test.js +84 -71
- package/dist/gates/context.js +7 -0
- package/dist/gates/deep-analysis.d.ts +33 -8
- package/dist/gates/deep-analysis.js +169 -306
- package/dist/gates/deep-analysis.test.d.ts +1 -0
- package/dist/gates/deep-analysis.test.js +86 -0
- package/dist/gates/deep-intent.d.ts +3 -0
- package/dist/gates/deep-intent.js +57 -0
- package/dist/gates/deep-intent.test.d.ts +1 -0
- package/dist/gates/deep-intent.test.js +51 -0
- package/dist/gates/deep-runner.d.ts +15 -0
- package/dist/gates/deep-runner.js +66 -0
- package/dist/gates/dependency.js +4 -0
- package/dist/gates/deprecated-apis-ast.d.ts +8 -0
- package/dist/gates/deprecated-apis-ast.js +25 -0
- package/dist/gates/deprecated-apis-rules-node.js +2 -8
- package/dist/gates/deprecated-apis.js +5 -1
- package/dist/gates/deprecated-apis.test.js +30 -0
- package/dist/gates/frontend-secret-exposure.js +5 -1
- package/dist/gates/frontend-secret-exposure.test.js +20 -0
- package/dist/gates/hallucinated-imports/framework-modules.d.ts +14 -0
- package/dist/gates/hallucinated-imports/framework-modules.js +75 -0
- package/dist/gates/hallucinated-imports/index.js +14 -6
- package/dist/gates/hallucinated-imports/js-resolver.d.ts +16 -12
- package/dist/gates/hallucinated-imports/js-resolver.js +65 -153
- package/dist/gates/hallucinated-imports/ts-path-target.js +16 -0
- package/dist/gates/hallucinated-imports/tsconfig-paths.d.ts +16 -0
- package/dist/gates/hallucinated-imports/tsconfig-paths.js +120 -0
- package/dist/gates/hallucinated-imports-sveltekit.test.d.ts +1 -0
- package/dist/gates/hallucinated-imports-sveltekit.test.js +108 -0
- package/dist/gates/logic-drift.js +7 -1
- package/dist/gates/runner.js +10 -40
- package/dist/gates/runner.test.js +37 -25
- package/dist/gates/scoped-gates.test.d.ts +1 -0
- package/dist/gates/scoped-gates.test.js +52 -0
- package/dist/gates/semantic-bugs.d.ts +23 -0
- package/dist/gates/semantic-bugs.js +48 -0
- package/dist/gates/semantic-bugs.test.d.ts +1 -0
- package/dist/gates/semantic-bugs.test.js +70 -0
- package/dist/gates/side-effect-analysis/index.js +11 -1
- package/dist/gates/side-effect-analysis.test.d.ts +1 -0
- package/dist/gates/side-effect-analysis.test.js +162 -0
- package/dist/gates/side-effect-helpers/pattern-detection.js +35 -7
- package/dist/gates/side-effect-helpers/resource-tracking.js +17 -10
- package/dist/gates/side-effect-helpers/scope-analysis.d.ts +6 -0
- package/dist/gates/side-effect-helpers/scope-analysis.js +20 -0
- package/dist/gates/style-drift.js +3 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +10 -2
- package/dist/inference/http-download.d.ts +22 -0
- package/dist/inference/http-download.js +132 -0
- package/dist/inference/http-download.test.d.ts +1 -0
- package/dist/inference/http-download.test.js +109 -0
- package/dist/inference/index.d.ts +5 -1
- package/dist/inference/index.js +2 -1
- package/dist/inference/llama-engine.d.ts +27 -0
- package/dist/inference/llama-engine.js +152 -0
- package/dist/inference/llama-engine.test.d.ts +1 -0
- package/dist/inference/llama-engine.test.js +51 -0
- package/dist/inference/llama-process.d.ts +72 -0
- package/dist/inference/llama-process.js +126 -0
- package/dist/inference/llama-process.test.d.ts +1 -0
- package/dist/inference/llama-process.test.js +61 -0
- package/dist/inference/model-download.test.d.ts +1 -0
- package/dist/inference/model-download.test.js +73 -0
- package/dist/inference/model-manager.d.ts +14 -1
- package/dist/inference/model-manager.js +65 -80
- package/dist/inference/sidecar-provider.d.ts +19 -10
- package/dist/inference/sidecar-provider.js +125 -342
- package/dist/inference/types.d.ts +2 -0
- package/dist/semantic/ast.d.ts +29 -0
- package/dist/semantic/ast.js +122 -0
- package/dist/semantic/benchmark.d.ts +16 -0
- package/dist/semantic/benchmark.js +31 -0
- package/dist/semantic/benchmark.test.d.ts +1 -0
- package/dist/semantic/benchmark.test.js +18 -0
- package/dist/semantic/engine.d.ts +8 -0
- package/dist/semantic/engine.js +45 -0
- package/dist/semantic/intent/benchmark.d.ts +31 -0
- package/dist/semantic/intent/benchmark.js +74 -0
- package/dist/semantic/intent/intent-check.d.ts +43 -0
- package/dist/semantic/intent/intent-check.js +72 -0
- package/dist/semantic/intent/intent.test.d.ts +1 -0
- package/dist/semantic/intent/intent.test.js +46 -0
- package/dist/semantic/intent/parallel-reads.d.ts +23 -0
- package/dist/semantic/intent/parallel-reads.js +59 -0
- package/dist/semantic/learn/benchmark.d.ts +20 -0
- package/dist/semantic/learn/benchmark.js +71 -0
- package/dist/semantic/learn/compile.d.ts +4 -0
- package/dist/semantic/learn/compile.js +56 -0
- package/dist/semantic/learn/conditions.d.ts +22 -0
- package/dist/semantic/learn/conditions.js +93 -0
- package/dist/semantic/learn/extract.d.ts +30 -0
- package/dist/semantic/learn/extract.js +122 -0
- package/dist/semantic/learn/git-trees.d.ts +8 -0
- package/dist/semantic/learn/git-trees.js +32 -0
- package/dist/semantic/learn/identity.d.ts +18 -0
- package/dist/semantic/learn/identity.js +97 -0
- package/dist/semantic/learn/learn.d.ts +25 -0
- package/dist/semantic/learn/learn.js +109 -0
- package/dist/semantic/learn/learn.test.d.ts +1 -0
- package/dist/semantic/learn/learn.test.js +67 -0
- package/dist/semantic/learn/store.d.ts +4 -0
- package/dist/semantic/learn/store.js +39 -0
- package/dist/semantic/learn/types.d.ts +468 -0
- package/dist/semantic/learn/types.js +55 -0
- package/dist/semantic/origins.d.ts +42 -0
- package/dist/semantic/origins.js +149 -0
- package/dist/semantic/origins.test.d.ts +1 -0
- package/dist/semantic/origins.test.js +77 -0
- package/dist/semantic/program.d.ts +13 -0
- package/dist/semantic/program.js +62 -0
- package/dist/semantic/rules/credential-redirect.d.ts +2 -0
- package/dist/semantic/rules/credential-redirect.js +209 -0
- package/dist/semantic/rules/degraded-response-cached.d.ts +2 -0
- package/dist/semantic/rules/degraded-response-cached.js +271 -0
- package/dist/semantic/rules/in-memory-aggregation.d.ts +2 -0
- package/dist/semantic/rules/in-memory-aggregation.js +179 -0
- package/dist/semantic/summaries.d.ts +18 -0
- package/dist/semantic/summaries.js +33 -0
- package/dist/semantic/types.d.ts +32 -0
- package/dist/semantic/types.js +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/team-diagnostics.d.ts +22 -0
- package/dist/storage/team-diagnostics.js +58 -0
- package/dist/storage/team-diagnostics.test.d.ts +1 -0
- package/dist/storage/team-diagnostics.test.js +51 -0
- package/dist/storage/team-schema.d.ts +6 -0
- package/dist/storage/team-schema.js +82 -0
- package/dist/storage/team-store.js +25 -72
- package/dist/templates/universal-config.js +4 -2
- package/dist/types/index.d.ts +114 -14
- package/dist/types/index.js +16 -2
- package/dist/utils/deep-status.d.ts +7 -0
- package/dist/utils/deep-status.js +11 -0
- package/dist/utils/diff.d.ts +3 -0
- package/dist/utils/diff.js +52 -0
- package/dist/utils/diff.test.d.ts +1 -0
- package/dist/utils/diff.test.js +38 -0
- package/dist/utils/scanner.js +13 -2
- package/dist/utils/scanner.test.js +22 -8
- package/dist/utils/scope.d.ts +15 -0
- package/dist/utils/scope.js +51 -0
- package/dist/utils/scope.test.d.ts +1 -0
- package/dist/utils/scope.test.js +38 -0
- package/package.json +6 -6
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { diagnoseMissingMembership, explainTeamConnectionError } from './team-diagnostics.js';
|
|
3
|
+
import { TEAM_SCHEMA } from './team-schema.js';
|
|
4
|
+
function pool(rows) {
|
|
5
|
+
return {
|
|
6
|
+
query: async () => {
|
|
7
|
+
if (rows instanceof Error)
|
|
8
|
+
throw rows;
|
|
9
|
+
return { rows, rowCount: rows.length };
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
describe('explainTeamConnectionError', () => {
|
|
14
|
+
it('explains an untrusted certificate chain with the CA fix', () => {
|
|
15
|
+
const message = explainTeamConnectionError(Object.assign(new Error('self-signed certificate in certificate chain'), { code: 'SELF_SIGNED_CERT_IN_CHAIN' }));
|
|
16
|
+
expect(message).toContain('NODE_EXTRA_CA_CERTS');
|
|
17
|
+
expect(message).toContain('sslmode=verify-full');
|
|
18
|
+
});
|
|
19
|
+
it('points a missing schema at init-schema', () => {
|
|
20
|
+
expect(explainTeamConnectionError({ code: '42P01', message: 'relation "rigour.memberships" does not exist' })).toContain('rigour team init-schema');
|
|
21
|
+
});
|
|
22
|
+
it('explains a rejected login and an unreachable host', () => {
|
|
23
|
+
expect(explainTeamConnectionError({ code: '28P01', message: 'password authentication failed' })).toContain('rejected the login');
|
|
24
|
+
expect(explainTeamConnectionError({ code: 'ECONNREFUSED', message: 'connect ECONNREFUSED' })).toContain('Cannot reach');
|
|
25
|
+
});
|
|
26
|
+
it('passes other errors through', () => {
|
|
27
|
+
expect(explainTeamConnectionError(new Error('something else'))).toBe('something else');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('diagnoseMissingMembership', () => {
|
|
31
|
+
it('names row-level security without a policy as the cause', async () => {
|
|
32
|
+
const message = await diagnoseMissingMembership(pool([{ table_name: 'memberships' }, { table_name: 'meta' }]));
|
|
33
|
+
expect(message).toContain('rigour.memberships and rigour.meta');
|
|
34
|
+
expect(message).toContain('init-schema');
|
|
35
|
+
});
|
|
36
|
+
it('reports a missing membership when nothing hides it', async () => {
|
|
37
|
+
expect(await diagnoseMissingMembership(pool([]))).toContain('no membership');
|
|
38
|
+
});
|
|
39
|
+
it('falls back to the membership message when the catalog cannot be read', async () => {
|
|
40
|
+
expect(await diagnoseMissingMembership(pool(new Error('permission denied')))).toContain('no membership');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('team schema', () => {
|
|
44
|
+
it('protects every team table with row-level security and a read policy', () => {
|
|
45
|
+
for (const table of ['lessons', 'memberships', 'meta']) {
|
|
46
|
+
expect(TEAM_SCHEMA).toContain(`ALTER TABLE rigour.${table} ENABLE ROW LEVEL SECURITY;`);
|
|
47
|
+
}
|
|
48
|
+
expect(TEAM_SCHEMA).toContain('CREATE POLICY memberships_read_own ON rigour.memberships FOR SELECT USING (db_role = current_user);');
|
|
49
|
+
expect(TEAM_SCHEMA).toContain('CREATE POLICY meta_read ON rigour.meta FOR SELECT USING (true);');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL team schema. Idempotent: re-running it on an existing database
|
|
3
|
+
* (`rigour team init-schema`) adds anything missing, including the RLS
|
|
4
|
+
* policies, without touching data.
|
|
5
|
+
*/
|
|
6
|
+
export declare const TEAM_SCHEMA = "\nCREATE SCHEMA IF NOT EXISTS rigour;\nCREATE TABLE IF NOT EXISTS rigour.meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\nCREATE TABLE IF NOT EXISTS rigour.memberships (\n db_role NAME NOT NULL,\n organization_id TEXT NOT NULL,\n team_id TEXT NOT NULL,\n actor_id TEXT NOT NULL,\n role TEXT NOT NULL CHECK (role IN ('member', 'sme', 'owner')),\n PRIMARY KEY (db_role, team_id)\n);\nCREATE TABLE IF NOT EXISTS rigour.lessons (\n id TEXT PRIMARY KEY,\n organization_id TEXT NOT NULL,\n team_id TEXT NOT NULL,\n repository_id TEXT NOT NULL,\n actor_id TEXT NOT NULL,\n visibility TEXT NOT NULL CHECK (visibility IN ('personal', 'team')),\n state TEXT NOT NULL CHECK (state IN ('candidate', 'validated', 'promoted', 'rejected', 'superseded')),\n kind TEXT NOT NULL,\n subject TEXT NOT NULL,\n evidence_json JSONB NOT NULL,\n confidence DOUBLE PRECISION NOT NULL,\n source TEXT NOT NULL,\n supersedes_id TEXT,\n created_at BIGINT NOT NULL,\n updated_at BIGINT NOT NULL\n);\nALTER TABLE rigour.lessons ENABLE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS lessons_read ON rigour.lessons;\nCREATE POLICY lessons_read ON rigour.lessons FOR SELECT USING (\n EXISTS (\n SELECT 1 FROM rigour.memberships membership\n WHERE membership.db_role = current_user\n AND membership.organization_id = lessons.organization_id\n AND membership.team_id = lessons.team_id\n AND ((lessons.visibility = 'team' AND lessons.state = 'promoted')\n OR membership.actor_id = lessons.actor_id)\n )\n);\nDROP POLICY IF EXISTS lessons_write ON rigour.lessons;\nCREATE POLICY lessons_write ON rigour.lessons FOR ALL USING (\n EXISTS (\n SELECT 1 FROM rigour.memberships membership\n WHERE membership.db_role = current_user\n AND membership.organization_id = lessons.organization_id\n AND membership.team_id = lessons.team_id\n AND membership.actor_id = lessons.actor_id\n AND (lessons.visibility = 'personal' OR membership.role IN ('sme', 'owner'))\n )\n) WITH CHECK (\n EXISTS (\n SELECT 1 FROM rigour.memberships membership\n WHERE membership.db_role = current_user\n AND membership.organization_id = lessons.organization_id\n AND membership.team_id = lessons.team_id\n AND membership.actor_id = lessons.actor_id\n AND (lessons.visibility = 'personal' OR membership.role IN ('sme', 'owner'))\n )\n);\n-- Row-level security on every team table. A host (for example Supabase's\n-- security advisor) may enable RLS on its own; without a policy each member\n-- login would then see no rows at all. Members read only their own\n-- membership rows (the lessons policies read memberships the same way), and\n-- meta holds only the schema version.\nALTER TABLE rigour.memberships ENABLE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS memberships_read_own ON rigour.memberships;\nCREATE POLICY memberships_read_own ON rigour.memberships FOR SELECT USING (db_role = current_user);\nALTER TABLE rigour.meta ENABLE ROW LEVEL SECURITY;\nDROP POLICY IF EXISTS meta_read ON rigour.meta;\nCREATE POLICY meta_read ON rigour.meta FOR SELECT USING (true);\nINSERT INTO rigour.meta (key, value) VALUES ('schema_version', '1')\nON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;\n";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL team schema. Idempotent: re-running it on an existing database
|
|
3
|
+
* (`rigour team init-schema`) adds anything missing, including the RLS
|
|
4
|
+
* policies, without touching data.
|
|
5
|
+
*/
|
|
6
|
+
export const TEAM_SCHEMA = `
|
|
7
|
+
CREATE SCHEMA IF NOT EXISTS rigour;
|
|
8
|
+
CREATE TABLE IF NOT EXISTS rigour.meta (
|
|
9
|
+
key TEXT PRIMARY KEY,
|
|
10
|
+
value TEXT NOT NULL
|
|
11
|
+
);
|
|
12
|
+
CREATE TABLE IF NOT EXISTS rigour.memberships (
|
|
13
|
+
db_role NAME NOT NULL,
|
|
14
|
+
organization_id TEXT NOT NULL,
|
|
15
|
+
team_id TEXT NOT NULL,
|
|
16
|
+
actor_id TEXT NOT NULL,
|
|
17
|
+
role TEXT NOT NULL CHECK (role IN ('member', 'sme', 'owner')),
|
|
18
|
+
PRIMARY KEY (db_role, team_id)
|
|
19
|
+
);
|
|
20
|
+
CREATE TABLE IF NOT EXISTS rigour.lessons (
|
|
21
|
+
id TEXT PRIMARY KEY,
|
|
22
|
+
organization_id TEXT NOT NULL,
|
|
23
|
+
team_id TEXT NOT NULL,
|
|
24
|
+
repository_id TEXT NOT NULL,
|
|
25
|
+
actor_id TEXT NOT NULL,
|
|
26
|
+
visibility TEXT NOT NULL CHECK (visibility IN ('personal', 'team')),
|
|
27
|
+
state TEXT NOT NULL CHECK (state IN ('candidate', 'validated', 'promoted', 'rejected', 'superseded')),
|
|
28
|
+
kind TEXT NOT NULL,
|
|
29
|
+
subject TEXT NOT NULL,
|
|
30
|
+
evidence_json JSONB NOT NULL,
|
|
31
|
+
confidence DOUBLE PRECISION NOT NULL,
|
|
32
|
+
source TEXT NOT NULL,
|
|
33
|
+
supersedes_id TEXT,
|
|
34
|
+
created_at BIGINT NOT NULL,
|
|
35
|
+
updated_at BIGINT NOT NULL
|
|
36
|
+
);
|
|
37
|
+
ALTER TABLE rigour.lessons ENABLE ROW LEVEL SECURITY;
|
|
38
|
+
DROP POLICY IF EXISTS lessons_read ON rigour.lessons;
|
|
39
|
+
CREATE POLICY lessons_read ON rigour.lessons FOR SELECT USING (
|
|
40
|
+
EXISTS (
|
|
41
|
+
SELECT 1 FROM rigour.memberships membership
|
|
42
|
+
WHERE membership.db_role = current_user
|
|
43
|
+
AND membership.organization_id = lessons.organization_id
|
|
44
|
+
AND membership.team_id = lessons.team_id
|
|
45
|
+
AND ((lessons.visibility = 'team' AND lessons.state = 'promoted')
|
|
46
|
+
OR membership.actor_id = lessons.actor_id)
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
DROP POLICY IF EXISTS lessons_write ON rigour.lessons;
|
|
50
|
+
CREATE POLICY lessons_write ON rigour.lessons FOR ALL USING (
|
|
51
|
+
EXISTS (
|
|
52
|
+
SELECT 1 FROM rigour.memberships membership
|
|
53
|
+
WHERE membership.db_role = current_user
|
|
54
|
+
AND membership.organization_id = lessons.organization_id
|
|
55
|
+
AND membership.team_id = lessons.team_id
|
|
56
|
+
AND membership.actor_id = lessons.actor_id
|
|
57
|
+
AND (lessons.visibility = 'personal' OR membership.role IN ('sme', 'owner'))
|
|
58
|
+
)
|
|
59
|
+
) WITH CHECK (
|
|
60
|
+
EXISTS (
|
|
61
|
+
SELECT 1 FROM rigour.memberships membership
|
|
62
|
+
WHERE membership.db_role = current_user
|
|
63
|
+
AND membership.organization_id = lessons.organization_id
|
|
64
|
+
AND membership.team_id = lessons.team_id
|
|
65
|
+
AND membership.actor_id = lessons.actor_id
|
|
66
|
+
AND (lessons.visibility = 'personal' OR membership.role IN ('sme', 'owner'))
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
-- Row-level security on every team table. A host (for example Supabase's
|
|
70
|
+
-- security advisor) may enable RLS on its own; without a policy each member
|
|
71
|
+
-- login would then see no rows at all. Members read only their own
|
|
72
|
+
-- membership rows (the lessons policies read memberships the same way), and
|
|
73
|
+
-- meta holds only the schema version.
|
|
74
|
+
ALTER TABLE rigour.memberships ENABLE ROW LEVEL SECURITY;
|
|
75
|
+
DROP POLICY IF EXISTS memberships_read_own ON rigour.memberships;
|
|
76
|
+
CREATE POLICY memberships_read_own ON rigour.memberships FOR SELECT USING (db_role = current_user);
|
|
77
|
+
ALTER TABLE rigour.meta ENABLE ROW LEVEL SECURITY;
|
|
78
|
+
DROP POLICY IF EXISTS meta_read ON rigour.meta;
|
|
79
|
+
CREATE POLICY meta_read ON rigour.meta FOR SELECT USING (true);
|
|
80
|
+
INSERT INTO rigour.meta (key, value) VALUES ('schema_version', '1')
|
|
81
|
+
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
|
|
82
|
+
`;
|
|
@@ -3,6 +3,8 @@ import os from 'os';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { openDatabase } from './db.js';
|
|
5
5
|
import { decryptLocalPayload, encryptLocalPayload } from './local-encryption.js';
|
|
6
|
+
import { TEAM_SCHEMA } from './team-schema.js';
|
|
7
|
+
import { diagnoseMissingMembership, explainTeamConnectionError } from './team-diagnostics.js';
|
|
6
8
|
import { TEAM_VECTOR_SCHEMA, backfillConfiguredTeamEmbeddings, getTeamSemanticHealth, searchConfiguredTeamKnowledge, upsertTeamLessonEmbedding, } from './team-vector-store.js';
|
|
7
9
|
const DEFAULT_SEMANTIC = {
|
|
8
10
|
provider: 'pgvector',
|
|
@@ -84,72 +86,6 @@ async function queuedChanges() {
|
|
|
84
86
|
await db.close();
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
|
-
const TEAM_SCHEMA = `
|
|
88
|
-
CREATE SCHEMA IF NOT EXISTS rigour;
|
|
89
|
-
CREATE TABLE IF NOT EXISTS rigour.meta (
|
|
90
|
-
key TEXT PRIMARY KEY,
|
|
91
|
-
value TEXT NOT NULL
|
|
92
|
-
);
|
|
93
|
-
CREATE TABLE IF NOT EXISTS rigour.memberships (
|
|
94
|
-
db_role NAME NOT NULL,
|
|
95
|
-
organization_id TEXT NOT NULL,
|
|
96
|
-
team_id TEXT NOT NULL,
|
|
97
|
-
actor_id TEXT NOT NULL,
|
|
98
|
-
role TEXT NOT NULL CHECK (role IN ('member', 'sme', 'owner')),
|
|
99
|
-
PRIMARY KEY (db_role, team_id)
|
|
100
|
-
);
|
|
101
|
-
CREATE TABLE IF NOT EXISTS rigour.lessons (
|
|
102
|
-
id TEXT PRIMARY KEY,
|
|
103
|
-
organization_id TEXT NOT NULL,
|
|
104
|
-
team_id TEXT NOT NULL,
|
|
105
|
-
repository_id TEXT NOT NULL,
|
|
106
|
-
actor_id TEXT NOT NULL,
|
|
107
|
-
visibility TEXT NOT NULL CHECK (visibility IN ('personal', 'team')),
|
|
108
|
-
state TEXT NOT NULL CHECK (state IN ('candidate', 'validated', 'promoted', 'rejected', 'superseded')),
|
|
109
|
-
kind TEXT NOT NULL,
|
|
110
|
-
subject TEXT NOT NULL,
|
|
111
|
-
evidence_json JSONB NOT NULL,
|
|
112
|
-
confidence DOUBLE PRECISION NOT NULL,
|
|
113
|
-
source TEXT NOT NULL,
|
|
114
|
-
supersedes_id TEXT,
|
|
115
|
-
created_at BIGINT NOT NULL,
|
|
116
|
-
updated_at BIGINT NOT NULL
|
|
117
|
-
);
|
|
118
|
-
ALTER TABLE rigour.lessons ENABLE ROW LEVEL SECURITY;
|
|
119
|
-
DROP POLICY IF EXISTS lessons_read ON rigour.lessons;
|
|
120
|
-
CREATE POLICY lessons_read ON rigour.lessons FOR SELECT USING (
|
|
121
|
-
EXISTS (
|
|
122
|
-
SELECT 1 FROM rigour.memberships membership
|
|
123
|
-
WHERE membership.db_role = current_user
|
|
124
|
-
AND membership.organization_id = lessons.organization_id
|
|
125
|
-
AND membership.team_id = lessons.team_id
|
|
126
|
-
AND ((lessons.visibility = 'team' AND lessons.state = 'promoted')
|
|
127
|
-
OR membership.actor_id = lessons.actor_id)
|
|
128
|
-
)
|
|
129
|
-
);
|
|
130
|
-
DROP POLICY IF EXISTS lessons_write ON rigour.lessons;
|
|
131
|
-
CREATE POLICY lessons_write ON rigour.lessons FOR ALL USING (
|
|
132
|
-
EXISTS (
|
|
133
|
-
SELECT 1 FROM rigour.memberships membership
|
|
134
|
-
WHERE membership.db_role = current_user
|
|
135
|
-
AND membership.organization_id = lessons.organization_id
|
|
136
|
-
AND membership.team_id = lessons.team_id
|
|
137
|
-
AND membership.actor_id = lessons.actor_id
|
|
138
|
-
AND (lessons.visibility = 'personal' OR membership.role IN ('sme', 'owner'))
|
|
139
|
-
)
|
|
140
|
-
) WITH CHECK (
|
|
141
|
-
EXISTS (
|
|
142
|
-
SELECT 1 FROM rigour.memberships membership
|
|
143
|
-
WHERE membership.db_role = current_user
|
|
144
|
-
AND membership.organization_id = lessons.organization_id
|
|
145
|
-
AND membership.team_id = lessons.team_id
|
|
146
|
-
AND membership.actor_id = lessons.actor_id
|
|
147
|
-
AND (lessons.visibility = 'personal' OR membership.role IN ('sme', 'owner'))
|
|
148
|
-
)
|
|
149
|
-
);
|
|
150
|
-
INSERT INTO rigour.meta (key, value) VALUES ('schema_version', '1')
|
|
151
|
-
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;
|
|
152
|
-
`;
|
|
153
89
|
export async function initializeTeamSchema(databaseUrl, options = {}) {
|
|
154
90
|
validateTeamDatabaseUrl(databaseUrl);
|
|
155
91
|
const { Pool } = await loadPg();
|
|
@@ -171,7 +107,7 @@ export async function getTeamModeStatus() {
|
|
|
171
107
|
}
|
|
172
108
|
try {
|
|
173
109
|
const result = await doctorTeamConnection(config);
|
|
174
|
-
return result;
|
|
110
|
+
return result.connectivity === 'offline' ? { ...result, message: `Offline — changes queued. ${result.message}` } : result;
|
|
175
111
|
}
|
|
176
112
|
catch (error) {
|
|
177
113
|
return {
|
|
@@ -190,7 +126,21 @@ export async function doctorTeamConnection(config) {
|
|
|
190
126
|
if (!resolved?.databaseUrl) {
|
|
191
127
|
return { mode: 'local', connectivity: 'local', queuedChanges: await queuedChanges(), message: 'Team mode is not configured.' };
|
|
192
128
|
}
|
|
193
|
-
|
|
129
|
+
const offline = async (message) => ({
|
|
130
|
+
mode: 'team',
|
|
131
|
+
connectivity: 'offline',
|
|
132
|
+
organizationId: resolved.organizationId,
|
|
133
|
+
teamId: resolved.teamId,
|
|
134
|
+
actorId: resolved.actorId,
|
|
135
|
+
queuedChanges: await queuedChanges(),
|
|
136
|
+
message,
|
|
137
|
+
});
|
|
138
|
+
try {
|
|
139
|
+
validateTeamDatabaseUrl(resolved.databaseUrl);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
return offline(explainTeamConnectionError(error));
|
|
143
|
+
}
|
|
194
144
|
const { Pool } = await loadPg();
|
|
195
145
|
const pool = new Pool({ connectionString: resolved.databaseUrl });
|
|
196
146
|
try {
|
|
@@ -200,10 +150,10 @@ export async function doctorTeamConnection(config) {
|
|
|
200
150
|
FROM rigour.memberships
|
|
201
151
|
WHERE db_role = current_user AND team_id = $1 AND actor_id = $2 AND organization_id = $3`, [resolved.teamId, resolved.actorId, resolved.organizationId]);
|
|
202
152
|
if (result.rowCount !== 1)
|
|
203
|
-
|
|
153
|
+
return offline(await diagnoseMissingMembership(pool));
|
|
204
154
|
const schemaVersion = Number(result.rows[0].schema_version);
|
|
205
155
|
if (schemaVersion !== 1)
|
|
206
|
-
|
|
156
|
+
return offline(`Incompatible team schema version ${schemaVersion}; expected 1.`);
|
|
207
157
|
const semantic = resolved.semantic ? await getTeamSemanticHealth(pool, resolved) : undefined;
|
|
208
158
|
return {
|
|
209
159
|
mode: 'team',
|
|
@@ -219,8 +169,11 @@ export async function doctorTeamConnection(config) {
|
|
|
219
169
|
message: 'PostgreSQL team mode is healthy.',
|
|
220
170
|
};
|
|
221
171
|
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
return offline(explainTeamConnectionError(error));
|
|
174
|
+
}
|
|
222
175
|
finally {
|
|
223
|
-
await pool.end();
|
|
176
|
+
await pool.end().catch(() => undefined);
|
|
224
177
|
}
|
|
225
178
|
}
|
|
226
179
|
export async function syncTeamOutbox(options = {}) {
|
|
@@ -243,7 +196,7 @@ export async function syncTeamOutbox(options = {}) {
|
|
|
243
196
|
WHERE db_role = current_user AND team_id = $1
|
|
244
197
|
AND actor_id = $2 AND organization_id = $3`, [config.teamId, config.actorId, config.organizationId]);
|
|
245
198
|
if (membership.rowCount !== 1)
|
|
246
|
-
throw new Error(
|
|
199
|
+
throw new Error(await diagnoseMissingMembership(pool));
|
|
247
200
|
for (const item of pending) {
|
|
248
201
|
const lesson = await decryptLocalPayload(item.payload_json);
|
|
249
202
|
try {
|
|
@@ -234,6 +234,9 @@ export const UNIVERSAL_CONFIG = {
|
|
|
234
234
|
track_branches: true,
|
|
235
235
|
track_returns: true,
|
|
236
236
|
},
|
|
237
|
+
semantic_bugs: {
|
|
238
|
+
enabled: false,
|
|
239
|
+
},
|
|
237
240
|
side_effect_analysis: {
|
|
238
241
|
enabled: true,
|
|
239
242
|
check_unbounded_timers: true,
|
|
@@ -251,9 +254,8 @@ export const UNIVERSAL_CONFIG = {
|
|
|
251
254
|
pro: false,
|
|
252
255
|
provider: 'local',
|
|
253
256
|
threads: 4,
|
|
254
|
-
max_tokens: 512,
|
|
255
257
|
temperature: 0.1,
|
|
256
|
-
|
|
258
|
+
intent_checks: false,
|
|
257
259
|
checks: {
|
|
258
260
|
solid: true,
|
|
259
261
|
dry: true,
|