@skillstech/thunderlang 0.1.6
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/CHANGELOG.md +375 -0
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/dist/core.cjs +6768 -0
- package/dist/index.cjs +9308 -0
- package/intent-graph.schema.json +687 -0
- package/package.json +84 -0
- package/src/ai-core.mjs +67 -0
- package/src/ai-events.mjs +56 -0
- package/src/ai.mjs +324 -0
- package/src/arch.mjs +55 -0
- package/src/atlas.mjs +118 -0
- package/src/changes.mjs +74 -0
- package/src/classification.mjs +36 -0
- package/src/cli.mjs +1534 -0
- package/src/codegen.mjs +214 -0
- package/src/compile.mjs +142 -0
- package/src/comprehension.mjs +130 -0
- package/src/conflict.mjs +0 -0
- package/src/core.d.ts +99 -0
- package/src/core.mjs +92 -0
- package/src/data-schema.mjs +137 -0
- package/src/decision.mjs +38 -0
- package/src/distributed.mjs +48 -0
- package/src/draft.mjs +101 -0
- package/src/drift.mjs +177 -0
- package/src/emit.mjs +519 -0
- package/src/exporters.mjs +245 -0
- package/src/expr.mjs +245 -0
- package/src/fable.mjs +110 -0
- package/src/focus.mjs +151 -0
- package/src/format.mjs +55 -0
- package/src/governance.mjs +100 -0
- package/src/graph-source.mjs +292 -0
- package/src/guard.mjs +105 -0
- package/src/guardian.mjs +98 -0
- package/src/hash.mjs +89 -0
- package/src/importers.mjs +194 -0
- package/src/index.d.ts +725 -0
- package/src/index.mjs +185 -0
- package/src/intellisense.mjs +210 -0
- package/src/intent-atlas.mjs +77 -0
- package/src/intent-graph.mjs +346 -0
- package/src/intent-ir.mjs +144 -0
- package/src/intent-schema.mjs +215 -0
- package/src/ledger.mjs +109 -0
- package/src/lifecycle.mjs +56 -0
- package/src/lift.mjs +693 -0
- package/src/lsp.mjs +152 -0
- package/src/mcp.mjs +158 -0
- package/src/migrate.mjs +118 -0
- package/src/outcome.mjs +93 -0
- package/src/parse.mjs +733 -0
- package/src/patch.mjs +364 -0
- package/src/privacy.mjs +61 -0
- package/src/proof-schema.mjs +129 -0
- package/src/report.mjs +84 -0
- package/src/runtime.mjs +96 -0
- package/src/sarif.mjs +88 -0
- package/src/scan-queries.mjs +97 -0
- package/src/scan.mjs +87 -0
- package/src/security.mjs +73 -0
- package/src/select.mjs +80 -0
- package/src/semantic-diff.mjs +125 -0
- package/src/simulate.mjs +106 -0
- package/src/style.mjs +250 -0
- package/src/sync.mjs +103 -0
- package/src/testing.mjs +59 -0
- package/src/twelve-factor.mjs +173 -0
- package/src/verify-diff.mjs +105 -0
- package/src/xml.mjs +87 -0
- package/syntaxes/intent.tmLanguage.json +55 -0
package/src/atlas.mjs
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Mission Atlas indexing: aggregate many .intent files into one inventory.
|
|
2
|
+
//
|
|
3
|
+
// This is the deterministic slice of the Atlas. It reports only what is derivable
|
|
4
|
+
// from the .intent files themselves: mission, feature area, a risk heuristic,
|
|
5
|
+
// guarantee/never counts, and DECLARED verification (whether verify tests exist).
|
|
6
|
+
//
|
|
7
|
+
// It deliberately does NOT report test pass counts, proof, or drift. Those need a
|
|
8
|
+
// test runner and OpenThunder (repo evidence), which the compiler does not own.
|
|
9
|
+
// Those columns of the Proof Matrix stay evidence-dependent. See docs/proof-matrix.
|
|
10
|
+
|
|
11
|
+
import { parseIntent, slug } from './parse.mjs';
|
|
12
|
+
import { sha256 } from './emit.mjs';
|
|
13
|
+
|
|
14
|
+
// Feature area convention: an explicit `# area: X` comment wins; otherwise the
|
|
15
|
+
// header comment `# <Name>.intent , <Product> / <Area>` is used; else uncategorized.
|
|
16
|
+
function deriveArea(source) {
|
|
17
|
+
const explicit = source.match(/^#\s*area:\s*(.+)$/im);
|
|
18
|
+
if (explicit) return explicit[1].trim();
|
|
19
|
+
const header = source.match(/^#[^\n]*\.intent[^\n]*?\/\s*(.+?)\s*$/im);
|
|
20
|
+
if (header) return header[1].trim();
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const RISK_PATTERNS = {
|
|
25
|
+
payments: /\b(payment|charge|invoice|checkout|subscription|billing|refund)\b/i,
|
|
26
|
+
auth: /\b(password|login|auth|session|credential|sign[- ]?in)\b/i,
|
|
27
|
+
secret: /\b(secret|token)\b/i,
|
|
28
|
+
pii: /\b(email|pii|personal data|address|phone)\b/i,
|
|
29
|
+
deployment: /\b(rollback|migration|deploy)\b/i,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// The risk field is a HEURISTIC. To avoid prose noise it scans only structured
|
|
33
|
+
// signal: the mission name, guarantee and never statements, and input names/types.
|
|
34
|
+
function deriveRisk(ast) {
|
|
35
|
+
const signal = [
|
|
36
|
+
ast.mission,
|
|
37
|
+
...ast.guarantees.map((g) => g.statement),
|
|
38
|
+
...ast.neverRules.map((n) => n.statement),
|
|
39
|
+
...ast.inputs.map((i) => `${i.name} ${i.type}`),
|
|
40
|
+
].filter(Boolean).join(' ');
|
|
41
|
+
|
|
42
|
+
const factors = [];
|
|
43
|
+
for (const [name, re] of Object.entries(RISK_PATTERNS)) if (re.test(signal)) factors.push(name);
|
|
44
|
+
if (ast.inputs.some((i) => /secret/i.test(i.type)) && !factors.includes('secret')) factors.push('secret');
|
|
45
|
+
|
|
46
|
+
const high = factors.includes('payments') || factors.includes('deployment')
|
|
47
|
+
|| (factors.includes('secret') && factors.includes('auth'));
|
|
48
|
+
const level = high ? 'high'
|
|
49
|
+
: (factors.includes('secret') || factors.includes('pii') || factors.includes('auth') || factors.length >= 2) ? 'medium'
|
|
50
|
+
: 'low';
|
|
51
|
+
return { level, factors };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// DECLARED verification: how many verify tests the mission states, vs how many
|
|
55
|
+
// guarantees + never rules it makes. "declared" is not "passing"; proof needs evidence.
|
|
56
|
+
function deriveVerification(ast) {
|
|
57
|
+
const rules = ast.guarantees.length + ast.neverRules.length;
|
|
58
|
+
const verifyTests = ast.verify.length
|
|
59
|
+
+ ast.guarantees.reduce((n, g) => n + (g.verify ? g.verify.length : 0), 0)
|
|
60
|
+
+ ast.neverRules.reduce((n, r) => n + (r.verify ? r.verify.length : 0), 0);
|
|
61
|
+
let verification = 'none';
|
|
62
|
+
if (verifyTests > 0) verification = verifyTests >= rules ? 'declared-full' : 'declared-partial';
|
|
63
|
+
return { verification, verifyTests };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Build a mission index from parsed .intent files.
|
|
68
|
+
* @param {{path?: string, source: string}[]} files
|
|
69
|
+
* @param {{product?: string}} [opts]
|
|
70
|
+
*/
|
|
71
|
+
export function buildMissionIndex(files, opts = {}) {
|
|
72
|
+
const missions = files.map(({ path: p, source }) => {
|
|
73
|
+
const ast = parseIntent(source);
|
|
74
|
+
const { verification, verifyTests } = deriveVerification(ast);
|
|
75
|
+
const { level, factors } = deriveRisk(ast);
|
|
76
|
+
return {
|
|
77
|
+
// Stable join keys for downstream consumers (e.g. OpenThunder coverage/drift).
|
|
78
|
+
// missionId is the compiler's deterministic mission slug; intentProofHash is the
|
|
79
|
+
// same sha256 the .intent-proof.json carries as sourceHash, so joins need no remap.
|
|
80
|
+
missionId: ast.mission ? slug(ast.mission) : null,
|
|
81
|
+
mission: ast.mission || null,
|
|
82
|
+
intentProofHash: sha256(source),
|
|
83
|
+
file: p || null,
|
|
84
|
+
area: deriveArea(source),
|
|
85
|
+
risk: level,
|
|
86
|
+
riskFactors: factors,
|
|
87
|
+
guarantees: ast.guarantees.length,
|
|
88
|
+
neverRules: ast.neverRules.length,
|
|
89
|
+
verifyTests,
|
|
90
|
+
verification,
|
|
91
|
+
reviewed: ast.approval?.reviewed === true,
|
|
92
|
+
};
|
|
93
|
+
}).filter((m) => m.mission);
|
|
94
|
+
|
|
95
|
+
missions.sort((a, b) => a.mission.localeCompare(b.mission));
|
|
96
|
+
|
|
97
|
+
const byArea = {};
|
|
98
|
+
for (const m of missions) {
|
|
99
|
+
const a = m.area || 'uncategorized';
|
|
100
|
+
byArea[a] = (byArea[a] || 0) + 1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
schema: 'mission-index-v1',
|
|
105
|
+
generatedBy: 'intent index',
|
|
106
|
+
product: opts.product || null,
|
|
107
|
+
note: 'Verification is DECLARED (verify tests present), not proven. Test pass counts and drift require a test runner and OpenThunder; they are not in this index.',
|
|
108
|
+
missions,
|
|
109
|
+
summary: {
|
|
110
|
+
missions: missions.length,
|
|
111
|
+
byArea,
|
|
112
|
+
declaredFull: missions.filter((m) => m.verification === 'declared-full').length,
|
|
113
|
+
declaredPartial: missions.filter((m) => m.verification === 'declared-partial').length,
|
|
114
|
+
unverified: missions.filter((m) => m.verification === 'none').length,
|
|
115
|
+
highRisk: missions.filter((m) => m.risk === 'high').length,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
package/src/changes.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Change Lens (intent-changes-v1) , what a branch / PR / commit range changed, by MEANING, not
|
|
2
|
+
// lines. Given the before/after graphs of each changed .intent file, it aggregates the semantic
|
|
3
|
+
// diff (reusing diffGraphs), interprets the behavior-level changes (a guarantee/never/invariant
|
|
4
|
+
// added or removed, verification removed, approval invalidated), flags regressions, and returns
|
|
5
|
+
// the set of touched nodes to seed a Focus Graph. Pure (the CLI does the git I/O); browser-safe.
|
|
6
|
+
|
|
7
|
+
import { diffGraphs } from './semantic-diff.mjs';
|
|
8
|
+
|
|
9
|
+
export const CHANGES_SCHEMA = 'intent-changes-v1';
|
|
10
|
+
|
|
11
|
+
const EMPTY = { nodes: [], relationships: [] };
|
|
12
|
+
// The node kinds whose add/remove is a meaningful behavior change worth surfacing.
|
|
13
|
+
const SIGNIFICANT = new Set(['Mission', 'Guarantee', 'Never', 'Invariant', 'Constraint', 'VerificationRule', 'Decision', 'Event', 'Api', 'Outcome', 'OutcomeContract']);
|
|
14
|
+
// Removing one of these is a regression risk (a promise or its proof was taken away).
|
|
15
|
+
const REGRESSION_ON_REMOVE = new Set(['Guarantee', 'Never', 'Invariant', 'VerificationRule']);
|
|
16
|
+
|
|
17
|
+
const verbFor = (type) => {
|
|
18
|
+
switch (type) {
|
|
19
|
+
case 'Guarantee': return 'guarantee';
|
|
20
|
+
case 'Never': return 'never-rule';
|
|
21
|
+
case 'Invariant': return 'invariant';
|
|
22
|
+
case 'VerificationRule': return 'verification';
|
|
23
|
+
case 'Decision': return 'decision';
|
|
24
|
+
case 'Event': return 'event';
|
|
25
|
+
case 'Outcome': case 'OutcomeContract': return 'outcome';
|
|
26
|
+
default: return type.toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build a Change Report from before/after graphs of each changed file.
|
|
32
|
+
* @param {Array<{path:string, before:object|null, after:object|null}>} pairs
|
|
33
|
+
*/
|
|
34
|
+
export function changeReport(pairs) {
|
|
35
|
+
const files = [];
|
|
36
|
+
const highlights = [];
|
|
37
|
+
const touched = new Set();
|
|
38
|
+
let added = 0, removed = 0, changed = 0, invalidatedApprovals = 0;
|
|
39
|
+
|
|
40
|
+
for (const { path, before, after } of pairs || []) {
|
|
41
|
+
const d = diffGraphs(before || EMPTY, after || EMPTY);
|
|
42
|
+
added += d.addedNodes.length; removed += d.removedNodes.length; changed += d.changedNodes.length;
|
|
43
|
+
invalidatedApprovals += (d.invalidatedApprovals || []).length;
|
|
44
|
+
const record = (kind, n) => { touched.add(n.id); if (SIGNIFICANT.has(n.type)) highlights.push({ kind, type: n.type, thing: verbFor(n.type), title: n.title, path }); };
|
|
45
|
+
for (const n of d.addedNodes) record('added', n);
|
|
46
|
+
for (const n of d.removedNodes) record('removed', n);
|
|
47
|
+
for (const c of d.changedNodes) {
|
|
48
|
+
touched.add(c.id);
|
|
49
|
+
const title = c.after?.title ?? c.before?.title ?? c.id;
|
|
50
|
+
// A claim losing its verification is a real weakening (a regression), not just a change.
|
|
51
|
+
const weakened = c.before?.status === 'verify-declared' && c.after?.status === 'unverified';
|
|
52
|
+
highlights.push({ kind: weakened ? 'weakened' : 'changed', type: c.type, thing: verbFor(c.type), title, path });
|
|
53
|
+
}
|
|
54
|
+
files.push({
|
|
55
|
+
path,
|
|
56
|
+
status: !before ? 'added' : !after ? 'deleted' : 'modified',
|
|
57
|
+
added: d.addedNodes.length, removed: d.removedNodes.length, changed: d.changedNodes.length,
|
|
58
|
+
invalidatedApprovals: (d.invalidatedApprovals || []).length,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const regressions = highlights.filter((h) => h.kind === 'weakened' || (h.kind === 'removed' && REGRESSION_ON_REMOVE.has(h.type)));
|
|
63
|
+
const semantic = added + removed + changed;
|
|
64
|
+
return {
|
|
65
|
+
schema: CHANGES_SCHEMA,
|
|
66
|
+
totals: { files: files.length, added, removed, changed, invalidatedApprovals, touched: touched.size },
|
|
67
|
+
// review when a promise/proof was removed or an approval invalidated; else changed/no-op.
|
|
68
|
+
verdict: (regressions.length || invalidatedApprovals) ? 'review' : (semantic ? 'changed' : 'no-semantic-change'),
|
|
69
|
+
regressions,
|
|
70
|
+
highlights,
|
|
71
|
+
files,
|
|
72
|
+
touchedNodeIds: [...touched],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Classification + evidence model (intent-graph-v1, Section 5).
|
|
2
|
+
// Every statement carries a classification so AI-generated content never silently
|
|
3
|
+
// becomes observed fact. Pure (no Node deps): browser-safe.
|
|
4
|
+
|
|
5
|
+
export const CLASSIFICATIONS = [
|
|
6
|
+
'observed', // directly supported by a source
|
|
7
|
+
'inferred', // derived from evidence, not explicitly stated
|
|
8
|
+
'proposed', // a recommendation / possible solution
|
|
9
|
+
'assumed', // treated as true, but requires validation
|
|
10
|
+
'unknown', // required information, not yet resolved
|
|
11
|
+
'decided', // a human-approved choice
|
|
12
|
+
'verified', // supported by deterministic verification evidence
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// Confidence bands.
|
|
16
|
+
export const CONFIDENCE = ['low', 'medium', 'high'];
|
|
17
|
+
|
|
18
|
+
/** Classifications that are NOT established fact (must be surfaced with uncertainty). */
|
|
19
|
+
export const UNSETTLED = new Set(['inferred', 'proposed', 'assumed', 'unknown']);
|
|
20
|
+
|
|
21
|
+
/** Normalize a raw classification word; returns null if unrecognized. */
|
|
22
|
+
export function classify(word) {
|
|
23
|
+
const w = String(word || '').trim().toLowerCase();
|
|
24
|
+
return CLASSIFICATIONS.includes(w) ? w : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** True if a statement of this classification may be presented as established fact. */
|
|
28
|
+
export function isFactual(classification) {
|
|
29
|
+
const c = classify(classification);
|
|
30
|
+
return c === 'observed' || c === 'decided' || c === 'verified';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Phases an unresolved unknown/question can block (Section 7.4). */
|
|
34
|
+
export const BLOCKABLE_PHASES = [
|
|
35
|
+
'product-approval', 'ux-approval', 'implementation', 'verification', 'release',
|
|
36
|
+
];
|