graphlin 0.1.3 → 0.2.1
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +12 -3
- package/docs/decision-service.md +393 -0
- package/docs/extension-authoring.md +553 -0
- package/docs/model-api.md +293 -0
- package/docs/usage.md +472 -0
- package/docs/visualizer-views.md +240 -0
- package/node_modules/@vscode/tree-sitter-wasm/LICENSE +21 -0
- package/node_modules/@vscode/tree-sitter-wasm/README.md +36 -0
- package/node_modules/@vscode/tree-sitter-wasm/SECURITY.md +41 -0
- package/node_modules/@vscode/tree-sitter-wasm/cgmanifest.json +16 -0
- package/node_modules/@vscode/tree-sitter-wasm/package.json +42 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-bash.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-c-sharp.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-cpp.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-css.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-go.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ini.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-java.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-javascript.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-php.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-powershell.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-python.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-regex.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ruby.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-rust.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-tsx.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-typescript.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.js +4075 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter.d.ts +1027 -0
- package/package.json +78 -9
- package/plugin.json +4 -2
- package/runtime/architecture/analysis.mjs +344 -0
- package/runtime/architecture/controller.mjs +209 -0
- package/runtime/architecture/evidence.mjs +108 -0
- package/runtime/architecture/profile.mjs +56 -0
- package/runtime/core/evidence.mjs +43 -9
- package/runtime/core/graph.mjs +11 -6
- package/runtime/core/privacy.mjs +1 -0
- package/runtime/daemon/auth.mjs +7 -3
- package/runtime/daemon/diagnostics.mjs +1 -1
- package/runtime/daemon/extension-api.mjs +203 -0
- package/runtime/daemon/lineage.mjs +70 -0
- package/runtime/daemon/manager.mjs +9 -6
- package/runtime/daemon/model-api.mjs +728 -0
- package/runtime/daemon/model-persistence.mjs +220 -0
- package/runtime/daemon/server.mjs +81 -14
- package/runtime/daemon/settings.mjs +11 -3
- package/runtime/decisions/broker.mjs +349 -0
- package/runtime/decisions/contracts.mjs +179 -0
- package/runtime/decisions/evaluation.mjs +305 -0
- package/runtime/decisions/faults.mjs +32 -0
- package/runtime/decisions/index.mjs +818 -0
- package/runtime/decisions/profiles.mjs +93 -0
- package/runtime/decisions/questions.mjs +268 -0
- package/runtime/discovery/index.mjs +2 -0
- package/runtime/discovery/inventory.mjs +160 -0
- package/runtime/discovery/parser.mjs +40 -0
- package/runtime/discovery/structure.mjs +232 -0
- package/runtime/extensions/contracts.mjs +59 -0
- package/runtime/extensions/frame.mjs +64 -0
- package/runtime/extensions/index.mjs +9 -0
- package/runtime/extensions/manifest.mjs +95 -0
- package/runtime/extensions/packages.mjs +222 -0
- package/runtime/extensions/profiles.mjs +36 -0
- package/runtime/extensions/projection.mjs +130 -0
- package/runtime/extensions/registry.mjs +285 -0
- package/runtime/extensions/scene.mjs +105 -0
- package/runtime/extensions/sdk.d.ts +205 -0
- package/runtime/extensions/sdk.mjs +88 -0
- package/runtime/jev/index.mjs +13 -777
- package/runtime/jev/provider.mjs +101 -0
- package/runtime/jev/questions.mjs +16 -258
- package/runtime/jev/wire.mjs +17 -25
- package/runtime/model/changes.mjs +42 -0
- package/runtime/model/history.mjs +124 -0
- package/runtime/model/index.mjs +2 -0
- package/runtime/model/project-model.mjs +1020 -0
- package/runtime/model/records.mjs +240 -0
- package/runtime/pipeline.mjs +267 -55
- package/runtime/platform.mjs +254 -0
- package/runtime/visualizers/blocks.mjs +5 -0
- package/runtime/visualizers/c4.mjs +154 -0
- package/runtime/visualizers/changes.mjs +24 -0
- package/runtime/visualizers/code.mjs +5 -0
- package/runtime/visualizers/index.mjs +23 -0
- package/runtime/visualizers/structure.mjs +120 -0
- package/runtime/visualizers/timeline.mjs +66 -0
- package/runtime/web/app.js +225 -63
- package/runtime/web/extension-frame.js +128 -0
- package/runtime/web/index.html +38 -1
- package/runtime/web/model-client.js +162 -0
- package/runtime/web/platform.js +445 -0
- package/runtime/web/scene.js +111 -0
- package/runtime/web/style.css +51 -0
- package/schemas/graph.schema.json +4 -1
- package/scripts/arguments.mjs +5 -1
- package/scripts/build-packages.mjs +6 -2
- package/scripts/control.mjs +1 -1
- package/scripts/daemon.mjs +2 -1
- package/scripts/extensions.mjs +44 -0
- package/scripts/graphlin.mjs +23 -3
- package/scripts/onboarding.mjs +10 -3
- package/scripts/validate-packages.mjs +54 -8
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Live architecture diagrams while **Claude Code or Codex** explores and builds yo
|
|
|
7
7
|
## Get started
|
|
8
8
|
|
|
9
9
|
**You need:** macOS or Linux, Node.js 22.14+, and Claude Code or Codex CLI.
|
|
10
|
-
|
|
10
|
+
Local parsing needs no key. Optional AI classification uses a TypeSafe API key.
|
|
11
11
|
|
|
12
12
|
### 1. Start the viewer
|
|
13
13
|
|
|
@@ -17,13 +17,14 @@ In your project's terminal:
|
|
|
17
17
|
npx --yes graphlin@latest
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Graphlin asks which host to install, offers **source or metadata** mode for this
|
|
20
|
+
Graphlin asks which host to install, offers **local, source, or metadata** mode for this
|
|
21
21
|
project, and accepts your key at a **masked prompt** if needed. It stores the key
|
|
22
22
|
privately and builds stable plugins outside the npm cache. The browser opens
|
|
23
23
|
automatically. Keep this terminal running; **Ctrl+C** stops the viewer.
|
|
24
24
|
|
|
25
25
|
Source mode permits locally filtered source excerpts, user prompts, and public
|
|
26
26
|
agent messages to be sent to TypeSafe for classification.
|
|
27
|
+
Local mode parses JavaScript, TypeScript, TSX, and Python on your machine.
|
|
27
28
|
|
|
28
29
|
### 2. Start your agent
|
|
29
30
|
|
|
@@ -54,6 +55,14 @@ if you selected a custom data directory.
|
|
|
54
55
|
For setup alone, append `init` to the command. Append `uninstall` to remove
|
|
55
56
|
Graphlin's host plugins across projects while keeping your saved key and history.
|
|
56
57
|
|
|
58
|
+
Choose **Code**, nested **Blocks**, **C4**, **Changes**, or **Activity timeline**.
|
|
59
|
+
Search with `/`, expand a source scope, or set a task baseline. Architecture
|
|
60
|
+
documents are optional; uncertain boundaries stay marked as unknown.
|
|
61
|
+
|
|
62
|
+
Build another view with the [visualizer SDK](docs/extension-authoring.md).
|
|
63
|
+
Install it with `npx graphlin extensions add package-name@version`, then approve
|
|
64
|
+
its project access in the viewer.
|
|
65
|
+
|
|
57
66
|
## Just looking?
|
|
58
67
|
|
|
59
68
|
Try the offline demo—no key or agent required:
|
|
@@ -64,7 +73,7 @@ npx --yes graphlin@latest demo
|
|
|
64
73
|
|
|
65
74
|
## More
|
|
66
75
|
|
|
67
|
-
[User guide](docs/usage.md) · [Design](docs/graphlin-design.md) ·
|
|
76
|
+
[User guide](docs/usage.md) · [Views](docs/visualizer-views.md) · [Design](docs/graphlin-design.md) ·
|
|
68
77
|
[Contributing](CONTRIBUTING.md) · [Releasing](docs/releasing.md) · [MIT license](LICENSE)
|
|
69
78
|
|
|
70
79
|
Graphlin visualizes observable actions and code evidence. It does not capture
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# Provider-independent decisions
|
|
2
|
+
|
|
3
|
+
The daemon owns one decision service. A provider supplies bounded answers, never
|
|
4
|
+
bundles, graph mutations, source access, consent, evidence freshness, or grants.
|
|
5
|
+
`runtime/decisions/` has no dependency on `runtime/jev/`. Jev's adapter owns HTTP,
|
|
6
|
+
endpoint restrictions, credentials, model validation, wire types, and response
|
|
7
|
+
parsing. Alternate providers require none of those vendor conventions.
|
|
8
|
+
|
|
9
|
+
## Integration factories
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { createDecisionService } from '../runtime/decisions/index.mjs';
|
|
13
|
+
import { createJevProvider } from '../runtime/jev/provider.mjs';
|
|
14
|
+
|
|
15
|
+
const decisions = createDecisionService({
|
|
16
|
+
provider: createJevProvider({ apiKey, fetchImpl }), // Replace this adapter only.
|
|
17
|
+
// Existing: materializeBundle, buildRelationProposals, clock, limits,
|
|
18
|
+
// intakePolicy, admissionPolicy.
|
|
19
|
+
// Optional: profiles: [], cache: { maxEntries: 512, maxBytes: 8388608, ttlMs: 60000 }.
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`createJevProvider({ apiKey, model = 'jev-1.13.0',
|
|
24
|
+
fetchImpl = globalThis.fetch, endpoint })` uses the existing production
|
|
25
|
+
endpoint by default. Endpoint overrides are restricted to injected loopback
|
|
26
|
+
test transports. Its provenance is
|
|
27
|
+
`{ id: 'jev', version: '1', model: 'jev-1.13.0' }`; the selected model is
|
|
28
|
+
reported if overridden. Mode is `live`, or `demo` for the existing branded
|
|
29
|
+
fixture transport. Missing credentials return `unavailable / missing_key`.
|
|
30
|
+
An injected alternate provider needs no Jev model, URL, key, or environment
|
|
31
|
+
variable. Model provenance is optional for alternate providers.
|
|
32
|
+
|
|
33
|
+
The old `runtime/jev/index.mjs` exports remain available:
|
|
34
|
+
`createDecisionService({ apiKey, fetchImpl, model, endpoint, ...coreOptions })`
|
|
35
|
+
explicitly selects Jev. It also exports `createJevProvider`,
|
|
36
|
+
`createFixtureTransport`, and the existing limit/policy constants. Jev's
|
|
37
|
+
question-builder and wire-validator exports remain compatible. No factory reads
|
|
38
|
+
environment keys.
|
|
39
|
+
|
|
40
|
+
The unchanged pipeline accepts this service through its existing
|
|
41
|
+
`decisionService` option. `classify({ event, candidates, policy, deadlineAt,
|
|
42
|
+
signal })`, `stats()`, and `close()` retain their existing contracts.
|
|
43
|
+
|
|
44
|
+
## Broker evaluation API and SDK descriptors
|
|
45
|
+
|
|
46
|
+
`evaluate(input)` and `decide(input)` are aliases. These are trusted daemon
|
|
47
|
+
interfaces, not extension message handlers. The broker first validates the
|
|
48
|
+
installed profile, grant, consent, replay state, metadata projection, and
|
|
49
|
+
ownership. It supplies only approved model metadata and identifiers/relations.
|
|
50
|
+
Do not pass extension-provided state, prompts, paths, URLs, or raw source here.
|
|
51
|
+
No render mount, replay, layout, or view switch should implicitly invoke them.
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const result = await decisions.evaluate({
|
|
55
|
+
state: {
|
|
56
|
+
entities: [{ id: 'entity-1', kind: 'module' }],
|
|
57
|
+
relations: [],
|
|
58
|
+
},
|
|
59
|
+
questions: [
|
|
60
|
+
{
|
|
61
|
+
id: 'boundary', kind: 'choice',
|
|
62
|
+
question: 'Which supplied boundary describes `entities[0]`?',
|
|
63
|
+
options: [
|
|
64
|
+
{ id: 'component', label: 'A supported component boundary' },
|
|
65
|
+
{ id: 'unknown', label: 'Insufficient boundary evidence' },
|
|
66
|
+
],
|
|
67
|
+
requiredMetrics: ['probabilities', 'confidence'],
|
|
68
|
+
},
|
|
69
|
+
{ id: 'supported', kind: 'boolean', question: 'Is this boundary supported?',
|
|
70
|
+
requiredMetrics: ['probability'] },
|
|
71
|
+
{ id: 'relevance', kind: 'score', question: 'Rate relevance to the supplied scope.',
|
|
72
|
+
options: ['Low', 'Medium', 'High'] },
|
|
73
|
+
],
|
|
74
|
+
profile: { id: 'example.boundaries', version: '1' }, // Optional.
|
|
75
|
+
cacheContext: { // Optional; all six fields required when provided.
|
|
76
|
+
projectId: 'project-1', worktreeId: 'worktree-1', lineage: 'branch-generation-1',
|
|
77
|
+
policyVersion: 'policy-1', evidenceVersion: 'metadata-revision-7', taskScope: null,
|
|
78
|
+
},
|
|
79
|
+
signal, deadlineAt,
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Question fields:
|
|
84
|
+
|
|
85
|
+
| Field | Contract |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `id` | Unique bounded identifier |
|
|
88
|
+
| `kind` | `boolean`, `choice`, or `score` |
|
|
89
|
+
| `question` | Host-validated question text, up to 8192 characters |
|
|
90
|
+
| `focus` | Optional additional instruction, up to 8192 characters |
|
|
91
|
+
| `options` | Omitted for boolean; choice has 2–128 distinct `{id,label}` entries; score has 2–128 ordered string labels |
|
|
92
|
+
| `requiredMetrics` | Optional array: boolean supports `probability`; choice/score support `probabilities` and `confidence` |
|
|
93
|
+
|
|
94
|
+
Score values use zero-based option indices. A probabilistic score must equal
|
|
95
|
+
its distribution's weighted index, within the existing rounding tolerance.
|
|
96
|
+
Questions in one request are independent; they cannot consume another answer
|
|
97
|
+
from that request. Dependent stages require an explicit subsequent broker call
|
|
98
|
+
within the broker's overall workflow budget.
|
|
99
|
+
|
|
100
|
+
Every result has this shape:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
{
|
|
104
|
+
status: 'accepted', // Or abstained, invalid, unavailable, timeout, overloaded.
|
|
105
|
+
answers: [{
|
|
106
|
+
id: 'boundary', kind: 'choice', value: 'component',
|
|
107
|
+
probability: null,
|
|
108
|
+
probabilities: { component: 0.94, unknown: 0.06 },
|
|
109
|
+
confidence: 0.95,
|
|
110
|
+
}],
|
|
111
|
+
provenance: {
|
|
112
|
+
contractVersion: 1,
|
|
113
|
+
provider: { id: 'provider-id', version: '1' }, // Optional model if supplied.
|
|
114
|
+
profile: { id: 'example.boundaries', version: '1' }, // Or null.
|
|
115
|
+
cacheContext: { /* the exact supplied version context, or null */ },
|
|
116
|
+
inputHash: 'sha256-of-exact-provider-encoded-input',
|
|
117
|
+
completedAt: 1234,
|
|
118
|
+
usage: { inputTokens: 10, outputTokens: 5 }, // Or null.
|
|
119
|
+
mode: 'local', // live, demo, or local.
|
|
120
|
+
cacheKey: 'sha256-of-versioned-request-context', // Or null.
|
|
121
|
+
},
|
|
122
|
+
diagnostics: {
|
|
123
|
+
code: 'ok', calls: 1,
|
|
124
|
+
cache: { status: 'miss', key: 'sha256-of-versioned-request-context' },
|
|
125
|
+
// Existing bounded timing, question counts, usage and trace metadata.
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Boolean `value` is a supplied boolean or null; `probability` is the probability
|
|
131
|
+
that the proposition is true. Neither is inferred from the other. Choice
|
|
132
|
+
`value` is an option ID; score `value` is the numeric score. All unavailable
|
|
133
|
+
metrics are null, including irrelevant metric fields. Never interpret missing
|
|
134
|
+
confidence or a deterministic selection as probability 1. A provider can
|
|
135
|
+
return an answer without probabilities only when the question does not require
|
|
136
|
+
them. Missing required metrics yield `abstained / missing_answer_metrics`;
|
|
137
|
+
unsupported declared capabilities yield `abstained / unsupported_capability`
|
|
138
|
+
with `diagnostics.capabilityLimitations`. Invalid results have no answers.
|
|
139
|
+
|
|
140
|
+
`accepted` means the bounded answer contract passed. Core still decides whether
|
|
141
|
+
the answer supports an interpretation, a graph change, or no change. This does
|
|
142
|
+
not establish execution, verification, or authoritative architectural truth.
|
|
143
|
+
Provider-added text, explanations, bundles, judgments, and arbitrary usage
|
|
144
|
+
fields are discarded.
|
|
145
|
+
|
|
146
|
+
The metadata API rejects non-JSON values, cycles, excessive depth/size, and
|
|
147
|
+
obvious source/credential fields such as `code`, `text`, `snippet`, `transcript`,
|
|
148
|
+
`prompt`, and `apiKey`. This guard is not a substitute for broker authorization
|
|
149
|
+
or core's local secret filtering; approved metadata must not hide raw content
|
|
150
|
+
under another field name. No raw metadata state is retained in completed cache
|
|
151
|
+
entries or diagnostics.
|
|
152
|
+
|
|
153
|
+
## Extension analysis broker
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
import { createAnalysisBroker } from '../runtime/decisions/broker.mjs';
|
|
157
|
+
|
|
158
|
+
const runAnalysis = createAnalysisBroker({
|
|
159
|
+
service: decisions,
|
|
160
|
+
model: pipeline.model,
|
|
161
|
+
policy, // A policy object, or () => currentPolicy for a changing policy.
|
|
162
|
+
projectId,
|
|
163
|
+
registry,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const result = await runAnalysis({
|
|
167
|
+
projectId, extensionId, digest,
|
|
168
|
+
profile, // Installed declarative profile from runtime/extensions/profiles.mjs.
|
|
169
|
+
entityIds, // Nonempty unique canonical model IDs.
|
|
170
|
+
revision, // Current live model revision.
|
|
171
|
+
grant, // Grant snapshot already checked by the HTTP helper.
|
|
172
|
+
signal,
|
|
173
|
+
});
|
|
174
|
+
// { status: 'complete', requestId, interpretationIds }
|
|
175
|
+
// or { status: 'unavailable' }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
These are the exact five factory options. `service` supplies `evaluate`;
|
|
179
|
+
`model` supplies synchronous `snapshot()` and `observeInterpretations()`.
|
|
180
|
+
`registry` supplies async `getAssets(extensionId, { digest })` and
|
|
181
|
+
`getGrant(extensionId)`. Use the same policy and project model as the pipeline.
|
|
182
|
+
The factory returns the async function directly, with no extra object wrapper,
|
|
183
|
+
queue, cache, source reader, or transport.
|
|
184
|
+
|
|
185
|
+
The parent HTTP helper authenticates explicit POST requests with exactly
|
|
186
|
+
`{id,digest,profileId,entityIds,revision}` and resolves the current installed
|
|
187
|
+
profile and grant before this callback. It must never accept extension-supplied
|
|
188
|
+
state or raw source. It owns response notification after the callback. Mounting,
|
|
189
|
+
rendering, viewing, switching layouts, and replay never invoke analysis.
|
|
190
|
+
|
|
191
|
+
The broker independently checks `analysis.request`, the immutable package
|
|
192
|
+
digest and installed profile, approved profile/field/entity scope, current
|
|
193
|
+
grant equality, and `transmitSource`. Read-only source consent is insufficient.
|
|
194
|
+
It rejects replay/checkpoint snapshots. It repeats authority, policy,
|
|
195
|
+
revision, metadata, and exact-reference checks after evaluation before core
|
|
196
|
+
admission; it checks authority and resulting current records again before
|
|
197
|
+
returning IDs. Revocation, stale evidence, malformed responses, cancellation,
|
|
198
|
+
or exceeded bounds produce only `unavailable`.
|
|
199
|
+
|
|
200
|
+
Profile questions are `{id,kind,question,options?,interpretationKind?,
|
|
201
|
+
interpretationLabel?}`. Choice options are strings,
|
|
202
|
+
mapped to neutral `{id: 'option-N', label}` entries. Extension scores have no
|
|
203
|
+
options and a 0–1 range; the broker supplies neutral `['Low','High']` criteria.
|
|
204
|
+
No taxonomy, provider name, model, key, endpoint, or vendor wire type is part of
|
|
205
|
+
the extension profile schema. Questions remain independent.
|
|
206
|
+
|
|
207
|
+
Only selected, current, locally filtered model metadata is sent. Entity fields
|
|
208
|
+
are `id/kind/label/basis/parentId`; relation fields are
|
|
209
|
+
`id/source/target/kind/basis`. Optional interpretation context contains bounded
|
|
210
|
+
IDs, namespace/version, kind, safe label, entity IDs, and support. Relationships
|
|
211
|
+
and interpretations cannot escape the selected entity scope. Paths,
|
|
212
|
+
qualified names, source references, raw source, and arbitrary extra fields
|
|
213
|
+
are omitted from transmission. Unsafe labels are replaced locally and make
|
|
214
|
+
support unknown. Profile text is locally secret-filtered before dispatch.
|
|
215
|
+
|
|
216
|
+
The exported `ANALYSIS_LIMITS` are 256 entities, 128 relations, 64 context
|
|
217
|
+
interpretations, 16 exact source references, 16 questions, and 32 KiB metadata.
|
|
218
|
+
The service's lower configured limits also apply. Oversized requests fail
|
|
219
|
+
closed, without implicit batching or truncation. Nonempty references require
|
|
220
|
+
a current, fresh, present artifact with exactly matching hash/generation and a
|
|
221
|
+
known, safe, nonexcluded relative path; that path stays local. Public intent
|
|
222
|
+
references cannot support analysis. Entities without references can receive
|
|
223
|
+
only unknown interpretations.
|
|
224
|
+
|
|
225
|
+
Evaluation provenance uses profile `{id: extensionId + '.' + profile.id,
|
|
226
|
+
version: digest}`. The cache context binds project/worktree to the canonical
|
|
227
|
+
project ID and lineage to `snapshot.coverage.lineage.id ?? projectId`. Policy
|
|
228
|
+
version binds the effective policy plus the exact grant; evidence version binds
|
|
229
|
+
revision plus the metadata/reference/lineage hash; task scope binds
|
|
230
|
+
extension/profile/digest. A changed opaque lineage ID rejects an in-flight
|
|
231
|
+
result even if source and revision are otherwise identical. Branch names and
|
|
232
|
+
HEAD values are never sent to the provider; the opaque lineage ID stays in
|
|
233
|
+
local cache provenance. The parent also invalidates/cancels work when replacing
|
|
234
|
+
the project model.
|
|
235
|
+
|
|
236
|
+
Profile `interpretationKind` may explicitly name `application`, `container`,
|
|
237
|
+
`component`, `system`, `external_system`, `actor`, `person`, `context`, or
|
|
238
|
+
`datastore`. `selected-choice` is allowed only for choice questions whose
|
|
239
|
+
options are those exact kinds or `unknown`. Only supported, accepted answers
|
|
240
|
+
with nonempty current references receive the declared kind. The broker derives
|
|
241
|
+
it from the validated declaration and selected option ID, never from answer
|
|
242
|
+
text or an arbitrary label. All other records keep their generic analysis kind.
|
|
243
|
+
Without a declaration, choosing the string `application` cannot create a C4
|
|
244
|
+
boundary; the default view remains unknown.
|
|
245
|
+
|
|
246
|
+
An optional `interpretationLabel` requires a mapping and is limited to 80
|
|
247
|
+
locally filtered characters. Otherwise the boundary uses the first selected
|
|
248
|
+
entity's filtered core label. The label and mapping stay out of provider
|
|
249
|
+
question descriptors; their installed digest binds them into grant and cache
|
|
250
|
+
provenance. They do not change provider capabilities or wire contracts.
|
|
251
|
+
|
|
252
|
+
Interpretations use namespace `extensionId + '.' + profile.id`, version
|
|
253
|
+
`digest`, basis `decision`, and exact current references retained locally.
|
|
254
|
+
The combined namespace must fit the model's 80-character limit. Missing
|
|
255
|
+
probabilities or required confidence always produce `support: 'unknown'` and
|
|
256
|
+
`classification: 'unknown'`, even if a deterministic provider selected a value.
|
|
257
|
+
Probability thresholds reuse core admission defaults. Incomplete metadata
|
|
258
|
+
support also remains unknown. These records do not establish runtime success.
|
|
259
|
+
|
|
260
|
+
The broker reads back model-assigned IDs and returns only records that match
|
|
261
|
+
this request, namespace, version, references, and current validity. It respects
|
|
262
|
+
the core display-label projection. Capacity rejection cannot fabricate IDs;
|
|
263
|
+
partial retention returns only the IDs actually retained. Results expose no
|
|
264
|
+
provider response fields, grants, prompts, or metadata. The wider HTTP callback
|
|
265
|
+
contract also permits `accepted` and `pending`; this synchronous admission
|
|
266
|
+
broker currently returns only `complete` or `unavailable`.
|
|
267
|
+
|
|
268
|
+
## Scheduling, cache, cancellation, and freshness
|
|
269
|
+
|
|
270
|
+
All entry points share one concurrency limit, pending queue, cooldown, request
|
|
271
|
+
and response byte limits, question limits, and deadline clock. Source work
|
|
272
|
+
reserves two calls; a broker evaluation reserves one. Defaults remain two active
|
|
273
|
+
workflows, 32 queued workflows, 2000 ms, 40 questions per request, 64 KiB request,
|
|
274
|
+
and 256 KiB response. Requests do not retry. Adapter serialization is synchronous
|
|
275
|
+
and side-effect-free; transport and response-body work must honor the shared
|
|
276
|
+
abort signal. The service also bounds providers that ignore it.
|
|
277
|
+
Queued broker evaluations yield to queued source/intake work.
|
|
278
|
+
|
|
279
|
+
Evaluation caching requires all six `cacheContext` fields. They are bounded
|
|
280
|
+
opaque identifiers or nonnegative integer versions; `taskScope` may be null.
|
|
281
|
+
The broker must bind `evidenceVersion` to every supplied record/reference and
|
|
282
|
+
`lineage` to the selected worktree/branch. Exact metadata, questions, options,
|
|
283
|
+
required metrics, profile ID/version, provider ID/version/model/mode and
|
|
284
|
+
capabilities also enter the key. Different taxonomy options therefore cannot
|
|
285
|
+
reuse the same result. No context means no cache.
|
|
286
|
+
|
|
287
|
+
Only successful normalized evaluation results are cached. Defaults are 512
|
|
288
|
+
entries, 8 MiB total serialized results, and 60 seconds, with LRU eviction.
|
|
289
|
+
Set any cache bound to zero to disable caching and coalescing. Cache entries
|
|
290
|
+
contain answers and provenance, not source, state, or approved bundle objects.
|
|
291
|
+
Source `classify` and `analyze` never reuse serialized approval capabilities.
|
|
292
|
+
The parent may retain their original branded bundles under its own bounded
|
|
293
|
+
admission lifecycle.
|
|
294
|
+
|
|
295
|
+
Concurrent equivalent evaluations share one queued/active workflow. Each
|
|
296
|
+
subscriber has its own cancellation and deadline; losing one subscriber does
|
|
297
|
+
not cancel another. The underlying work stops when no subscribers remain.
|
|
298
|
+
The first workflow's service deadline cannot be extended by later subscribers.
|
|
299
|
+
Subscriber count is bounded by concurrency plus queue capacity. Stats expose
|
|
300
|
+
cache hits, shared hits, evictions, entries/bytes, and active subscribers.
|
|
301
|
+
|
|
302
|
+
`invalidateCache()` clears retained evaluations and aborts shared evaluations
|
|
303
|
+
as `stale_evidence`; use it on revocation or scope invalidation, together with
|
|
304
|
+
the broker's owner cancellation. It invalidates the whole service cache.
|
|
305
|
+
Every consumer must still revalidate policy, lineage, evidence versions, task
|
|
306
|
+
scope, and grant before applying either a fresh or cached result. Noncached
|
|
307
|
+
calls are cancelled through their supplied signal. `close()` cancels all work
|
|
308
|
+
and clears cached results. A cache hit reports zero new calls/usage; provenance
|
|
309
|
+
retains the original provider usage and completion time.
|
|
310
|
+
|
|
311
|
+
## Source A/B and registered source profiles
|
|
312
|
+
|
|
313
|
+
`classify` keeps the existing neutral architecture catalog: immutable candidate
|
|
314
|
+
snapshots, shared evidence sensitivity, per-entity relevance, core
|
|
315
|
+
`materializeBundle`, exact approved B evidence, independent relation/context
|
|
316
|
+
questions, and unchanged admission thresholds. The returned bundle is the
|
|
317
|
+
exact branded core object. Providers never receive core functions, candidates'
|
|
318
|
+
private IDs/digests, or the bundle capability. Source evidence and public intent
|
|
319
|
+
stay distinct. The unchanged pipeline performs final version revalidation.
|
|
320
|
+
|
|
321
|
+
Optional host `profiles` registrations support source-backed analysis through
|
|
322
|
+
`analyze({ ...classifyInput, profileId })`. Each registration has
|
|
323
|
+
`{ id, version, scope: 'entity' | 'bundle', questions }`; `questions` is an object
|
|
324
|
+
keyed by question ID, containing neutral
|
|
325
|
+
`{ type, instructions: { question, focus? }, criteria, requiredMetrics? }`.
|
|
326
|
+
Boolean criteria have `true`/`false` descriptions, choice criteria map option
|
|
327
|
+
IDs to descriptions, and score criteria are ordered string labels.
|
|
328
|
+
Entity instructions use `{{entity}}` and `{{evidence}}` placeholders, expanded
|
|
329
|
+
to exact approved array paths. The registry is a trusted host operation, not
|
|
330
|
+
an extension callback.
|
|
331
|
+
|
|
332
|
+
This path always runs A first and uses only the exact core bundle for its B
|
|
333
|
+
questions. It returns `abstained / profile_answers` plus
|
|
334
|
+
`analysis: { profileId, profileVersion, status: 'answered', answers, subjects }`
|
|
335
|
+
and no graph judgments. `subjects` maps question IDs to local approved candidate
|
|
336
|
+
IDs (null for bundle questions); core owns interpretation admission. Budgets
|
|
337
|
+
can reject a large profile rather than adding hidden stages or weakening intake.
|
|
338
|
+
The low-level array descriptor API above is the broker/SDK integration seam;
|
|
339
|
+
these source registrations are an additional internal A/B facility.
|
|
340
|
+
|
|
341
|
+
## Adapter contract v1
|
|
342
|
+
|
|
343
|
+
A provider supplies:
|
|
344
|
+
|
|
345
|
+
```js
|
|
346
|
+
{
|
|
347
|
+
contractVersion: 1, id: 'example', version: '1', mode: 'local',
|
|
348
|
+
// model is optional opaque provenance, not a required vendor name.
|
|
349
|
+
capabilities: {
|
|
350
|
+
boolean: { probability: true },
|
|
351
|
+
choice: { probabilities: true, confidence: true },
|
|
352
|
+
score: { probabilities: true, confidence: true },
|
|
353
|
+
},
|
|
354
|
+
unavailableCode: null, // Or provider_unavailable; Jev compatibility uses missing_key.
|
|
355
|
+
encode(request) { return JSON.stringify(request); },
|
|
356
|
+
async execute(encoded, { signal, deadlineAt, maxResponseBytes, now, reportTransport }) {
|
|
357
|
+
// Return { answers: { questionId: normalizedAnswer }, usage: null | counts }.
|
|
358
|
+
},
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Absent capability types are unsupported; omitted/false metric flags mean the
|
|
363
|
+
provider cannot supply that metric. The service exposes its immutable snapshot
|
|
364
|
+
as `service.capabilities`. Internal request descriptors use the neutral
|
|
365
|
+
`type/instructions/criteria/requiredMetrics` shape described above; adapters
|
|
366
|
+
receive frozen projections. `encode` returns the exact outbound string, which
|
|
367
|
+
core measures and hashes before dispatch. `execute` must use that string and
|
|
368
|
+
make at most one bounded provider request. Credentials remain in the adapter's
|
|
369
|
+
closure, not encoded requests, provenance, capabilities, or diagnostics.
|
|
370
|
+
|
|
371
|
+
Internal boolean answers are `{ type:'boolean', value?, probability? }`;
|
|
372
|
+
choice answers are `{ type:'choice', choice, probabilities?, confidence? }`;
|
|
373
|
+
score answers are `{ type:'score', score, probabilities?, confidence? }`.
|
|
374
|
+
Usage is optional `{ inputTokens, outputTokens }`. The service validates IDs,
|
|
375
|
+
types, option coverage, finite metrics, distributions, winners, score
|
|
376
|
+
consistency, and usage; Jev also retains its stricter wire validation.
|
|
377
|
+
|
|
378
|
+
Adapters may throw `DecisionFault(code, status, { retryAfterMs })`; overload
|
|
379
|
+
delays are clamped by core. Unknown fault strings are sanitized. Optional
|
|
380
|
+
`reportTransport({httpStatus})` supports legacy diagnostics; non-HTTP providers
|
|
381
|
+
need not call it. Providers are trusted daemon code, not sandboxed extensions.
|
|
382
|
+
|
|
383
|
+
## Packaging and verification
|
|
384
|
+
|
|
385
|
+
The parent owns pipeline/server integration and package allowlists. Include all
|
|
386
|
+
`runtime/decisions/*.mjs` files and `runtime/jev/provider.mjs` in the root package
|
|
387
|
+
file list and generated host packages. No version bump or release is made here.
|
|
388
|
+
|
|
389
|
+
Run `npm test -- tests/decisions/*.test.mjs tests/jev/*.test.mjs` plus the
|
|
390
|
+
pipeline/runtime integration tests. The unchanged Jev wire fixtures pin exact
|
|
391
|
+
requests and rubrics. Deterministic provider tests exercise the same caller,
|
|
392
|
+
pipeline, profiles, filtering, branded materialization, deadlines, stale
|
|
393
|
+
rejection, capabilities, and cache behavior without credentials or live calls.
|