speclock 4.5.0 → 4.5.2
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/package.json +1 -1
- package/src/cli/index.js +1 -1
- package/src/core/compliance.js +1 -1
- package/src/core/memory.js +6 -10
- package/src/core/semantics.js +43 -4
- package/src/core/telemetry.js +1 -1
- package/src/dashboard/index.html +2 -2
- package/src/mcp/http-server.js +1 -1
- package/src/mcp/server.js +1 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
"name": "speclock",
|
|
4
4
|
|
|
5
|
-
"version": "4.5.
|
|
5
|
+
"version": "4.5.2",
|
|
6
6
|
|
|
7
7
|
"description": "AI constraint engine with Gemini LLM universal detection, Policy-as-Code DSL, OAuth/OIDC SSO, admin dashboard, telemetry, API key auth, RBAC, AES-256-GCM encryption, hard enforcement, semantic pre-commit, HMAC audit chain, SOC 2/HIPAA compliance. Cross-platform: MCP + direct API. 31 MCP tools + CLI. Enterprise platform.",
|
|
8
8
|
|
package/src/cli/index.js
CHANGED
|
@@ -117,7 +117,7 @@ function refreshContext(root) {
|
|
|
117
117
|
|
|
118
118
|
function printHelp() {
|
|
119
119
|
console.log(`
|
|
120
|
-
SpecLock v4.5.
|
|
120
|
+
SpecLock v4.5.2 — AI Constraint Engine (Gemini LLM + Policy-as-Code + SSO + Dashboard + Telemetry + Auth + RBAC + Encryption)
|
|
121
121
|
Developed by Sandeep Roy (github.com/sgroy10)
|
|
122
122
|
|
|
123
123
|
Usage: speclock <command> [options]
|
package/src/core/compliance.js
CHANGED
package/src/core/memory.js
CHANGED
|
@@ -84,32 +84,28 @@ export function addLock(root, text, tags, source) {
|
|
|
84
84
|
const brain = ensureInit(root);
|
|
85
85
|
const lockId = newId("lock");
|
|
86
86
|
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
// Store the user's exact words — no rewriting.
|
|
88
|
+
// The semantic engine handles verb contamination via subject extraction
|
|
89
|
+
// and scope matching, so rewriting is no longer needed.
|
|
90
90
|
brain.specLock.items.unshift({
|
|
91
91
|
id: lockId,
|
|
92
|
-
text:
|
|
93
|
-
originalText: normResult.wasRewritten ? normResult.original : undefined,
|
|
92
|
+
text: text,
|
|
94
93
|
createdAt: nowIso(),
|
|
95
94
|
source: source || "user",
|
|
96
95
|
tags: tags || [],
|
|
97
96
|
active: true,
|
|
98
97
|
});
|
|
99
98
|
const eventId = newId("evt");
|
|
100
|
-
const rewriteNote = normResult.wasRewritten
|
|
101
|
-
? ` (auto-rewritten from: "${normResult.original.substring(0, 60)}")`
|
|
102
|
-
: "";
|
|
103
99
|
const event = {
|
|
104
100
|
eventId,
|
|
105
101
|
type: "lock_added",
|
|
106
102
|
at: nowIso(),
|
|
107
103
|
files: [],
|
|
108
|
-
summary: `Lock added: ${
|
|
104
|
+
summary: `Lock added: ${text.substring(0, 80)}`,
|
|
109
105
|
patchPath: "",
|
|
110
106
|
};
|
|
111
107
|
recordEvent(root, brain, event);
|
|
112
|
-
return { brain, lockId, rewritten:
|
|
108
|
+
return { brain, lockId, rewritten: false, rewriteReason: null };
|
|
113
109
|
}
|
|
114
110
|
|
|
115
111
|
export function removeLock(root, lockId) {
|
package/src/core/semantics.js
CHANGED
|
@@ -28,7 +28,7 @@ export const SYNONYM_GROUPS = [
|
|
|
28
28
|
|
|
29
29
|
// --- Modification actions ---
|
|
30
30
|
["change", "modify", "alter", "update", "mutate", "transform",
|
|
31
|
-
"rewrite", "revise", "amend", "adjust", "tweak"],
|
|
31
|
+
"rewrite", "revise", "amend", "adjust", "tweak", "touch", "tamper"],
|
|
32
32
|
["replace", "swap", "substitute", "switch", "exchange",
|
|
33
33
|
"override", "overwrite"],
|
|
34
34
|
["move", "relocate", "migrate", "transfer", "shift", "rearrange", "reorganize",
|
|
@@ -1372,6 +1372,13 @@ function _extractSubjectsInline(text) {
|
|
|
1372
1372
|
content = content.replace(/\s+must\s+(?:be\s+)?(?:preserved|remain)\b.*$/i, "").trim();
|
|
1373
1373
|
content = content.replace(/\s*[—–]\s+(?:prohibited|no\s+|must\s+not|deletion|do\s+not|migration)\b.*$/i, "").trim();
|
|
1374
1374
|
|
|
1375
|
+
// Strip comma-separated explanatory clauses
|
|
1376
|
+
// "KYC verification flow, it's SEC-compliant" → "KYC verification flow"
|
|
1377
|
+
// "patient records, which are HIPAA-protected" → "patient records"
|
|
1378
|
+
// "the auth system, because it's production-critical" → "the auth system"
|
|
1379
|
+
content = content.replace(/,\s+(?:it|they|that|this|which|who)\s*(?:'s|'re|is|are|was|were|has|have|had)\b.*$/i, "").trim();
|
|
1380
|
+
content = content.replace(/,\s+(?:because|since|as|for|due\s+to|given\s+that)\b.*$/i, "").trim();
|
|
1381
|
+
|
|
1375
1382
|
// Strip leading verb
|
|
1376
1383
|
const words = content.split(/\s+/);
|
|
1377
1384
|
let startIdx = 0;
|
|
@@ -1964,10 +1971,34 @@ export function scoreConflict({ actionText, lockText }) {
|
|
|
1964
1971
|
}
|
|
1965
1972
|
}
|
|
1966
1973
|
|
|
1974
|
+
// Check 3b: Safe/verification verbs against preservation/maintenance locks
|
|
1975
|
+
// "Test that Stripe is working" is COMPLIANT with "must always use Stripe"
|
|
1976
|
+
// "Debug the Stripe webhook" is COMPLIANT — it's verifying the preserved system
|
|
1977
|
+
if (!intentAligned && actionPrimaryVerb) {
|
|
1978
|
+
const lockIsPreservation = /must remain|must be preserved|must always|at all times|must stay/i.test(lockText);
|
|
1979
|
+
|
|
1980
|
+
if (lockIsPreservation) {
|
|
1981
|
+
const SAFE_FOR_PRESERVATION = new Set([
|
|
1982
|
+
"test", "verify", "check", "validate", "confirm", "ensure",
|
|
1983
|
+
"debug", "inspect", "review", "examine", "monitor", "observe",
|
|
1984
|
+
"watch", "scan", "detect", "audit", "report", "document",
|
|
1985
|
+
"read", "view", "generate", "fix", "repair", "patch",
|
|
1986
|
+
"protect", "secure", "guard", "maintain", "preserve",
|
|
1987
|
+
]);
|
|
1988
|
+
if (SAFE_FOR_PRESERVATION.has(actionPrimaryVerb)) {
|
|
1989
|
+
intentAligned = true;
|
|
1990
|
+
reasons.push(
|
|
1991
|
+
`intent alignment: verification/maintenance "${actionPrimaryVerb}" is ` +
|
|
1992
|
+
`compliant with preservation lock`);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1967
1997
|
// Check 4: Enhancement/constructive actions against preservation/maintenance locks
|
|
1968
1998
|
// "Increase the rate limit" is COMPLIANT with "rate limiting must remain active"
|
|
1969
1999
|
// "Add better rate limit error messages" is COMPLIANT (doesn't disable rate limiting)
|
|
1970
2000
|
// But "Add a way to bypass rate limiting" is NOT safe (contains negative op "bypass")
|
|
2001
|
+
// And "Add Razorpay" vs "must always use Stripe" is NOT safe (competing alternative)
|
|
1971
2002
|
if (!intentAligned && actionPrimaryVerb) {
|
|
1972
2003
|
const ENHANCEMENT_VERBS = new Set([
|
|
1973
2004
|
"increase", "improve", "enhance", "boost", "strengthen",
|
|
@@ -1986,15 +2017,23 @@ export function scoreConflict({ actionText, lockText }) {
|
|
|
1986
2017
|
`intent alignment: enhancement action "${actionPrimaryVerb}" is ` +
|
|
1987
2018
|
`compliant with preservation lock`);
|
|
1988
2019
|
} else if (CONSTRUCTIVE_FOR_PRESERVATION.has(actionPrimaryVerb)) {
|
|
1989
|
-
// Constructive verbs align ONLY if
|
|
2020
|
+
// Constructive verbs align ONLY if:
|
|
2021
|
+
// 1. No negative operations in the action
|
|
2022
|
+
// 2. The action doesn't introduce a COMPETING alternative
|
|
2023
|
+
// "Add Razorpay" vs "must always use Stripe" → competitor (same synonym group)
|
|
2024
|
+
// "Add dark mode" vs "must always use Stripe" → unrelated (safe)
|
|
1990
2025
|
const actionLower = actionText.toLowerCase();
|
|
1991
2026
|
const hasNegativeOp = NEGATIVE_INTENT_MARKERS.some(m =>
|
|
1992
2027
|
new RegExp(`\\b${escapeRegex(m)}\\b`, "i").test(actionLower));
|
|
1993
|
-
if
|
|
2028
|
+
// Check if action introduces a competing product/brand from the same category
|
|
2029
|
+
const hasCompetitorMatch = subjectComparison.matchedSubjects.some(m =>
|
|
2030
|
+
typeof m === "string" && m.startsWith("synonym:")
|
|
2031
|
+
);
|
|
2032
|
+
if (!hasNegativeOp && !hasCompetitorMatch) {
|
|
1994
2033
|
intentAligned = true;
|
|
1995
2034
|
reasons.push(
|
|
1996
2035
|
`intent alignment: constructive "${actionPrimaryVerb}" is ` +
|
|
1997
|
-
`compliant with preservation lock (no negative operations)`);
|
|
2036
|
+
`compliant with preservation lock (no negative operations, no competitor)`);
|
|
1998
2037
|
}
|
|
1999
2038
|
}
|
|
2000
2039
|
}
|
package/src/core/telemetry.js
CHANGED
|
@@ -257,7 +257,7 @@ export async function flushToRemote(root) {
|
|
|
257
257
|
// Build anonymized payload
|
|
258
258
|
const payload = {
|
|
259
259
|
instanceId: summary.instanceId,
|
|
260
|
-
version: "4.5.
|
|
260
|
+
version: "4.5.2",
|
|
261
261
|
totalCalls: summary.totalCalls,
|
|
262
262
|
avgResponseMs: summary.avgResponseMs,
|
|
263
263
|
conflicts: summary.conflicts,
|
package/src/dashboard/index.html
CHANGED
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
<div class="header">
|
|
90
90
|
<div>
|
|
91
91
|
<h1><span>SpecLock</span> Dashboard</h1>
|
|
92
|
-
<div class="meta">v4.5.
|
|
92
|
+
<div class="meta">v4.5.2 — AI Constraint Engine</div>
|
|
93
93
|
</div>
|
|
94
94
|
<div style="display:flex;align-items:center;gap:12px;">
|
|
95
95
|
<span id="health-badge" class="status-badge healthy">Loading...</span>
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
</div>
|
|
183
183
|
|
|
184
184
|
<div style="text-align:center;padding:24px;color:var(--muted);font-size:12px;">
|
|
185
|
-
SpecLock v4.5.
|
|
185
|
+
SpecLock v4.5.2 — Developed by Sandeep Roy — <a href="https://github.com/sgroy10/speclock" style="color:var(--accent)">GitHub</a>
|
|
186
186
|
</div>
|
|
187
187
|
|
|
188
188
|
<script>
|
package/src/mcp/http-server.js
CHANGED
|
@@ -91,7 +91,7 @@ import { fileURLToPath } from "url";
|
|
|
91
91
|
import _path from "path";
|
|
92
92
|
|
|
93
93
|
const PROJECT_ROOT = process.env.SPECLOCK_PROJECT_ROOT || process.cwd();
|
|
94
|
-
const VERSION = "4.5.
|
|
94
|
+
const VERSION = "4.5.2";
|
|
95
95
|
const AUTHOR = "Sandeep Roy";
|
|
96
96
|
const START_TIME = Date.now();
|
|
97
97
|
|
package/src/mcp/server.js
CHANGED
|
@@ -100,7 +100,7 @@ const PROJECT_ROOT =
|
|
|
100
100
|
args.project || process.env.SPECLOCK_PROJECT_ROOT || process.cwd();
|
|
101
101
|
|
|
102
102
|
// --- MCP Server ---
|
|
103
|
-
const VERSION = "4.5.
|
|
103
|
+
const VERSION = "4.5.2";
|
|
104
104
|
const AUTHOR = "Sandeep Roy";
|
|
105
105
|
|
|
106
106
|
const server = new McpServer(
|