norn-cli 3.3.0 → 4.0.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/dist/cli.js +1374 -260
- package/dist/knowledgeIndexWorker.js +68 -22
- package/package.json +3 -3
- package/schemas/norn.config.schema.json +13 -0
- package/CLAUDE.md +0 -69
- package/NOW.md +0 -223
- package/demos/agent-workbench/README.md +0 -156
- package/demos/agent-workbench/agents.nornagent +0 -87
- package/demos/agent-workbench/contracts/domain-answer.schema.json +0 -28
- package/demos/agent-workbench/contracts/domain-question.schema.json +0 -34
- package/demos/agent-workbench/contracts/router-verdict.schema.json +0 -43
- package/demos/agent-workbench/contracts/ticket.schema.json +0 -29
- package/demos/agent-workbench/contracts/verdict.schema.json +0 -48
- package/demos/agent-workbench/fake-openai-server.js +0 -128
- package/demos/agent-workbench/fixtures/aligned-ticket.json +0 -10
- package/demos/agent-workbench/fixtures/cross-domain-ticket.json +0 -10
- package/demos/agent-workbench/fixtures/invalid-output-ticket.json +0 -8
- package/demos/agent-workbench/norn.config.json +0 -15
- package/demos/agent-workbench/prompts/ticket-router.md +0 -11
- package/demos/agent-workbench/workbench.norn +0 -32
- package/demos/knowledge-editor/README.md +0 -74
- package/demos/knowledge-editor/agents.nornagent +0 -16
- package/demos/knowledge-editor/api.nornapi +0 -10
- package/demos/knowledge-editor/handbook/ops/flow.svg +0 -1
- package/demos/knowledge-editor/handbook/ops/retired.md +0 -9
- package/demos/knowledge-editor/handbook/ops/runbook.md +0 -14
- package/demos/knowledge-editor/handbook/payments/charges.md +0 -25
- package/demos/knowledge-editor/handbook/payments/pricing-tiers.draft.md +0 -7
- package/demos/knowledge-editor/handbook/payments/refunds/policy.md +0 -12
- package/demos/knowledge-editor/support.norn +0 -10
- package/demos/mcp-ticket-testing/README.md +0 -114
- package/demos/mcp-ticket-testing/agents.nornagent +0 -77
- package/demos/mcp-ticket-testing/contracts/test-run.schema.json +0 -31
- package/demos/mcp-ticket-testing/expectations/proj-142.md +0 -12
- package/demos/mcp-ticket-testing/fixtures/proj-142.json +0 -13
- package/demos/mcp-ticket-testing/prompts/backend-tester.md +0 -12
- package/demos/mcp-ticket-testing/prompts/frontend-tester.md +0 -14
- package/demos/mcp-ticket-testing/prompts/reporter.md +0 -10
- package/demos/mcp-ticket-testing/servers/browser-server.js +0 -133
- package/demos/mcp-ticket-testing/servers/house-server.js +0 -125
- package/demos/mcp-ticket-testing/tickets.norn +0 -32
- package/demos/nornenv-region-refactor/README.md +0 -64
- package/demos/nornenv-showcase/README.md +0 -62
- package/demos/nornenv-showcase/norn.config.json +0 -16
- package/demos/nornenv-showcase/showcase.norn +0 -70
- package/demos/nornenv-showcase/showcase.nornapi +0 -26
- package/demos/nornenv-showcase/showcase.nornsql +0 -20
- package/demos/tests-showcase/01-single-requests.norn +0 -31
- package/demos/tests-showcase/02-sequences.norn +0 -54
- package/demos/tests-showcase/03-sidecars.norn +0 -42
- package/demos/tests-showcase/04-api-plus-sql.norn +0 -27
- package/demos/tests-showcase/db/testDb.nornsql +0 -12
- package/demos/tests-showcase/demo-api.nornapi +0 -17
- package/demos/tests-showcase/norn.config.json +0 -16
- package/playground/ai.norn +0 -15
- package/playground/ai_orchastration.nornagent +0 -34
- package/playground/knowedge_base/ACDC/testing_notes_new_clients.md +0 -11
- package/playground/knowedge_base/nexus_system_prompt.md +0 -1
|
@@ -7592,11 +7592,14 @@ function readAgentPromptBlock(lines, openingLineNumber, opening, stripTrailingCo
|
|
|
7592
7592
|
};
|
|
7593
7593
|
}
|
|
7594
7594
|
|
|
7595
|
+
// src/agents/agentTypes.ts
|
|
7596
|
+
var AGENT_REASONING_EFFORTS = ["none", "low", "medium", "high", "xhigh", "max"];
|
|
7597
|
+
|
|
7595
7598
|
// src/agents/nornAgentParser.ts
|
|
7596
7599
|
var IDENTIFIER = "[a-zA-Z_][a-zA-Z0-9_]*";
|
|
7597
|
-
var MODEL_PROVIDERS = ["anthropic", "google", "local", "openai"];
|
|
7600
|
+
var MODEL_PROVIDERS = ["anthropic", "google", "local", "openai", "openai_compatible"];
|
|
7598
7601
|
var MODEL_PROVIDER = new RegExp(`^(${MODEL_PROVIDERS.join("|")})$`, "i");
|
|
7599
|
-
var MODEL_DIRECTIVES = ["provider", "name", "apiKey", "baseUrl"];
|
|
7602
|
+
var MODEL_DIRECTIVES = ["provider", "name", "reasoning", "apiKey", "baseUrl"];
|
|
7600
7603
|
var MCP_DIRECTIVES = {
|
|
7601
7604
|
transport: { transport: "both", repeatable: false },
|
|
7602
7605
|
command: { transport: "stdio", repeatable: false },
|
|
@@ -7635,6 +7638,7 @@ function splitHeaderDirective(value) {
|
|
|
7635
7638
|
}
|
|
7636
7639
|
var LIMIT_DIRECTIVES = {
|
|
7637
7640
|
max_tokens: "maxTokens",
|
|
7641
|
+
max_input_tokens: "maxInputTokens",
|
|
7638
7642
|
max_depth: "maxDepth",
|
|
7639
7643
|
max_invocations: "maxInvocations",
|
|
7640
7644
|
max_turns: "maxTurns",
|
|
@@ -8069,6 +8073,23 @@ end mcp
|
|
|
8069
8073
|
));
|
|
8070
8074
|
model.invalid = true;
|
|
8071
8075
|
}
|
|
8076
|
+
const reasoning = model.values.reasoning;
|
|
8077
|
+
if (reasoning && !reasoning.value.includes("{{") && !AGENT_REASONING_EFFORTS.some((effort) => effort === reasoning.value.toLowerCase())) {
|
|
8078
|
+
errors.push(parseError(
|
|
8079
|
+
reasoning.lineNumber,
|
|
8080
|
+
`Unknown reasoning effort '${reasoning.value}' in model '${model.alias}'. Expected one of: ${AGENT_REASONING_EFFORTS.join(", ")}.`,
|
|
8081
|
+
"agent-model-reasoning-invalid"
|
|
8082
|
+
));
|
|
8083
|
+
model.invalid = true;
|
|
8084
|
+
}
|
|
8085
|
+
if (reasoning && provider && !provider.value.includes("{{") && provider.value.toLowerCase() !== "openai") {
|
|
8086
|
+
errors.push(parseError(
|
|
8087
|
+
reasoning.lineNumber,
|
|
8088
|
+
`Model '${model.alias}' can declare reasoning only with provider openai. Provider '${provider.value}' does not support this directive yet.`,
|
|
8089
|
+
"agent-model-reasoning-provider"
|
|
8090
|
+
));
|
|
8091
|
+
model.invalid = true;
|
|
8092
|
+
}
|
|
8072
8093
|
if (model.invalid || !provider || !name) {
|
|
8073
8094
|
return;
|
|
8074
8095
|
}
|
|
@@ -8077,6 +8098,7 @@ end mcp
|
|
|
8077
8098
|
providerTemplate: provider.value,
|
|
8078
8099
|
nameTemplate: name.value,
|
|
8079
8100
|
targetTemplate: `${provider.value}/${name.value}`,
|
|
8101
|
+
...reasoning ? { reasoningTemplate: reasoning.value } : {},
|
|
8080
8102
|
...model.values.apiKey ? { apiKeyTemplate: model.values.apiKey.value } : {},
|
|
8081
8103
|
...model.values.baseUrl ? { baseUrlTemplate: model.values.baseUrl.value } : {},
|
|
8082
8104
|
lineNumber: model.lineNumber,
|
|
@@ -8116,6 +8138,13 @@ end mcp
|
|
|
8116
8138
|
"agent-model-alias-unknown"
|
|
8117
8139
|
));
|
|
8118
8140
|
pending.invalid = true;
|
|
8141
|
+
} else if (model.alias !== pending.modelAlias.value) {
|
|
8142
|
+
errors.push(parseError(
|
|
8143
|
+
pending.modelAlias.lineNumber,
|
|
8144
|
+
`Model alias '${pending.modelAlias.value}' in agent '${pending.name}' does not match the declared alias '${model.alias}'. Model aliases are case-sensitive.`,
|
|
8145
|
+
"agent-model-alias-case-mismatch"
|
|
8146
|
+
));
|
|
8147
|
+
pending.invalid = true;
|
|
8119
8148
|
}
|
|
8120
8149
|
}
|
|
8121
8150
|
}
|
|
@@ -8151,6 +8180,7 @@ end mcp
|
|
|
8151
8180
|
accepts: pending.accepts,
|
|
8152
8181
|
returns: pending.returns,
|
|
8153
8182
|
maxTokens: pending.maxTokens?.value,
|
|
8183
|
+
maxInputTokens: pending.maxInputTokens?.value,
|
|
8154
8184
|
maxDepth: pending.maxDepth?.value,
|
|
8155
8185
|
maxInvocations: pending.maxInvocations?.value,
|
|
8156
8186
|
maxTurns: pending.maxTurns?.value,
|
|
@@ -8555,29 +8585,38 @@ end model`,
|
|
|
8555
8585
|
}
|
|
8556
8586
|
const knowledgeGrantMatch = trimmed.match(/^knowledge(?:\s+(.*))?$/i);
|
|
8557
8587
|
if (knowledgeGrantMatch) {
|
|
8558
|
-
const
|
|
8559
|
-
|
|
8588
|
+
const rawValue = (knowledgeGrantMatch[1] ?? "").trim();
|
|
8589
|
+
const aliases = rawValue ? rawValue.split(",").map((part) => part.trim()) : [];
|
|
8590
|
+
if (aliases.length === 0 || aliases.some((alias) => !new RegExp(`^${IDENTIFIER}$`).test(alias))) {
|
|
8560
8591
|
errors.push(parseError(
|
|
8561
8592
|
index,
|
|
8562
|
-
`Invalid knowledge grant '${trimmed}' in agent '${pending.name}'. Expected: knowledge <Alias
|
|
8593
|
+
`Invalid knowledge grant '${trimmed}' in agent '${pending.name}'. Expected: knowledge <Alias>[, <Alias>\u2026].`,
|
|
8563
8594
|
"agent-knowledge-grant-invalid"
|
|
8564
8595
|
));
|
|
8565
8596
|
pending.invalid = true;
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8597
|
+
continue;
|
|
8598
|
+
}
|
|
8599
|
+
const granted = new Set(pending.knowledge.map((grant) => grant.alias.toLowerCase()));
|
|
8600
|
+
for (const alias of aliases) {
|
|
8601
|
+
if (granted.has(alias.toLowerCase())) {
|
|
8602
|
+
errors.push(parseError(
|
|
8603
|
+
index,
|
|
8604
|
+
`Agent '${pending.name}' grants knowledge '${alias}' more than once.`,
|
|
8605
|
+
"agent-knowledge-grant-duplicate"
|
|
8606
|
+
));
|
|
8607
|
+
pending.invalid = true;
|
|
8608
|
+
continue;
|
|
8609
|
+
}
|
|
8610
|
+
if (!knowledgeBases.has(alias.toLowerCase())) {
|
|
8611
|
+
errors.push(parseError(
|
|
8612
|
+
index,
|
|
8613
|
+
`Unknown knowledge alias '${alias}' in agent '${pending.name}'.`,
|
|
8614
|
+
"agent-knowledge-alias-unknown"
|
|
8615
|
+
));
|
|
8616
|
+
pending.invalid = true;
|
|
8617
|
+
continue;
|
|
8618
|
+
}
|
|
8619
|
+
granted.add(alias.toLowerCase());
|
|
8581
8620
|
pending.knowledge.push({ alias, lineNumber: index });
|
|
8582
8621
|
}
|
|
8583
8622
|
continue;
|
|
@@ -9533,6 +9572,7 @@ function isNonNegativeInteger(value) {
|
|
|
9533
9572
|
var AGENT_LIMIT_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
9534
9573
|
"_comment",
|
|
9535
9574
|
"max_tokens",
|
|
9575
|
+
"max_input_tokens",
|
|
9536
9576
|
"max_depth",
|
|
9537
9577
|
"max_invocations",
|
|
9538
9578
|
"max_turns",
|
|
@@ -9542,7 +9582,7 @@ function hasOnlyKeys(value, allowedKeys) {
|
|
|
9542
9582
|
return Object.keys(value).every((key) => allowedKeys.has(key));
|
|
9543
9583
|
}
|
|
9544
9584
|
function hasValidNornAgentLimits(value) {
|
|
9545
|
-
return (value.max_tokens === void 0 || isPositiveInteger(value.max_tokens)) && (value.max_depth === void 0 || isPositiveInteger(value.max_depth)) && (value.max_invocations === void 0 || isPositiveInteger(value.max_invocations)) && (value.max_turns === void 0 || isPositiveInteger(value.max_turns)) && (value.contract_retries === void 0 || isNonNegativeInteger(value.contract_retries));
|
|
9585
|
+
return (value.max_tokens === void 0 || isPositiveInteger(value.max_tokens)) && (value.max_input_tokens === void 0 || isPositiveInteger(value.max_input_tokens)) && (value.max_depth === void 0 || isPositiveInteger(value.max_depth)) && (value.max_invocations === void 0 || isPositiveInteger(value.max_invocations)) && (value.max_turns === void 0 || isPositiveInteger(value.max_turns)) && (value.contract_retries === void 0 || isNonNegativeInteger(value.contract_retries));
|
|
9546
9586
|
}
|
|
9547
9587
|
function isNornAgentProviderConfig(value) {
|
|
9548
9588
|
if (!isObjectRecord(value)) {
|
|
@@ -9562,7 +9602,13 @@ function isNornAgentRecordingConfig(value) {
|
|
|
9562
9602
|
}
|
|
9563
9603
|
return hasOnlyKeys(value, /* @__PURE__ */ new Set(["_comment", "enabled"])) && (value.enabled === void 0 || typeof value.enabled === "boolean");
|
|
9564
9604
|
}
|
|
9565
|
-
var AGENT_PROVIDER_NAMES = /* @__PURE__ */ new Set([
|
|
9605
|
+
var AGENT_PROVIDER_NAMES = /* @__PURE__ */ new Set([
|
|
9606
|
+
"openai",
|
|
9607
|
+
"openai_compatible",
|
|
9608
|
+
"anthropic",
|
|
9609
|
+
"google",
|
|
9610
|
+
"local"
|
|
9611
|
+
]);
|
|
9566
9612
|
function isNornAgentsConfig(value) {
|
|
9567
9613
|
if (value === void 0) {
|
|
9568
9614
|
return true;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "norn-cli",
|
|
3
3
|
"displayName": "Norn",
|
|
4
4
|
"description": "Version-controlled API and database tests. Author in VS Code, then run the same files from the CLI and CI.",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "4.0.0",
|
|
6
6
|
"publisher": "Norn-PeterKrustanov",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Peter Krastanov"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "https://github.com/PeterCrest/Norn"
|
|
13
|
+
"url": "git+https://github.com/PeterCrest/Norn.git"
|
|
14
14
|
},
|
|
15
15
|
"bugs": {
|
|
16
16
|
"url": "https://github.com/PeterCrest/Norn/issues"
|
|
@@ -665,6 +665,6 @@
|
|
|
665
665
|
"tough-cookie": "^6.0.0"
|
|
666
666
|
},
|
|
667
667
|
"bin": {
|
|
668
|
-
"norn": "
|
|
668
|
+
"norn": "dist/cli.js"
|
|
669
669
|
}
|
|
670
670
|
}
|
|
@@ -440,6 +440,11 @@
|
|
|
440
440
|
"minimum": 1,
|
|
441
441
|
"description": "Provider-specific maximum output-token ceiling for each agent invocation."
|
|
442
442
|
},
|
|
443
|
+
"max_input_tokens": {
|
|
444
|
+
"type": "integer",
|
|
445
|
+
"minimum": 1,
|
|
446
|
+
"description": "Optional cumulative provider-reported input-token budget for one top-level agent invocation subtree."
|
|
447
|
+
},
|
|
443
448
|
"max_depth": {
|
|
444
449
|
"type": "integer",
|
|
445
450
|
"minimum": 1,
|
|
@@ -492,6 +497,11 @@
|
|
|
492
497
|
"minimum": 1,
|
|
493
498
|
"description": "Maximum output-token ceiling for each agent invocation. This is a ceiling, not a spend; hosted providers default to 16000 and local defaults to 4096."
|
|
494
499
|
},
|
|
500
|
+
"max_input_tokens": {
|
|
501
|
+
"type": "integer",
|
|
502
|
+
"minimum": 1,
|
|
503
|
+
"description": "Optional cumulative provider-reported input-token budget for one top-level agent invocation subtree. When omitted, Norn records usage without applying this spend guardrail."
|
|
504
|
+
},
|
|
495
505
|
"max_depth": {
|
|
496
506
|
"type": "integer",
|
|
497
507
|
"minimum": 1,
|
|
@@ -536,6 +546,9 @@
|
|
|
536
546
|
"openai": {
|
|
537
547
|
"$ref": "#/definitions/agentProviderLimits"
|
|
538
548
|
},
|
|
549
|
+
"openai_compatible": {
|
|
550
|
+
"$ref": "#/definitions/agentProviderLimits"
|
|
551
|
+
},
|
|
539
552
|
"anthropic": {
|
|
540
553
|
"$ref": "#/definitions/agentProviderLimits"
|
|
541
554
|
},
|
package/CLAUDE.md
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# Norn Extension — Claude Code instructions
|
|
2
|
-
|
|
3
|
-
## Read these first
|
|
4
|
-
|
|
5
|
-
1. **[`NOW.md`](NOW.md)** — the tool-agnostic active-work board shared by every agent and by
|
|
6
|
-
Peter. Whatever sits under **Active now** is the current priority. This board, not any
|
|
7
|
-
assistant's memory, is the source of truth for what we are working on.
|
|
8
|
-
2. **[`AGENTS.md`](AGENTS.md)** — the full repo instructions (CLI parity requirement, how to
|
|
9
|
-
run the local CLI, code style, testing). Everything in that file applies here.
|
|
10
|
-
|
|
11
|
-
## Skills: consult `.github/skills/` — always
|
|
12
|
-
|
|
13
|
-
This repo keeps its accumulated implementation knowledge in **[`.github/skills/`](.github/skills/)**,
|
|
14
|
-
maintained by every agent that works here. Treat it as required reading, not an optional
|
|
15
|
-
reference.
|
|
16
|
-
|
|
17
|
-
**Before touching any area of the codebase, check `.github/skills/` for a skill covering it
|
|
18
|
-
and read that skill.** Do this even for changes that look small — the skills exist precisely
|
|
19
|
-
because these areas have non-obvious rules that are easy to break.
|
|
20
|
-
|
|
21
|
-
Current skills and what they cover:
|
|
22
|
-
|
|
23
|
-
| Skill | Read it when working on |
|
|
24
|
-
|-------|------------------------|
|
|
25
|
-
| `norn-syntax-highlighting` | TextMate grammars in `syntaxes/`, token scopes, highlighting bugs |
|
|
26
|
-
| `norn-agent-workbench` | `.nornagent` sidecars, agent `run` steps, providers, contracts, record/replay, agent graph |
|
|
27
|
-
| `norn-intellisense-preferences` | Completion providers, IntelliSense behaviour |
|
|
28
|
-
| `norn-value-resolution` | Variables, interpolation, `.nornenv`, value precedence |
|
|
29
|
-
| `add-norn-keyword` | Adding a keyword to the `.norn` language |
|
|
30
|
-
| `add-assertion-operator` | Adding an assertion operator |
|
|
31
|
-
| `add-sequence-step-type` | Adding a new sequence step type |
|
|
32
|
-
| `add-imported-sidecar-language` | Adding a new imported sidecar file type |
|
|
33
|
-
| `norn-cli-integration` | `src/cli.ts` and CLI parity |
|
|
34
|
-
| `norn-editor-decorations` | Drawing onto the editor — end-of-line chips, hiding markup, folding, caret traps |
|
|
35
|
-
| `norn-debugger-integration` | DAP / debugger work |
|
|
36
|
-
| `norn-response-panel` | The response webview panel |
|
|
37
|
-
| `norn-test-explorer` | VS Code Test Explorer integration |
|
|
38
|
-
| `norn-sequence-tags` | Sequence tags and filtering |
|
|
39
|
-
| `norn-api-coverage` | `.nornapi` coverage features |
|
|
40
|
-
| `norn-mcp-tools` | MCP client and tool wiring |
|
|
41
|
-
| `norn-k8s` | Kubernetes features |
|
|
42
|
-
| `norn-terminal` | Norn Terminal / PTY work |
|
|
43
|
-
| `norn-env-templates` | `.nornenv` templates |
|
|
44
|
-
| `norn-publishing` | Packaging and publishing the extension |
|
|
45
|
-
| `norn-starter-catalog` | Starter templates catalog |
|
|
46
|
-
| `norn-title-cards` | Title cards |
|
|
47
|
-
| `demo-task-helper` | Building demos |
|
|
48
|
-
| `readme-sync` | Keeping README/docs in sync with features |
|
|
49
|
-
|
|
50
|
-
The table can go stale — list the directory when the work doesn't obviously map to a row.
|
|
51
|
-
|
|
52
|
-
### Maintaining skills
|
|
53
|
-
|
|
54
|
-
Per `AGENTS.md`, skills are living documents:
|
|
55
|
-
|
|
56
|
-
- If a skill is **wrong or outdated**, fix it as part of the change.
|
|
57
|
-
- If a skill is **missing** for an area you just learned, write one in Agent Skills format.
|
|
58
|
-
- After creating or editing any skill, run `npm run validate:skills`.
|
|
59
|
-
|
|
60
|
-
Capture the lesson that was hard to learn, not a restatement of the code.
|
|
61
|
-
|
|
62
|
-
## Verification
|
|
63
|
-
|
|
64
|
-
Never use `npx norn` — that runs the published package, not local changes:
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
npm run compile
|
|
68
|
-
node ./dist/cli.js tests/file.norn --env prelive
|
|
69
|
-
```
|
package/NOW.md
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
# NOW — Active Work Board
|
|
2
|
-
|
|
3
|
-
> **The single source of truth for what we are working on _right now_** — for Peter and
|
|
4
|
-
> for every AI agent (Claude, Codex, Copilot, Cursor, any). Deliberately tool-agnostic:
|
|
5
|
-
> it lives here in the repo, **not** in any one assistant's memory or private notes.
|
|
6
|
-
>
|
|
7
|
-
> We have a lot of planning docs; this board exists so nobody (human or AI) has to guess
|
|
8
|
-
> what's current. If it's on this board, it's the work. If it's not, it isn't — yet.
|
|
9
|
-
|
|
10
|
-
## How this board works
|
|
11
|
-
|
|
12
|
-
- **Empty by default.** Nothing is "current" unless it has been explicitly **Accepted**
|
|
13
|
-
onto this board by Peter. An empty "Active now" means: no directed work in flight —
|
|
14
|
-
don't start big work off a backlog/plan doc without Peter accepting it here first.
|
|
15
|
-
- **Accepting work:** Peter says "Accept X" (or moves it here). That's the signal it
|
|
16
|
-
becomes current. Until then, ideas live in the plan/backlog docs, not here.
|
|
17
|
-
- **This board is an _index_, not the detail.** Each item is one or two lines + a link to
|
|
18
|
-
its real plan doc. Keep the detail in the linked doc; keep this page short and scannable.
|
|
19
|
-
- **Finishing work:** when an item is done, move it to "Recently done" (keep the last
|
|
20
|
-
handful), then prune. Don't let this page grow.
|
|
21
|
-
- **Agents:** treat items under "Active now" as the current priority order. Do these
|
|
22
|
-
before picking up anything from the wider `Docs/` set.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Active now
|
|
27
|
-
|
|
28
|
-
### 📚 Knowledge Base — the third pillar
|
|
29
|
-
**Accepted 2026-08-15.** Norn covers **models** (providers, contracts, evals, record/replay) and
|
|
30
|
-
**capabilities** (MCP servers and tools). What an agent still cannot get is **context**. The user
|
|
31
|
-
writes prose in a declared folder and Norn derives the structure; because our runs have ground
|
|
32
|
-
truth, *"did editing this document make agent runs better?"* is answerable here and nowhere else.
|
|
33
|
-
Eight stages, ordered by dependency — **1 → 2 → 3** is the spine and where a wrong call is most
|
|
34
|
-
expensive. **Stages 1, 2, 3 and 4 are built as of 2026-08-15.** Stage 1 established declared membership,
|
|
35
|
-
topic identity, source-bounded domains, tombstones and deterministic DAG lineage. Stage 2 replaced
|
|
36
|
-
its temporary JSON cache with one project-global built-in SQLite authority: atomic content-addressed
|
|
37
|
-
snapshots, occurrence-scoped authored metadata, versioned action caches, conservative typed entity
|
|
38
|
-
crossing, FTS5 diagnostics, retention/pins, and a queued extension worker. The calibrated fixture
|
|
39
|
-
kept all nine entity kinds; exact values cross automatically while service spelling aliases remain
|
|
40
|
-
proposals only. The database/WAL byte test proves `.nornenv` values and ciphertext do not persist.
|
|
41
|
-
The CLI remains deliberately narrow and generic at index-backed `knowledge ls --explain`. Stage 3
|
|
42
|
-
now applies sidecar-local grants to one lazily pinned snapshot per top-level run, routes exact typed
|
|
43
|
-
entities with one conservative lexical fallback, assembles literal cache-stable prompt blocks under
|
|
44
|
-
four explicit budgets, offers the bounded `load_knowledge` tool, and writes durable masked SQLite
|
|
45
|
-
run/invocation/domain/topic/load telemetry independently of rolling replay files. Pure replay stays
|
|
46
|
-
offline; a live tail fails closed without its recorded snapshot. The extension awaits its worker
|
|
47
|
-
queue and the compiled local CLI shares identical runner semantics without new subcommands.
|
|
48
|
-
**Stage 4 turned the ordinary Markdown editor into the authoring surface** — extension-only, no CLI
|
|
49
|
-
command. A declared document now shows its own interpretation (topics, effective type and its
|
|
50
|
-
origin, `use_when`, token estimates, exact entities that cross into the project), honest recorded
|
|
51
|
-
use from Stage 3 telemetry, folded metadata, dimmed exclusions, and actions that write
|
|
52
|
-
`type`/`use_when` and exclusions without moving a topic hash. The cost boundary is the design: a
|
|
53
|
-
keystroke parses the open buffer once per version and reads cached memory, while every SQLite read
|
|
54
|
-
runs on the index worker. Counts are raw co-occurrence — distinct completed runs for the headline,
|
|
55
|
-
invocations and earlier lineage versions in their own rows, no causal language anywhere. Nine
|
|
56
|
-
Stage 1, eleven Stage 2, eleven Stage 3 and eighteen Stage 4 focused tests pass under Node 24; 345
|
|
57
|
-
pass across every vscode-free suite. **One gate stays open: the visual smoke test
|
|
58
|
-
(light/dark/high-contrast) needs Peter's eyes — fixture at
|
|
59
|
-
[`demos/knowledge-editor`](demos/knowledge-editor).**
|
|
60
|
-
**Stage 5, the coverage panel, is the next dependency boundary and its cold-session handoff is
|
|
61
|
-
ready.** Reviewing it against the built code moved three things: the mock's `402 runs 94%` is two
|
|
62
|
-
different measurements plus one that does not exist anywhere (`K-20`), a panel row must be an
|
|
63
|
-
endpoint rather than an entity or it contradicts itself on the first screen (`K-48`), and the
|
|
64
|
-
"stale ref" list is where extractor false positives accumulate (`K-46`). Three gates need Peter
|
|
65
|
-
before code: those, plus CLI scope and `K-47`.
|
|
66
|
-
→ **Stage 5 implementation handoff (start here next session):
|
|
67
|
-
[`Docs/to-be-built/knowledge-base-stage-5.md`](Docs/to-be-built/knowledge-base-stage-5.md).**
|
|
68
|
-
→ **Stage 4 build record:
|
|
69
|
-
[`Docs/to-be-built/knowledge-base-stage-4.md`](Docs/to-be-built/knowledge-base-stage-4.md).**
|
|
70
|
-
→ **Stage 3 build record:
|
|
71
|
-
[`Docs/to-be-built/knowledge-base-stage-3.md`](Docs/to-be-built/knowledge-base-stage-3.md).**
|
|
72
|
-
→ Stage 2 build record: [`Docs/to-be-built/knowledge-base-stage-2.md`](Docs/to-be-built/knowledge-base-stage-2.md).
|
|
73
|
-
→ Stage 1 build record: [`Docs/to-be-built/knowledge-base-stage-1.md`](Docs/to-be-built/knowledge-base-stage-1.md).
|
|
74
|
-
→ Decision record: [`knowledge-base-refinement.md`](Docs/to-be-built/knowledge-base-refinement.md)
|
|
75
|
-
(`K-01`…`K-45`, every Stage 1/2 gate shut and the Stage 3 review carried forward). Reasoning lives
|
|
76
|
-
here; **do not re-litigate** — if a decision looks wrong, say so and stop.
|
|
77
|
-
→ Design and the *why*: [`knowledge-base.md`](Docs/to-be-built/knowledge-base.md).
|
|
78
|
-
|
|
79
|
-
**The `K-28` spike answered its question — markdown cuts it, no custom renderer.** It and the
|
|
80
|
-
`playground/knowledgeBase/` development corpus were deleted at the Stage 1 gate. Its durable
|
|
81
|
-
findings remain in skill `norn-editor-decorations`; production knowledge code shares no spike
|
|
82
|
-
foundation.
|
|
83
|
-
|
|
84
|
-
Two decisions the spike overturned, both worth knowing before reading anything else: per-topic
|
|
85
|
-
metadata sits **inline under each heading** (`K-13`), which removed the binding-key problem rather
|
|
86
|
-
than solving it; and **depth counts** for domains (`K-30`), so a folder inside a domain is a
|
|
87
|
-
sub-domain and splitting an oversized domain actually works.
|
|
88
|
-
→ **Refinement worksheet (start here): [`Docs/to-be-built/knowledge-base-refinement.md`](Docs/to-be-built/knowledge-base-refinement.md)**
|
|
89
|
-
→ Parent spec: [`Docs/to-be-built/knowledge-base.md`](Docs/to-be-built/knowledge-base.md)
|
|
90
|
-
→ Runtime floor and SQLite authority are recorded in `K-11` / `K-12` / `K-31`; Stage 2 applied
|
|
91
|
-
them and removed the temporary Stage 1 JSON store.
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## Backlog / awaiting acceptance
|
|
96
|
-
|
|
97
|
-
_Nothing awaiting acceptance._ (Slice 5f was scrapped 2026-08-15 — it had decayed into a reminder
|
|
98
|
-
to update docs. Docs get reviewed as one pass when the website is updated for the knowledge base.
|
|
99
|
-
Its spec remains at [`agent-workbench-slice-5f.md`](Docs/to-be-built/agent-workbench-slice-5f.md)
|
|
100
|
-
if it is ever wanted.)
|
|
101
|
-
|
|
102
|
-
## Parked
|
|
103
|
-
|
|
104
|
-
### Contract & Drift + Voiden-playbook marketing
|
|
105
|
-
The previous direction, parked 2026-08-07 — untested, not failed. Stages 1+2 are built and
|
|
106
|
-
shipped in 2.9.8; the QA dogfood and the "why Norn, now" hook never happened. Full
|
|
107
|
-
narrative and how to resume:
|
|
108
|
-
[`Docs/random/parked_contract_drift_and_marketing.md`](Docs/random/parked_contract_drift_and_marketing.md)
|
|
109
|
-
|
|
110
|
-
## Recently done
|
|
111
|
-
|
|
112
|
-
- **3.2.0 — the knowledge base ships, quietly** (2026-08-19) — **done.** Stages 1–4 of the
|
|
113
|
-
Knowledge Base are in the published build: declared `knowledge` blocks in `.nornagent`,
|
|
114
|
-
the project-global SQLite authority, grant-scoped routing with the bounded `load_knowledge`
|
|
115
|
-
tool, and the Markdown authoring surface. **Deliberately undocumented in public** — Peter's
|
|
116
|
-
call at release time. Stages 5–8 are unbuilt and the coverage panel is still the next
|
|
117
|
-
dependency boundary, so the website says nothing about it and neither does the README. The
|
|
118
|
-
feature is reachable by anyone who writes the block; that is accepted, not overlooked.
|
|
119
|
-
**Agent graph details now open on click, not hover.** The 650ms open delay and 120ms hide
|
|
120
|
-
delay are gone: the reader either waited on a canvas that looked inert or was chased across
|
|
121
|
-
it by a popover they never asked for. A second click closes, Escape dismisses, and the
|
|
122
|
-
navigation that used to live on the tile — reveal source, step replay, reveal declaration —
|
|
123
|
-
is now one labelled button in the detail footer, so a tile has exactly one gesture. The
|
|
124
|
-
website's two "hover an agent or a boundary" sentences were updated to match, and the
|
|
125
|
-
response panel's agent invocation tree got the docs line it never had.
|
|
126
|
-
Verified at 426 Extension Host tests and 198/200 regression; the two failures are the live
|
|
127
|
-
SQL Server Docker fixture, which was not running and was accepted as a known failure.
|
|
128
|
-
|
|
129
|
-
- **Agent results view** (2026-08-14; performance follow-up 2026-08-15) — **done.** The normal
|
|
130
|
-
response panel has a safe themed invocation tree; rows/details now start collapsed and large
|
|
131
|
-
MCP values remain host-side until reveal/copy. Streamed contract/agent rows also have distinct
|
|
132
|
-
toggle identities. → [Spec](Docs/to-be-built/agent-results-view.md)
|
|
133
|
-
- **3.1.0 — MCP authoring parity, and the changelog is gone** (2026-08-13) — **done.**
|
|
134
|
-
Three fixes that all came out of one painful hour authoring a real server by hand. **`header`
|
|
135
|
-
now reads like HTTP**: `header Name: value` is accepted alongside the original no-colon form,
|
|
136
|
-
because the colon form is what a `.norn` request writes and what everyone arrives with —
|
|
137
|
-
rejecting it silently produced a header *named* `Authorization:` and the only complaint came
|
|
138
|
-
from `Headers.append` at dial time, three layers from the line at fault. The name is now
|
|
139
|
-
validated against the RFC 9110 token set at parse time, so an unsendable one is red in the
|
|
140
|
-
editor. The header name and value carry `.norn`'s own `entity.name.tag.http` /
|
|
141
|
-
`string.unquoted.http` scopes, so the identical header is the identical colour in both files,
|
|
142
|
-
and the quoted-string pattern is gone from that rule — it advertised an escape that does not
|
|
143
|
-
exist, and quotes really do end up inside the token. IntelliSense offers the shared
|
|
144
|
-
`commonHeaders` list after `header `, inserting the colon form and chaining into the
|
|
145
|
-
`Bearer {{$env.…}}` value completion.
|
|
146
|
-
**`run mcp` completions now see sidecar-declared servers.** The precedence rule lived in three
|
|
147
|
-
copies and the completion copy was left on config-only, so a project that declared every server
|
|
148
|
-
in its sidecar offered no aliases at all while running perfectly. It is now one shared module,
|
|
149
|
-
`src/mcpAliasScope.ts`, and the alias carries its origin — which matters because the tools cache
|
|
150
|
-
is written beside whatever *declared* the server, so tool-name completions and signature help
|
|
151
|
-
were reading the wrong `.norn-cache` even once an alias resolved. **Config `mcp.servers` stays**:
|
|
152
|
-
supporting both is one function and one line per caller, so retiring it would have been strictly
|
|
153
|
-
more work than keeping it.
|
|
154
|
-
**`CHANGELOG.md` deleted** at Peter's call — the release record is git history plus this board,
|
|
155
|
-
and a changelog was a third place saying the same thing and the likeliest to drift.
|
|
156
|
-
Also fixed: `@vscode/test-electron` 2.5.2 could not launch VS Code 1.133 (the binary was renamed
|
|
157
|
-
from `Electron` to `Code`), which had left the Extension Host suite dark.
|
|
158
|
-
- **Agent Workbench — declared MCP servers** (2026-08-12) — **done, all five items.**
|
|
159
|
-
Agents can now *do* things. A `.nornagent` sidecar declares the servers it uses beside the
|
|
160
|
-
models it uses — `mcp <Alias> … end mcp`, stdio and http, credentials as `.nornenv`
|
|
161
|
-
references — and each agent is granted a whole server with `mcp <Alias>` or named tools with
|
|
162
|
-
`tools <Alias>.<tool>`. The two stay visibly different statements on purpose: a granted tool
|
|
163
|
-
executes when the model asks, so the authored grant *is* the permission boundary and dropping
|
|
164
|
-
four characters must not widen it from one tool to twenty. `session run | agent | call` makes
|
|
165
|
-
shared server state a decision rather than a surprise; `run` stays the default because five
|
|
166
|
-
agents testing one website are one testing session, and `session agent` is how an agent asks
|
|
167
|
-
not to inherit a login it never earned. Config `mcp.servers` keeps working with the sidecar
|
|
168
|
-
winning, `run mcp` steps reach declared servers so a server is provable with no model and no
|
|
169
|
-
cost, and a granted server is a real node on the canvas — **declared servers only**, per
|
|
170
|
-
Peter: the graph draws the program the file describes.
|
|
171
|
-
Four corrections the build forced on the spec, all recorded in it: grant expansion **cannot**
|
|
172
|
-
live above the runner (pure replay never runs that preflight, and a derived root definition
|
|
173
|
-
never reaches sub-agents), so it is an injected hook with a live and a replay implementation;
|
|
174
|
-
drift between record and replay is only knowable where a live listing happens, so the
|
|
175
|
-
acceptance criterion demanding both was impossible as written; preflight needed a session
|
|
176
|
-
boundary of its own, or `session agent` was already broken by the check that proves an agent's
|
|
177
|
-
tools exist; and the spec's own demo sketch used an input expression the language does not
|
|
178
|
-
have. Fixed along the way: a templated URL resolving differently mid-run silently reused the
|
|
179
|
-
old session, and a refused connection reported the SDK's "fetch failed" instead of naming what
|
|
180
|
-
it dialled. 60 focused tests across five items; 256 pass across every runnable suite, plus
|
|
181
|
-
type-check, lint, bundles, skill validation, and regression at 198/200 (two live-SQL-Server
|
|
182
|
-
sequences needing a database).
|
|
183
|
-
→ Spec: [`Docs/to-be-built/agent-workbench-mcp-servers.md`](Docs/to-be-built/agent-workbench-mcp-servers.md)
|
|
184
|
-
· Demo: [`demos/mcp-ticket-testing`](demos/mcp-ticket-testing)
|
|
185
|
-
- **Agent Workbench Slice 5e — reporting** (2026-08-10) — **done.**
|
|
186
|
-
A verdict is now visible in the response panel, the canvas, the debugger, and JUnit, derived
|
|
187
|
-
once through `agentHopPresentation` so the surfaces cannot disagree. The load-bearing move was
|
|
188
|
-
putting the verdict on `AgentHopResult` rather than the step: a recording persists the trace
|
|
189
|
-
and never step results, so a step-only verdict vanished the moment a past run was reopened —
|
|
190
|
-
the round-trip is now a test. Two bugs the build surfaced: a judged hop has `success === true`
|
|
191
|
-
even when the verdict failed, so the canvas drew a **green node for a failed run** until the
|
|
192
|
-
verdict was counted explicitly; and the runner emits its `completed` event before the verdict
|
|
193
|
-
exists, so the deep-cloned event log needed patching or replay playback showed no verdict.
|
|
194
|
-
Also: judge statements now draw a sequence handoff edge from whatever produced the value they
|
|
195
|
-
judge, live progress is emitted as `stepType: 'judge'`, and JUnit gets one testcase per
|
|
196
|
-
statement with `ExpectationsUnmet` and `JudgeError` kept distinct. 13 new focused tests; 159
|
|
197
|
-
pass across every runnable suite, plus type-check, lint, bundles, regression, skill validation,
|
|
198
|
-
and a real CLI run producing JUnit.
|
|
199
|
-
→ [`Docs/to-be-built/agent-workbench-slice-5e.md`](Docs/to-be-built/agent-workbench-slice-5e.md)
|
|
200
|
-
- **Agent Workbench Slice 5c — judge execution and the result contract** (2026-08-10) — **done,
|
|
201
|
-
one deliverable deferred.** A `judge` statement now runs its judge and produces a verdict.
|
|
202
|
-
The design that carries it: **the model is never asked for an overall result** — it rules on
|
|
203
|
-
each expectation and quotes evidence, and Norn computes `passed`, checks one ruling per
|
|
204
|
-
expectation with indexes in range, and verifies each quote occurs in the judged text. A judge
|
|
205
|
-
is an ordinary agent invocation; everything judge-specific lives in a derived definition, so
|
|
206
|
-
there is no judge runtime and no `if (isJudge)` in the runner. `builtInContracts` generalises
|
|
207
|
-
"a contract that lives in code" and rides the existing generation/validation path.
|
|
208
|
-
A provider or contract failure sets an error and no verdict, so "the judge said no" never
|
|
209
|
-
reads like "the judge never answered". **A failed judge stops the sequence**, the same as a
|
|
210
|
-
failed `assert` — it is the concluding check on an orchestration, so later steps must not run
|
|
211
|
-
on a disproved premise; the run is still recorded and replayable. A malformed reply *shape* is
|
|
212
|
-
corrected up to `contract_retries` via the opt-in `retryReturnsContract` (off for `run
|
|
213
|
-
<Agent>`); the verdict itself is never re-rolled. Evidence that cannot be found in the judged
|
|
214
|
-
text is flagged and still passes — strictness is intended to become per-expectation, not
|
|
215
|
-
global. **Deferred:** the automatic replay drift check —
|
|
216
|
-
`expectationsHash` is recorded and diffable, but pure replay never re-reads the `.norn`, so
|
|
217
|
-
the comparison needs `runLineNumber` plumbing and lands with 5d, which owns the policy.
|
|
218
|
-
Fixed along the way: judge hops reported false prompt drift on every replay. 18 new focused
|
|
219
|
-
tests; 141 pass across every runnable suite, plus type-check, lint, bundles, regression, skill
|
|
220
|
-
validation, and a real CLI run against a deterministic judge server.
|
|
221
|
-
→ [`Docs/to-be-built/agent-workbench-slice-5c.md`](Docs/to-be-built/agent-workbench-slice-5c.md)
|
|
222
|
-
---
|
|
223
|
-
_Last updated: 2026-08-15_
|