norn-cli 3.3.0 → 4.0.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.
Files changed (58) hide show
  1. package/dist/cli.js +1396 -276
  2. package/dist/knowledgeIndexWorker.js +71 -35
  3. package/package.json +3 -3
  4. package/schemas/norn.config.schema.json +13 -0
  5. package/CLAUDE.md +0 -69
  6. package/NOW.md +0 -223
  7. package/demos/agent-workbench/README.md +0 -156
  8. package/demos/agent-workbench/agents.nornagent +0 -87
  9. package/demos/agent-workbench/contracts/domain-answer.schema.json +0 -28
  10. package/demos/agent-workbench/contracts/domain-question.schema.json +0 -34
  11. package/demos/agent-workbench/contracts/router-verdict.schema.json +0 -43
  12. package/demos/agent-workbench/contracts/ticket.schema.json +0 -29
  13. package/demos/agent-workbench/contracts/verdict.schema.json +0 -48
  14. package/demos/agent-workbench/fake-openai-server.js +0 -128
  15. package/demos/agent-workbench/fixtures/aligned-ticket.json +0 -10
  16. package/demos/agent-workbench/fixtures/cross-domain-ticket.json +0 -10
  17. package/demos/agent-workbench/fixtures/invalid-output-ticket.json +0 -8
  18. package/demos/agent-workbench/norn.config.json +0 -15
  19. package/demos/agent-workbench/prompts/ticket-router.md +0 -11
  20. package/demos/agent-workbench/workbench.norn +0 -32
  21. package/demos/knowledge-editor/README.md +0 -74
  22. package/demos/knowledge-editor/agents.nornagent +0 -16
  23. package/demos/knowledge-editor/api.nornapi +0 -10
  24. package/demos/knowledge-editor/handbook/ops/flow.svg +0 -1
  25. package/demos/knowledge-editor/handbook/ops/retired.md +0 -9
  26. package/demos/knowledge-editor/handbook/ops/runbook.md +0 -14
  27. package/demos/knowledge-editor/handbook/payments/charges.md +0 -25
  28. package/demos/knowledge-editor/handbook/payments/pricing-tiers.draft.md +0 -7
  29. package/demos/knowledge-editor/handbook/payments/refunds/policy.md +0 -12
  30. package/demos/knowledge-editor/support.norn +0 -10
  31. package/demos/mcp-ticket-testing/README.md +0 -114
  32. package/demos/mcp-ticket-testing/agents.nornagent +0 -77
  33. package/demos/mcp-ticket-testing/contracts/test-run.schema.json +0 -31
  34. package/demos/mcp-ticket-testing/expectations/proj-142.md +0 -12
  35. package/demos/mcp-ticket-testing/fixtures/proj-142.json +0 -13
  36. package/demos/mcp-ticket-testing/prompts/backend-tester.md +0 -12
  37. package/demos/mcp-ticket-testing/prompts/frontend-tester.md +0 -14
  38. package/demos/mcp-ticket-testing/prompts/reporter.md +0 -10
  39. package/demos/mcp-ticket-testing/servers/browser-server.js +0 -133
  40. package/demos/mcp-ticket-testing/servers/house-server.js +0 -125
  41. package/demos/mcp-ticket-testing/tickets.norn +0 -32
  42. package/demos/nornenv-region-refactor/README.md +0 -64
  43. package/demos/nornenv-showcase/README.md +0 -62
  44. package/demos/nornenv-showcase/norn.config.json +0 -16
  45. package/demos/nornenv-showcase/showcase.norn +0 -70
  46. package/demos/nornenv-showcase/showcase.nornapi +0 -26
  47. package/demos/nornenv-showcase/showcase.nornsql +0 -20
  48. package/demos/tests-showcase/01-single-requests.norn +0 -31
  49. package/demos/tests-showcase/02-sequences.norn +0 -54
  50. package/demos/tests-showcase/03-sidecars.norn +0 -42
  51. package/demos/tests-showcase/04-api-plus-sql.norn +0 -27
  52. package/demos/tests-showcase/db/testDb.nornsql +0 -12
  53. package/demos/tests-showcase/demo-api.nornapi +0 -17
  54. package/demos/tests-showcase/norn.config.json +0 -16
  55. package/playground/ai.norn +0 -15
  56. package/playground/ai_orchastration.nornagent +0 -34
  57. package/playground/knowedge_base/ACDC/testing_notes_new_clients.md +0 -11
  58. package/playground/knowedge_base/nexus_system_prompt.md +0 -1
@@ -1,156 +0,0 @@
1
- # Agent Workbench Slice 2
2
-
3
- This demo includes the Slice 1 linear graph and a Slice 2 model-driven graph.
4
- `TicketRouter` decides which domain experts to invoke as tools, while Norn
5
- contract-checks both sides of every handoff. It uses no MCP server. The checked-in
6
- model declaration uses OpenAI, but you can swap it to Google, Anthropic, or an
7
- OpenAI-compatible local runtime (subject to each model's tool/structured-output
8
- support).
9
-
10
- The checked-in [`norn.config.json`](./norn.config.json) spells out the runtime
11
- defaults used by the demo: 16,000 output tokens for hosted providers, 4,096 for
12
- `local`, depth 5, 25 total invocations, and 2 failed contract attempts.
13
- Agent recording is enabled by default and keeps the latest 20 artifacts under
14
- `.norn-cache/runs/`.
15
-
16
- ## Run it
17
-
18
- Build the local CLI, export a key, then run the local build:
19
-
20
- ```bash
21
- npm run compile
22
- export OPENAI_API_KEY="your-key"
23
- node ./dist/cli.js demos/agent-workbench/workbench.norn --sequence HappyPath
24
- ```
25
-
26
- The variable name is not a convention Norn assumes — it is written in the model
27
- block in [agents.nornagent](./agents.nornagent) as `apiKey {{$env.OPENAI_API_KEY}}`,
28
- so any name works as long as the file and the environment agree.
29
-
30
- Alternatively, store `OPENAI_API_KEY` as a secret under `[env:openai]` in a
31
- local `.nornenv`. Norn gives the selected `.nornenv` values precedence over
32
- process environment variables; add `--env openai` to the commands in that case.
33
-
34
- `HappyPath` runs `TicketReader -> CriteriaComparer -> Responder`. The first two
35
- agents produce structured output; `accepts` checks each incoming handoff and
36
- `returns` checks each outgoing handoff. The final result remains available to
37
- ordinary Norn assertions and interpolation through `verdict.body` and
38
- `reply.text`.
39
-
40
- ## Demonstrate model-driven delegation and correction
41
-
42
- ```bash
43
- node ./dist/cli.js demos/agent-workbench/workbench.norn --sequence DelegatedRouting
44
- ```
45
-
46
- `TicketRouter` consults `FrontendExpert` and `BackendExpert`. Its prompt is the
47
- longest in the graph, so it lives in
48
- [`prompts/ticket-router.md`](./prompts/ticket-router.md) and is pulled in with
49
- `system file prompts/ticket-router.md`. The file's text is the prompt verbatim,
50
- which is why the quoted instructions there need no escaping. For the checked-in
51
- `DEMO-201` fixture, its instructions deliberately make the first backend
52
- question omit the contract-only `DOMAIN QUESTION:` prefix. That `pattern` is
53
- removed from the provider's generation schema but remains in Norn's untouched
54
- validation contract, so the calling model receives the exact field-level error,
55
- retries with conforming input, and completes. The human trace shows the nested
56
- agents and `retry 1/2 — model corrected the call`; `--json` preserves the same
57
- graph under each hop's `children` array.
58
-
59
- For a deterministic, no-key proof of the exact violate → correct → pass
60
- trace, start the checked-in OpenAI-compatible demo server in one terminal:
61
-
62
- ```bash
63
- node demos/agent-workbench/fake-openai-server.js
64
- ```
65
-
66
- Then run the normal demo from another terminal:
67
-
68
- ```bash
69
- OPENAI_BASE_URL=http://127.0.0.1:18766/v1 \
70
- OPENAI_API_KEY=demo \
71
- node ./dist/cli.js demos/agent-workbench/workbench.norn --sequence DelegatedRouting
72
- ```
73
-
74
- The fake server is only a deterministic demo fixture. Normal runs continue to
75
- use the model declared in `agents.nornagent` and the provider credentials below.
76
-
77
- To inspect the machine-readable run artifact:
78
-
79
- ```bash
80
- node ./dist/cli.js demos/agent-workbench/workbench.norn --sequence DelegatedRouting --json
81
- ```
82
-
83
- After either run, replay its newest `.norn-cache/runs/*.json` artifact without
84
- the fake server, an API key, or any model/tool side effects:
85
-
86
- ```bash
87
- node ./dist/cli.js replay .norn-cache/runs/<recording>.json
88
- node ./dist/cli.js replay .norn-cache/runs/<recording>.json --from BackendExpert
89
- ```
90
-
91
- The first command is fully offline. The second replays everything before the
92
- selected hop and then switches to the currently configured live provider from
93
- that hop onward.
94
-
95
- ## Demonstrate a broken handoff
96
-
97
- ```bash
98
- node ./dist/cli.js demos/agent-workbench/workbench.norn --sequence ContractFailure
99
- ```
100
-
101
- The failure fixture asks `CriteriaComparer` for the summary `deliberately
102
- invalid`, while the untouched contract permits only `aligned` or `drift found`.
103
- The provider-generation schema omits the unsupported `pattern` constraint, but
104
- Norn then validates the parsed output against the full schema. The run should
105
- therefore fail at `summary`, retain the raw model text, and exit non-zero.
106
-
107
- Model output is nondeterministic. If the model declines the deliberate bad-output
108
- instruction, rerun the failure sequence or use the injected mock adapter in a
109
- focused runtime test for deterministic automation.
110
-
111
- ## Swap the model
112
-
113
- Edit the one `model Workbench` block in
114
- [agents.nornagent](./agents.nornagent). Every agent references the alias, so the
115
- whole graph moves together:
116
-
117
- ```nornagent
118
- model Workbench
119
- provider openai
120
- name gpt-4o
121
- apiKey {{$env.OPENAI_API_KEY}}
122
- end model
123
- ```
124
-
125
- The other providers, with the variable names this demo assumes. `apiKey` and
126
- `baseUrl` accept any `.nornenv` variable — these are only the conventional names:
127
-
128
- ```nornagent
129
- model Workbench
130
- provider google # native Gemini Developer API, never Vertex AI
131
- name gemini-3-flash-preview
132
- apiKey {{$env.GOOGLE_API_KEY}}
133
- end model
134
-
135
- model Workbench
136
- provider anthropic
137
- name claude-sonnet-4-6
138
- apiKey {{$env.ANTHROPIC_API_KEY}}
139
- end model
140
-
141
- model Workbench
142
- provider local # any OpenAI-compatible runtime
143
- name qwen3-coder
144
- baseUrl {{$env.LOCAL_BASE_URL}} # required for local
145
- apiKey {{$env.LOCAL_API_KEY}} # optional
146
- end model
147
- ```
148
-
149
- Omitting `apiKey` falls back to each provider's conventional variable
150
- (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY` or `GEMINI_API_KEY`) and
151
- then to the vendor SDK's own credential chain. Declaring it is preferred — it is
152
- the difference between a file that documents what it needs and one that fails at
153
- the provider call. For Google, `baseUrl` is only for a compatible proxy: leave it
154
- unset for normal use and never paste a full `:generateContent` operation URL.
155
-
156
- There is no CLI model override: the authored graph stays the source of truth.
@@ -1,87 +0,0 @@
1
- # Agent Workbench Slice 2 demo
2
- #
3
- # Swap this block to compare the same graph on another model. Nothing is implied:
4
- # the provider, the model, and the variable holding the key are all named here.
5
- # apiKey points at a .nornenv variable — run with --env to select the environment.
6
- model Workbench
7
- provider openai
8
- name gpt-4o
9
- apiKey {{$env.OPENAI_API}}
10
- end model
11
-
12
- agent TicketReader
13
- model Workbench
14
- accepts "contracts/ticket.schema.json"
15
- returns "contracts/ticket.schema.json"
16
- system "
17
- Normalize the supplied ticket into the required JSON structure.
18
- Preserve the ticket's meaning and every acceptance criterion.
19
- Return JSON only.
20
- "
21
- end agent
22
-
23
- agent CriteriaComparer
24
- model Workbench
25
- accepts contracts/ticket.schema.json
26
- returns contracts/verdict.schema.json
27
- system "
28
- Compare implementationNotes with acceptanceCriteria.
29
- Return drifted=false with no findings when every criterion is covered.
30
- Otherwise return drifted=true and one finding per uncovered criterion.
31
- Use only low, medium, or high for finding severity.
32
-
33
- For the explicit contract-failure fixture, whose implementationNotes begin
34
- with DEMO_INVALID_OUTPUT, set summary exactly to deliberately invalid.
35
- This instruction exists only to demonstrate Norn's full post-generation
36
- contract check: the generation adapter intentionally cannot enforce the
37
- schema's pattern keyword.
38
- "
39
- end agent
40
-
41
- agent Responder
42
- model Workbench
43
- accepts contracts/verdict.schema.json
44
- system "
45
- Turn the verdict into a short neutral message for the developer.
46
- Use no preamble and do not invent findings.
47
- "
48
- end agent
49
-
50
- agent FrontendExpert
51
- model Workbench
52
- describe "
53
- Call when a ticket may affect browser or client-facing behaviour. Give it
54
- the complete ticket and one specific frontend question.
55
- "
56
- accepts contracts/domain-question.schema.json
57
- returns contracts/domain-answer.schema.json
58
- system "
59
- You own the frontend domain. Answer only the supplied question. Identify
60
- concrete checks without inventing requirements.
61
- "
62
- end agent
63
-
64
- agent BackendExpert
65
- model Workbench
66
- describe "
67
- Call when a ticket may affect APIs, services, persistence, or server-side
68
- behaviour. Give it the complete ticket and one specific backend question.
69
- "
70
- accepts contracts/domain-question.schema.json
71
- returns contracts/domain-answer.schema.json
72
- system "
73
- You own the backend domain. Answer only the supplied question. Identify
74
- concrete checks without inventing requirements.
75
- "
76
- end agent
77
-
78
- agent TicketRouter
79
- model Workbench
80
- accepts "contracts/ticket.schema.json"
81
- returns "contracts/router-verdict.schema.json"
82
- agents FrontendExpert, BackendExpert
83
- # The longest prompt in this graph lives in its own Markdown file. The path is
84
- # relative to this sidecar, and the file's text is the prompt verbatim — quotes
85
- # need no escaping at all, which is why this one is easier to read there.
86
- system file prompts/ticket-router.md
87
- end agent
@@ -1,28 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "Domain consultation answer",
4
- "type": "object",
5
- "additionalProperties": false,
6
- "required": [
7
- "domain",
8
- "affected",
9
- "checks",
10
- "rationale"
11
- ],
12
- "properties": {
13
- "domain": {
14
- "type": "string",
15
- "enum": ["frontend", "backend"]
16
- },
17
- "affected": {
18
- "type": "boolean"
19
- },
20
- "checks": {
21
- "type": "array",
22
- "items": { "type": "string" }
23
- },
24
- "rationale": {
25
- "type": "string"
26
- }
27
- }
28
- }
@@ -1,34 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "Domain consultation question",
4
- "type": "object",
5
- "additionalProperties": false,
6
- "required": [
7
- "ticket",
8
- "question"
9
- ],
10
- "properties": {
11
- "ticket": {
12
- "type": "object",
13
- "required": [
14
- "id",
15
- "title",
16
- "acceptanceCriteria",
17
- "implementationNotes"
18
- ],
19
- "properties": {
20
- "id": { "type": "string" },
21
- "title": { "type": "string" },
22
- "acceptanceCriteria": {
23
- "type": "array",
24
- "items": { "type": "string" }
25
- },
26
- "implementationNotes": { "type": "string" }
27
- }
28
- },
29
- "question": {
30
- "type": "string",
31
- "pattern": "^DOMAIN QUESTION: .+"
32
- }
33
- }
34
- }
@@ -1,43 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "Domain routing verdict",
4
- "type": "object",
5
- "additionalProperties": false,
6
- "required": [
7
- "domains",
8
- "consultations",
9
- "summary"
10
- ],
11
- "properties": {
12
- "domains": {
13
- "type": "array",
14
- "items": {
15
- "type": "string",
16
- "enum": ["frontend", "backend"]
17
- }
18
- },
19
- "consultations": {
20
- "type": "array",
21
- "items": {
22
- "type": "object",
23
- "additionalProperties": false,
24
- "required": ["domain", "affected", "checks", "rationale"],
25
- "properties": {
26
- "domain": {
27
- "type": "string",
28
- "enum": ["frontend", "backend"]
29
- },
30
- "affected": { "type": "boolean" },
31
- "checks": {
32
- "type": "array",
33
- "items": { "type": "string" }
34
- },
35
- "rationale": { "type": "string" }
36
- }
37
- }
38
- },
39
- "summary": {
40
- "type": "string"
41
- }
42
- }
43
- }
@@ -1,29 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "Demo ticket",
4
- "type": "object",
5
- "additionalProperties": false,
6
- "required": [
7
- "id",
8
- "title",
9
- "acceptanceCriteria",
10
- "implementationNotes"
11
- ],
12
- "properties": {
13
- "id": {
14
- "type": "string"
15
- },
16
- "title": {
17
- "type": "string"
18
- },
19
- "acceptanceCriteria": {
20
- "type": "array",
21
- "items": {
22
- "type": "string"
23
- }
24
- },
25
- "implementationNotes": {
26
- "type": "string"
27
- }
28
- }
29
- }
@@ -1,48 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "Acceptance criteria verdict",
4
- "type": "object",
5
- "additionalProperties": false,
6
- "required": [
7
- "drifted",
8
- "findings",
9
- "summary"
10
- ],
11
- "properties": {
12
- "drifted": {
13
- "type": "boolean"
14
- },
15
- "findings": {
16
- "type": "array",
17
- "items": {
18
- "type": "object",
19
- "additionalProperties": false,
20
- "required": [
21
- "criterion",
22
- "severity",
23
- "message"
24
- ],
25
- "properties": {
26
- "criterion": {
27
- "type": "string"
28
- },
29
- "severity": {
30
- "type": "string",
31
- "enum": [
32
- "low",
33
- "medium",
34
- "high"
35
- ]
36
- },
37
- "message": {
38
- "type": "string"
39
- }
40
- }
41
- }
42
- },
43
- "summary": {
44
- "type": "string",
45
- "pattern": "^(aligned|drift found)$"
46
- }
47
- }
48
- }
@@ -1,128 +0,0 @@
1
- const http = require('http');
2
-
3
- const port = Number(process.env.NORN_AGENT_DEMO_PORT || 18766);
4
-
5
- function completion(model, message, finishReason) {
6
- return {
7
- id: `chatcmpl-demo-${Date.now()}`,
8
- object: 'chat.completion',
9
- created: Math.floor(Date.now() / 1000),
10
- model,
11
- choices: [{ index: 0, message, finish_reason: finishReason }],
12
- usage: { prompt_tokens: 24, completion_tokens: 8, total_tokens: 32 }
13
- };
14
- }
15
-
16
- function toolCall(model, name, id, args) {
17
- return completion(model, {
18
- role: 'assistant',
19
- content: null,
20
- tool_calls: [{
21
- id,
22
- type: 'function',
23
- function: { name, arguments: JSON.stringify(args) }
24
- }]
25
- }, 'tool_calls');
26
- }
27
-
28
- function parseJson(value, fallback = {}) {
29
- try {
30
- return JSON.parse(value);
31
- } catch {
32
- return fallback;
33
- }
34
- }
35
-
36
- function expertAnswer(domain) {
37
- return {
38
- domain,
39
- affected: true,
40
- checks: domain === 'frontend'
41
- ? ['Render each retry-attempt event without refreshing checkout']
42
- : ['Emit retry-attempt events and preserve the final safe error mapping'],
43
- rationale: `The ticket explicitly changes ${domain} behaviour.`
44
- };
45
- }
46
-
47
- function handleDemoRequest(body) {
48
- const schemaName = body.response_format?.json_schema?.name;
49
- if (schemaName === 'FrontendExpert_return') {
50
- return completion(body.model, {
51
- role: 'assistant',
52
- content: JSON.stringify(expertAnswer('frontend'))
53
- }, 'stop');
54
- }
55
- if (schemaName === 'BackendExpert_return') {
56
- return completion(body.model, {
57
- role: 'assistant',
58
- content: JSON.stringify(expertAnswer('backend'))
59
- }, 'stop');
60
- }
61
-
62
- const messages = body.messages || [];
63
- const tools = body.tools || [];
64
- const frontendTool = tools.find(tool => tool.function?.name?.includes('FrontendExpert'))?.function?.name;
65
- const backendTool = tools.find(tool => tool.function?.name?.includes('BackendExpert'))?.function?.name;
66
- if (!frontendTool || !backendTool) {
67
- return completion(body.model, { role: 'assistant', content: 'missing demo tools' }, 'stop');
68
- }
69
-
70
- const userMessage = messages.find(message => message.role === 'user');
71
- const ticket = parseJson(String(userMessage?.content || '{}'));
72
- const toolMessages = messages.filter(message => message.role === 'tool');
73
- const lastToolContent = String(toolMessages.at(-1)?.content || '');
74
-
75
- if (toolMessages.length === 0) {
76
- return toolCall(body.model, frontendTool, 'demo-frontend', {
77
- ticket,
78
- question: 'DOMAIN QUESTION: What must the checkout client verify?'
79
- });
80
- }
81
- if (lastToolContent.includes('"domain":"frontend"')) {
82
- return toolCall(body.model, backendTool, 'demo-backend-invalid', {
83
- ticket,
84
- question: 'Does this affect backend services?'
85
- });
86
- }
87
- if (lastToolContent.includes('"error"')) {
88
- return toolCall(body.model, backendTool, 'demo-backend-corrected', {
89
- ticket,
90
- question: 'DOMAIN QUESTION: What must the payment service verify?'
91
- });
92
- }
93
-
94
- return completion(body.model, {
95
- role: 'assistant',
96
- content: JSON.stringify({
97
- domains: ['frontend', 'backend'],
98
- consultations: [expertAnswer('frontend'), expertAnswer('backend')],
99
- summary: 'The ticket crosses the checkout client and payment service boundary.'
100
- })
101
- }, 'stop');
102
- }
103
-
104
- const server = http.createServer((request, response) => {
105
- if (request.method !== 'POST' || !request.url.endsWith('/chat/completions')) {
106
- response.writeHead(404).end();
107
- return;
108
- }
109
-
110
- let raw = '';
111
- request.setEncoding('utf8');
112
- request.on('data', chunk => { raw += chunk; });
113
- request.on('end', () => {
114
- try {
115
- const result = handleDemoRequest(JSON.parse(raw));
116
- response.writeHead(200, { 'content-type': 'application/json' });
117
- response.end(JSON.stringify(result));
118
- } catch (error) {
119
- response.writeHead(500, { 'content-type': 'application/json' });
120
- response.end(JSON.stringify({ error: String(error) }));
121
- }
122
- });
123
- });
124
-
125
- server.listen(port, '127.0.0.1', () => {
126
- process.stdout.write(`Deterministic Agent Workbench demo listening on http://127.0.0.1:${port}/v1\n`);
127
- });
128
- process.on('SIGTERM', () => server.close(() => process.exit(0)));
@@ -1,10 +0,0 @@
1
- {
2
- "id": "DEMO-101",
3
- "title": "Retry transient payment failures",
4
- "acceptanceCriteria": [
5
- "Retry HTTP 429 and 5xx responses up to three times",
6
- "Wait 500 milliseconds between attempts",
7
- "Do not retry HTTP 4xx responses other than 429"
8
- ],
9
- "implementationNotes": "The client retries 429 and 5xx responses up to three times with a fixed 500 millisecond delay. Other 4xx responses are returned immediately."
10
- }
@@ -1,10 +0,0 @@
1
- {
2
- "id": "DEMO-201",
3
- "title": "Show live payment retry state in checkout",
4
- "acceptanceCriteria": [
5
- "The API retries transient payment failures up to three times",
6
- "Checkout shows the current retry attempt without a page refresh",
7
- "The final failure message is consistent in the API and browser"
8
- ],
9
- "implementationNotes": "The payment service now emits retry-attempt events and the checkout client subscribes to them to update its status panel. The final API error is mapped to the same customer-safe message displayed by the client."
10
- }
@@ -1,8 +0,0 @@
1
- {
2
- "id": "DEMO-102",
3
- "title": "Show a contract-checked agent boundary",
4
- "acceptanceCriteria": [
5
- "The comparer output must satisfy the verdict contract"
6
- ],
7
- "implementationNotes": "DEMO_INVALID_OUTPUT: return the deliberately invalid summary requested by the system prompt so post-generation validation reports the exact field mismatch."
8
- }
@@ -1,15 +0,0 @@
1
- {
2
- "version": 1,
3
- "agents": {
4
- "max_tokens": 16000,
5
- "max_depth": 5,
6
- "max_invocations": 25,
7
- "max_turns": 100,
8
- "contract_retries": 2,
9
- "providers": {
10
- "local": {
11
- "max_tokens": 4096
12
- }
13
- }
14
- }
15
- }
@@ -1,11 +0,0 @@
1
- Decide which domains the ticket touches. Consult only relevant domain
2
- agents, one at a time, and combine their answers into the required verdict.
3
-
4
- Every valid consultation question starts with "DOMAIN QUESTION: ". For
5
- ticket DEMO-201, call FrontendExpert with that prefix. On the first
6
- BackendExpert call, deliberately send the question "Does this affect
7
- backend services?" without the prefix. The provider generation schema does
8
- not include the contract-only pattern, so Norn will return the exact
9
- validation error as the tool result. Retry BackendExpert with the prefix,
10
- then use the successful answer. This one deliberate mistake demonstrates a
11
- typed, model-initiated handoff correcting itself.
@@ -1,32 +0,0 @@
1
- import "./agents.nornagent"
2
-
3
- test sequence HappyPath
4
- var fixture = run readJson "./fixtures/aligned-ticket.json"
5
-
6
-
7
- var ticket = run TicketReader fixture
8
- var verdict = run CriteriaComparer ticket
9
- var reply = run Responder verdict
10
-
11
- assert verdict.body.drifted == false
12
- print "Reply" | "{{reply.text}}"
13
- end sequence
14
-
15
- test sequence ContractFailure
16
- var fixture = run readJson "./fixtures/invalid-output-ticket.json"
17
-
18
- var ticket = run TicketReader fixture
19
- var verdict = run CriteriaComparer ticket
20
-
21
- print "Raw comparer output" | "{{verdict.text}}"
22
- end sequence
23
-
24
- test sequence DelegatedRouting
25
- var fixture = run readJson "./fixtures/cross-domain-ticket.json"
26
-
27
- var verdict = run TicketRouter fixture
28
-
29
- assert verdict.body.domains contains "frontend"
30
- assert verdict.body.domains contains "backend"
31
- print "Routing verdict" | "{{verdict.text}}"
32
- end sequence