@niroai/niro 0.17.0 → 0.18.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/CHANGELOG.md +136 -0
- package/README.md +123 -302
- package/bin/niro.js +12 -1
- package/docs/agent-antigravity.md +37 -0
- package/docs/agent-claude-code.md +48 -0
- package/docs/agent-claude-desktop.md +47 -0
- package/docs/agent-cline.md +42 -0
- package/docs/agent-codex.md +45 -0
- package/docs/agent-copilot-cli.md +42 -0
- package/docs/agent-cursor.md +44 -0
- package/docs/agent-factory.md +42 -0
- package/docs/agent-gemini-cli.md +42 -0
- package/docs/agent-goose.md +42 -0
- package/docs/agent-kiro.md +37 -0
- package/docs/agent-opencode.md +37 -0
- package/docs/agent-vscode.md +45 -0
- package/docs/agent-windsurf.md +38 -0
- package/docs/answer-shapes.md +69 -0
- package/docs/builds-and-updates.md +68 -0
- package/docs/cloud-README.md +372 -0
- package/docs/configuring-a-folder.md +51 -0
- package/docs/connect-ci.md +24 -0
- package/docs/connect-overview.md +20 -0
- package/docs/connect-review-bots.md +17 -0
- package/docs/connect-team-server.md +12 -0
- package/docs/connect-terminal.md +49 -0
- package/docs/connect-web-agents.md +9 -0
- package/docs/connect-your-ai-assistant.md +59 -175
- package/docs/coverage-audit-and-consent.md +51 -0
- package/docs/evaluate-what-niro-did.md +271 -0
- package/docs/faq-assistants-and-mcp.md +122 -0
- package/docs/faq-feedback-and-receipts.md +33 -0
- package/docs/faq-indexing.md +127 -0
- package/docs/faq-install-and-setup.md +103 -0
- package/docs/faq-licence-and-plans.md +86 -0
- package/docs/faq-login-and-account.md +69 -0
- package/docs/faq-offline.md +34 -0
- package/docs/faq-performance.md +33 -0
- package/docs/faq-privacy-and-security.md +113 -0
- package/docs/faq-uninstall.md +47 -0
- package/docs/faq-updates.md +59 -0
- package/docs/faq-worktrees-and-branches.md +59 -0
- package/docs/faq-wrong-or-missing-answers.md +275 -0
- package/docs/hosted-only.md +33 -0
- package/docs/how-the-graph-works.md +47 -0
- package/docs/index.md +157 -0
- package/docs/licence-and-plans.md +72 -0
- package/docs/lifecycle-debug.md +48 -0
- package/docs/lifecycle-onboarding.md +32 -0
- package/docs/lifecycle-overview.md +77 -0
- package/docs/lifecycle-plan-and-design.md +44 -0
- package/docs/lifecycle-review.md +30 -0
- package/docs/lifecycle-security-and-privacy.md +35 -0
- package/docs/lifecycle-test.md +28 -0
- package/docs/lifecycle-write-code.md +35 -0
- package/docs/llms-full.txt +8015 -0
- package/docs/llms.txt +126 -0
- package/docs/playbook-add-a-feature.md +108 -0
- package/docs/playbook-change-safely.md +93 -0
- package/docs/playbook-data-and-events.md +107 -0
- package/docs/playbook-debug-after-a-change.md +96 -0
- package/docs/playbook-debug-config.md +95 -0
- package/docs/playbook-debug-exception.md +124 -0
- package/docs/playbook-debug-failing-service-call.md +119 -0
- package/docs/playbook-debug-slow-path.md +80 -0
- package/docs/playbook-debug-wrong-value.md +137 -0
- package/docs/playbook-onboard.md +95 -0
- package/docs/playbook-review-a-change.md +72 -0
- package/docs/playbook-test-a-change.md +73 -0
- package/docs/reference-cli.md +113 -0
- package/docs/reference-config-files.md +42 -0
- package/docs/reference-engine-commands.md +56 -0
- package/docs/reference-environment-variables.md +89 -0
- package/docs/reference-exit-codes.md +67 -0
- package/docs/reference-limits.md +75 -0
- package/docs/reference-mcp-tools.md +326 -0
- package/docs/reference-messages.md +180 -0
- package/docs/secrets-and-masking.md +48 -0
- package/docs/supported-c.md +130 -0
- package/docs/supported-cpp.md +157 -0
- package/docs/supported-csharp.md +211 -0
- package/docs/supported-go.md +258 -0
- package/docs/supported-java.md +226 -0
- package/docs/supported-javascript.md +223 -0
- package/docs/supported-kotlin.md +124 -0
- package/docs/supported-overview.md +89 -0
- package/docs/supported-php.md +157 -0
- package/docs/supported-python.md +224 -0
- package/docs/supported-ruby.md +162 -0
- package/docs/supported-rust.md +150 -0
- package/docs/supported-swift.md +133 -0
- package/docs/supported-typescript.md +220 -0
- package/docs/tokens-speed-and-accuracy.md +99 -0
- package/docs/troubleshooting.md +42 -0
- package/docs/uninstall.md +74 -0
- package/docs/what-leaves-your-machine.md +71 -0
- package/docs/where-things-are-stored.md +59 -0
- package/docs/worktrees-and-branches.md +55 -0
- package/package.json +5 -3
- package/src/commands/add-repo.js +5 -0
- package/src/commands/auditSend.js +67 -0
- package/src/commands/build.js +6 -0
- package/src/commands/docsSend.js +73 -0
- package/src/commands/engine.js +120 -0
- package/src/commands/engineRoutes.js +105 -0
- package/src/commands/guide.js +254 -0
- package/src/commands/init.js +6 -2
- package/src/commands/initLocal.js +96 -0
- package/src/commands/journalSend.js +47 -0
- package/src/commands/login.js +89 -12
- package/src/commands/mcp.js +22 -6
- package/src/commands/plan.js +99 -0
- package/src/commands/repoPush.js +33 -0
- package/src/commands/requestIntegration.js +38 -0
- package/src/commands/share.js +56 -0
- package/src/commands/start.js +451 -371
- package/src/commands/status.js +57 -0
- package/src/commands/takeover.js +6 -0
- package/src/commands/uninstall.js +180 -0
- package/src/commands/upgrade.js +38 -8
- package/src/commands/watch.js +420 -6
- package/src/index.js +174 -3
- package/src/lib/agentApi.js +32 -1
- package/src/lib/api.js +20 -6
- package/src/lib/auditUpload.js +293 -0
- package/src/lib/branchDrift.js +149 -0
- package/src/lib/choose.js +116 -0
- package/src/lib/cloudGuard.js +74 -0
- package/src/lib/credentials.js +2 -2
- package/src/lib/docsMarked.js +55 -0
- package/src/lib/docsState.js +213 -0
- package/src/lib/docsUpload.js +481 -0
- package/src/lib/engine.js +619 -0
- package/src/lib/folderSync.js +2 -1
- package/src/lib/graphCheck.js +40 -0
- package/src/lib/guide.js +293 -0
- package/src/lib/guideSearch.js +252 -0
- package/src/lib/integrationRequest.js +89 -0
- package/src/lib/journalUpload.js +147 -0
- package/src/lib/languages.js +129 -0
- package/src/lib/lease.js +375 -0
- package/src/lib/localBuild.js +285 -0
- package/src/lib/localEngine.js +210 -0
- package/src/lib/localOnboard.js +124 -0
- package/src/lib/localWorktrees.js +243 -0
- package/src/lib/machineCheck.js +58 -0
- package/src/lib/mcpClients.js +10 -1
- package/src/lib/migrationNotice.js +118 -0
- package/src/lib/modelFetch.js +153 -0
- package/src/lib/nestedOutput.js +23 -9
- package/src/lib/onboardingLog.js +34 -0
- package/src/lib/planUsage.js +165 -0
- package/src/lib/progressBar.js +46 -0
- package/src/lib/prompt.js +1 -1
- package/src/lib/redact.js +155 -0
- package/src/lib/release.js +33 -0
- package/src/lib/repoPush.js +495 -0
- package/src/lib/secret.js +1 -1
- package/src/lib/service.js +372 -63
- package/src/lib/shareCode.js +367 -0
- package/src/lib/shareExclusions.js +114 -0
- package/src/lib/shareFiles.js +129 -0
- package/src/lib/spinner.js +9 -5
- package/src/lib/watchDaemon.js +33 -10
- package/src/lib/watchState.js +12 -1
- package/src/lib/windowsPaths.js +67 -0
- package/src/mcp/lineCheck.js +382 -0
- package/src/mcp/lineName.js +92 -0
- package/src/mcp/localBackend.js +751 -0
- package/src/mcp/localMode.js +383 -0
- package/src/mcp/localSkill.js +189 -0
- package/src/mcp/server.js +610 -79
- package/src/mcp/setup.js +519 -131
- package/src/mcp/shellSearchHook.js +509 -0
- package/src/mcp/teardown.js +173 -91
- package/src/mcp/usingNiro.js +39 -0
- package/docs/working-on-branches.md +0 -162
package/docs/llms.txt
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Niro (niro-cli with the local engine)
|
|
2
|
+
|
|
3
|
+
> Niro builds a code graph on the developer's own computer, and serves it to coding agents over MCP and to the developer through `niro ask`. These pages ship inside the @niroai/niro package. Pull request review and root cause analysis are features of hosted Niro, not of this package.
|
|
4
|
+
|
|
5
|
+
How an agent should use this index:
|
|
6
|
+
|
|
7
|
+
1. Read this file once.
|
|
8
|
+
2. Open only the page that matches the question.
|
|
9
|
+
3. When the user pastes an error message, open reference-messages.md first.
|
|
10
|
+
4. Never run `niro start`, `niro login` or `niro uninstall` on the user's behalf. Ask before `niro plan remove`.
|
|
11
|
+
5. Every factual sentence in these pages has its source in an HTML comment beside it.
|
|
12
|
+
|
|
13
|
+
llms-full.txt holds every page below in one file.
|
|
14
|
+
|
|
15
|
+
## Reference and help (most used)
|
|
16
|
+
|
|
17
|
+
- [reference-messages.md](reference-messages.md): every user-visible message with an answer, linked to its explanation
|
|
18
|
+
- [troubleshooting.md](troubleshooting.md): the first three checks, and where each kind of problem is answered
|
|
19
|
+
- [reference-mcp-tools.md](reference-mcp-tools.md): every MCP tool and its inputs, as the engine serves them
|
|
20
|
+
- [reference-cli.md](reference-cli.md): every niro command and option
|
|
21
|
+
- [reference-engine-commands.md](reference-engine-commands.md): the commands niro passes to the engine (ask, ui, env, route-rule and others)
|
|
22
|
+
- [reference-environment-variables.md](reference-environment-variables.md): every environment variable and its default
|
|
23
|
+
- [reference-config-files.md](reference-config-files.md): settings files and keys
|
|
24
|
+
- [reference-exit-codes.md](reference-exit-codes.md): what each exit code means
|
|
25
|
+
- [reference-limits.md](reference-limits.md): every size, time and count limit
|
|
26
|
+
|
|
27
|
+
## FAQ
|
|
28
|
+
|
|
29
|
+
- [faq-install-and-setup.md](faq-install-and-setup.md): installing, the engine, the background watcher
|
|
30
|
+
- [faq-login-and-account.md](faq-login-and-account.md): signing in, accounts
|
|
31
|
+
- [faq-licence-and-plans.md](faq-licence-and-plans.md): line limits, the licence
|
|
32
|
+
- [faq-indexing.md](faq-indexing.md): a folder not added yet, building the graph, files left out
|
|
33
|
+
- [faq-wrong-or-missing-answers.md](faq-wrong-or-missing-answers.md): known gaps in answers, by kind and by language
|
|
34
|
+
- [faq-assistants-and-mcp.md](faq-assistants-and-mcp.md): connecting agents, agents not using Niro
|
|
35
|
+
- [faq-worktrees-and-branches.md](faq-worktrees-and-branches.md): branches, worktrees, temporary projects
|
|
36
|
+
- [faq-privacy-and-security.md](faq-privacy-and-security.md): what is sent, masking, files never read
|
|
37
|
+
- [faq-performance.md](faq-performance.md): slow builds and answers, disk
|
|
38
|
+
- [faq-updates.md](faq-updates.md): upgrading
|
|
39
|
+
- [faq-uninstall.md](faq-uninstall.md): removing Niro from this computer: common questions
|
|
40
|
+
- [faq-offline.md](faq-offline.md): working without a network
|
|
41
|
+
- [faq-feedback-and-receipts.md](faq-feedback-and-receipts.md): the end-of-task line, feedback
|
|
42
|
+
|
|
43
|
+
## Ways in
|
|
44
|
+
|
|
45
|
+
- [connect-overview.md](connect-overview.md): which ways of using Niro work today: coding agents, the terminal, review bots, CI, a shared server
|
|
46
|
+
- [connect-terminal.md](connect-terminal.md): niro ask, niro ui and other terminal use
|
|
47
|
+
- [connect-your-ai-assistant.md](connect-your-ai-assistant.md): connecting coding agents over MCP
|
|
48
|
+
- [connect-review-bots.md](connect-review-bots.md): review bots (not supported locally)
|
|
49
|
+
- [connect-web-agents.md](connect-web-agents.md): browser agents (not in a release yet; use hosted Niro)
|
|
50
|
+
- [connect-ci.md](connect-ci.md): build machines (partial)
|
|
51
|
+
- [connect-team-server.md](connect-team-server.md): a shared server (not in a release yet)
|
|
52
|
+
- [agent-claude-code.md](agent-claude-code.md): Claude Code
|
|
53
|
+
- [agent-codex.md](agent-codex.md): Codex
|
|
54
|
+
- [agent-claude-desktop.md](agent-claude-desktop.md): Claude Desktop
|
|
55
|
+
- [agent-cursor.md](agent-cursor.md): Cursor
|
|
56
|
+
- [agent-windsurf.md](agent-windsurf.md): Windsurf
|
|
57
|
+
- [agent-vscode.md](agent-vscode.md): VS Code (GitHub Copilot)
|
|
58
|
+
- [agent-gemini-cli.md](agent-gemini-cli.md): Gemini CLI
|
|
59
|
+
- [agent-copilot-cli.md](agent-copilot-cli.md): GitHub Copilot CLI
|
|
60
|
+
- [agent-factory.md](agent-factory.md): Factory (droid)
|
|
61
|
+
- [agent-goose.md](agent-goose.md): Goose
|
|
62
|
+
- [agent-opencode.md](agent-opencode.md): OpenCode
|
|
63
|
+
- [agent-cline.md](agent-cline.md): Cline
|
|
64
|
+
- [agent-kiro.md](agent-kiro.md): Kiro
|
|
65
|
+
- [agent-antigravity.md](agent-antigravity.md): Antigravity
|
|
66
|
+
|
|
67
|
+
## How it works
|
|
68
|
+
|
|
69
|
+
- [how-the-graph-works.md](how-the-graph-works.md): what the graph holds, and how to read an answer
|
|
70
|
+
- [builds-and-updates.md](builds-and-updates.md): adding another folder (`niro init`), rebuilding a graph, the background watcher, files left out
|
|
71
|
+
- [worktrees-and-branches.md](worktrees-and-branches.md): one graph per folder and branch, temporary projects, clean-up
|
|
72
|
+
- [where-things-are-stored.md](where-things-are-stored.md): every file Niro writes
|
|
73
|
+
- [what-leaves-your-machine.md](what-leaves-your-machine.md): every upload, and the Data Processing Agreement in short
|
|
74
|
+
- [secrets-and-masking.md](secrets-and-masking.md): masking, files never opened
|
|
75
|
+
- [licence-and-plans.md](licence-and-plans.md): plans, line counting, the licence
|
|
76
|
+
- [coverage-audit-and-consent.md](coverage-audit-and-consent.md): the coverage audit and code sharing
|
|
77
|
+
- [configuring-a-folder.md](configuring-a-folder.md): telling Niro what the code does not say: env values, a service's address, route rules that connect calls between services
|
|
78
|
+
- [uninstall.md](uninstall.md): removing Niro from this computer, or only one folder or one coding agent
|
|
79
|
+
|
|
80
|
+
## Where Niro fits
|
|
81
|
+
|
|
82
|
+
- [lifecycle-overview.md](lifecycle-overview.md): where Niro helps at each stage of work on code: plan, write, review, test, debug
|
|
83
|
+
- [lifecycle-plan-and-design.md](lifecycle-plan-and-design.md): blast radius before a change
|
|
84
|
+
- [lifecycle-write-code.md](lifecycle-write-code.md): agents asking the graph while coding
|
|
85
|
+
- [lifecycle-review.md](lifecycle-review.md): reviewing a branch
|
|
86
|
+
- [lifecycle-test.md](lifecycle-test.md): what a change reaches, which tests reach it
|
|
87
|
+
- [lifecycle-debug.md](lifecycle-debug.md): debugging with Niro: from an error to the code that throws it, its callers and its catches
|
|
88
|
+
- [lifecycle-onboarding.md](lifecycle-onboarding.md): learning a codebase
|
|
89
|
+
- [lifecycle-security-and-privacy.md](lifecycle-security-and-privacy.md): security review with Niro
|
|
90
|
+
- [playbook-debug-exception.md](playbook-debug-exception.md): an error message or stack trace to its thrower, callers and catches
|
|
91
|
+
- [playbook-debug-wrong-value.md](playbook-debug-wrong-value.md): who writes a table or field, which event delivered a value
|
|
92
|
+
- [playbook-debug-failing-service-call.md](playbook-debug-failing-service-call.md): caller, handler, address, two services on one route
|
|
93
|
+
- [playbook-debug-after-a-change.md](playbook-debug-after-a-change.md): what a change reaches, tests first
|
|
94
|
+
- [playbook-debug-config.md](playbook-debug-config.md): settings the code reads, where they are set, calls with no address
|
|
95
|
+
- [playbook-debug-slow-path.md](playbook-debug-slow-path.md): what a slow method calls and where it reads the database
|
|
96
|
+
- [playbook-change-safely.md](playbook-change-safely.md): callers before an edit, overloads, static imports
|
|
97
|
+
- [playbook-add-a-feature.md](playbook-add-a-feature.md): the interface to implement and existing implementations
|
|
98
|
+
- [playbook-review-a-change.md](playbook-review-a-change.md): one blast-radius call for a whole change
|
|
99
|
+
- [playbook-test-a-change.md](playbook-test-a-change.md): the tests that reach a change
|
|
100
|
+
- [playbook-onboard.md](playbook-onboard.md): services, routes, stores, events, types
|
|
101
|
+
- [playbook-data-and-events.md](playbook-data-and-events.md): readers and writers of a table, publishers and handlers of a topic
|
|
102
|
+
- [answer-shapes.md](answer-shapes.md): shorter tool answers: the `answer` input (exists, count, locations, names, match)
|
|
103
|
+
- [tokens-speed-and-accuracy.md](tokens-speed-and-accuracy.md): how tokens, speed and accuracy are measured, and how to measure them on your own work
|
|
104
|
+
- [evaluate-what-niro-did.md](evaluate-what-niro-did.md): the end-of-task line, the call record, `niro impact` over 7 and 30 days, a do-it-yourself A/B
|
|
105
|
+
|
|
106
|
+
## What is supported
|
|
107
|
+
|
|
108
|
+
- [supported-overview.md](supported-overview.md): computers, languages, and library counts per language
|
|
109
|
+
- [supported-c.md](supported-c.md)
|
|
110
|
+
- [supported-cpp.md](supported-cpp.md)
|
|
111
|
+
- [supported-csharp.md](supported-csharp.md)
|
|
112
|
+
- [supported-go.md](supported-go.md)
|
|
113
|
+
- [supported-java.md](supported-java.md)
|
|
114
|
+
- [supported-javascript.md](supported-javascript.md)
|
|
115
|
+
- [supported-kotlin.md](supported-kotlin.md)
|
|
116
|
+
- [supported-php.md](supported-php.md)
|
|
117
|
+
- [supported-python.md](supported-python.md)
|
|
118
|
+
- [supported-ruby.md](supported-ruby.md)
|
|
119
|
+
- [supported-rust.md](supported-rust.md)
|
|
120
|
+
- [supported-swift.md](supported-swift.md)
|
|
121
|
+
- [supported-typescript.md](supported-typescript.md)
|
|
122
|
+
|
|
123
|
+
## Other
|
|
124
|
+
|
|
125
|
+
- [index.md](index.md): the start page for people
|
|
126
|
+
- [hosted-only.md](hosted-only.md): pull request review and root cause analysis (hosted Niro)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Playbook: add a feature
|
|
2
|
+
|
|
3
|
+
**The question, in the user's words:** "I want to add a new required action (a step a user must complete after login). What do I implement, and which existing classes can I copy?"
|
|
4
|
+
|
|
5
|
+
**What you get at the end:** the interface to implement, its methods (with overloads), every existing implementation to copy from, the matching factory interface, and a yes or no on whether a pattern you expect already exists.
|
|
6
|
+
<!-- source: outputs on this page: niro-local 43cce717 as `niro-local ask` from /home/niraj/projects/github/keycloak at 968726fe66, scratch home, 2026-11-10 (scratchpad niro-cli-task-playbooks/runs/feat-*.txt); `niro ask` runs the same engine command (src/commands/engineRoutes.js:28) -->
|
|
7
|
+
|
|
8
|
+
Words used on this page:
|
|
9
|
+
- an **interface** is a list of methods a class promises to have;
|
|
10
|
+
- an **implementation** is a class that keeps that promise;
|
|
11
|
+
- a **subtype** is a class or interface below another one;
|
|
12
|
+
- an **overload** is one of several methods with the same name and different parameters;
|
|
13
|
+
- a **default method** is an interface method that already has a body, so a class need not write it.
|
|
14
|
+
|
|
15
|
+
## The steps
|
|
16
|
+
|
|
17
|
+
### 1. How big is the interface?
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
niro ask get_class_structure class_fqn=org.keycloak.authentication.RequiredActionProvider answer=count
|
|
21
|
+
|
|
22
|
+
INTERFACE org.keycloak.authentication.RequiredActionProvider: 7 methods
|
|
23
|
+
extends org.keycloak.provider.Provider
|
|
24
|
+
15 subtypes below it; ask without answer= for them
|
|
25
|
+
```
|
|
26
|
+
<!-- source: runs/feat-01-count.txt (210 characters) -->
|
|
27
|
+
|
|
28
|
+
### 2. Its methods
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
niro ask get_class_structure class_fqn=org.keycloak.authentication.RequiredActionProvider answer=names
|
|
32
|
+
|
|
33
|
+
methods:
|
|
34
|
+
evaluateTriggers(RequiredActionContext)
|
|
35
|
+
getMaxAuthAge()
|
|
36
|
+
getMaxAuthAge(KeycloakSession)
|
|
37
|
+
initiatedActionCanceled(KeycloakSession, AuthenticationSessionModel)
|
|
38
|
+
initiatedActionSupport()
|
|
39
|
+
processAction(RequiredActionContext)
|
|
40
|
+
requiredActionChallenge(RequiredActionContext)
|
|
41
|
+
```
|
|
42
|
+
<!-- source: runs/feat-02-names.txt (517 characters) -->
|
|
43
|
+
|
|
44
|
+
Note the two `getMaxAuthAge` overloads. Four of the seven are `default` methods, so a new class must write only `evaluateTriggers`, `requiredActionChallenge` and `processAction`.
|
|
45
|
+
<!-- source: keycloak server-spi-private/.../RequiredActionProvider.java:46, :58, :96, :104 (default), :70, :79, :86 (abstract) -->
|
|
46
|
+
|
|
47
|
+
### 3. Existing implementations to copy
|
|
48
|
+
|
|
49
|
+
The full answer (no `answer`) lists the 15 subtypes with their files, and which methods each one implements:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
niro ask get_class_structure class_fqn=org.keycloak.authentication.RequiredActionProvider
|
|
53
|
+
|
|
54
|
+
extended or implemented by (15, up to 3 levels down):
|
|
55
|
+
org.keycloak.authentication.requiredactions.DeleteAccount (implements RequiredActionProvider) services/.../DeleteAccount.java:52
|
|
56
|
+
org.keycloak.authentication.requiredactions.UpdatePassword (implements RequiredActionProvider) services/.../UpdatePassword.java:60
|
|
57
|
+
org.keycloak.authentication.requiredactions.VerifyEmail (implements RequiredActionProvider) services/.../VerifyEmail.java:68
|
|
58
|
+
...
|
|
59
|
+
methods implemented or overridden elsewhere (56 implementations):
|
|
60
|
+
```
|
|
61
|
+
<!-- source: runs/feat-03-full.txt (15,214 characters) -->
|
|
62
|
+
|
|
63
|
+
This answer is large. Ask for it once, when you are choosing what to copy.
|
|
64
|
+
|
|
65
|
+
### 4. The factory that goes with it
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
niro ask get_class_structure class_fqn=org.keycloak.authentication.RequiredActionFactory answer=count
|
|
69
|
+
|
|
70
|
+
INTERFACE org.keycloak.authentication.RequiredActionFactory: 7 methods
|
|
71
|
+
extends org.keycloak.provider.ProviderFactory
|
|
72
|
+
19 subtypes below it; ask without answer= for them
|
|
73
|
+
```
|
|
74
|
+
<!-- source: runs/feat-05-factory.txt (216 characters) -->
|
|
75
|
+
|
|
76
|
+
### 5. Does the pattern you expect already exist?
|
|
77
|
+
|
|
78
|
+
`answer=exists` answers yes or no with a count:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
niro ask code_grep pattern="implements RequiredActionProvider, RequiredActionFactory" answer=exists
|
|
82
|
+
|
|
83
|
+
yes: 12 matching lines in 12 files in the files searched so far
|
|
84
|
+
PARTIAL: the search stopped at its 1.5 s limit after 9653 of 11045 file(s) ...
|
|
85
|
+
```
|
|
86
|
+
<!-- source: runs/feat-04-exists.txt -->
|
|
87
|
+
|
|
88
|
+
So one class often plays both roles. "In the files searched so far" and the PARTIAL line say the count is a floor. To search the rest, call again with the `continue_from` the answer gives, or pass a folder such as `path_pattern=services/`. Until an answer comes back without PARTIAL, treat the count as "at least".
|
|
89
|
+
<!-- source: runs/feat-04-exists.txt ("Call again with continue_from=9653"); niro-local docs/NIRO-LOCAL-ISSUES.md entry "2026-11-10: code_grep counts change with machine load, and a busy machine can give '0 matching lines ... PARTIAL'" (OPEN, committed f1a9fe59); scratchpad niro-cli-task-playbooks-check/reruns (claim check round 1: 59 of 59 graph-tool reruns byte-identical, 7 code_grep reruns differed under load 17 to 30) -->
|
|
90
|
+
|
|
91
|
+
## Why the answer is complete, and where it is not
|
|
92
|
+
|
|
93
|
+
- **Subtypes are listed up to 3 levels down,** by the `extends` and `implements` the code declares.
|
|
94
|
+
- **Finding code by what it does** ("is there already a rate limiter") is not in this release: `find_reusable_code` is off. Search by name with `code_grep` and `list_types`.
|
|
95
|
+
<!-- source: runs/feat-03-full.txt ("up to 3 levels down"); docs/reference-mcp-tools.md ("Two tools are off in this release: find_reusable_code and find_known_issues") -->
|
|
96
|
+
|
|
97
|
+
## What grep would do instead
|
|
98
|
+
|
|
99
|
+
`grep -rln "implements.*RequiredActionProvider"` finds 13 files. The graph lists 15 subtypes. The two extra are `VerifyUserProfile` (extends `UpdateProfile`) and `WebAuthnPasswordlessRegister` (extends `WebAuthnRegister`): they implement the interface through a parent class, so no line in their files names it.
|
|
100
|
+
<!-- source: `grep -rln --include=*.java "implements.*RequiredActionProvider\b\|implements RequiredActionProvider" .` = 13 files (Keycloak 968726fe66, 2026-11-10); runs/feat-03-full.txt (15 subtypes; VerifyUserProfile.java:45 and WebAuthnPasswordlessRegister.java:34 marked "extends") -->
|
|
101
|
+
|
|
102
|
+
## Cost
|
|
103
|
+
|
|
104
|
+
What these steps cost depends on your code and your question. How Niro counts the size of its answers, and how to measure the cost on your own tasks, is in [tokens-speed-and-accuracy.md](tokens-speed-and-accuracy.md).
|
|
105
|
+
|
|
106
|
+
## Related
|
|
107
|
+
|
|
108
|
+
[playbook-change-safely.md](playbook-change-safely.md), [playbook-onboard.md](playbook-onboard.md), [lifecycle-write-code.md](lifecycle-write-code.md).
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Playbook: change code safely
|
|
2
|
+
|
|
3
|
+
**The question, in the user's words:** "I want to change `KeycloakModelUtils.findUserByNameOrEmail` so it stops falling back to a username lookup. What depends on it?"
|
|
4
|
+
|
|
5
|
+
**What you get at the end:** how big the change is, every caller up to three levels, the places to check, and which callers are tests. This is the check to run before an edit; [playbook-debug-after-a-change.md](playbook-debug-after-a-change.md) is the same check after one.
|
|
6
|
+
<!-- source: outputs on this page: niro-local 43cce717 as `niro-local ask` from /home/niraj/projects/github/keycloak at 968726fe66, scratch home, 2026-11-10 (scratchpad niro-cli-task-playbooks/runs/chg-*.txt); `niro ask` runs the same engine command (src/commands/engineRoutes.js:28) -->
|
|
7
|
+
|
|
8
|
+
Words used on this page:
|
|
9
|
+
- the **blast radius** of a change is everything it can affect: what calls the changed code (upstream) and what it calls (downstream);
|
|
10
|
+
- an **overload** is one of several methods with the same name and different parameters;
|
|
11
|
+
- a **static import** lets a Java file call another class's static method with no class name in front, as `findUserByNameOrEmail(...)`.
|
|
12
|
+
|
|
13
|
+
In recorded agent sessions, this was the second most common use of Niro: 144 of 794 calls (18%) mapped callers before an edit (these recordings are mostly Niro's own development, labelled by hand by one agent, about plus or minus 10%).
|
|
14
|
+
<!-- source: scratchpad niro-cli-task-playbooks/TASKS.md (hand labels of 794 recorded calls; one labeller, about plus or minus 10%) -->
|
|
15
|
+
|
|
16
|
+
## The steps
|
|
17
|
+
|
|
18
|
+
### 1. How big is it?
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
niro ask get_blast_radius method_fqn=org.keycloak.models.utils.KeycloakModelUtils.findUserByNameOrEmail answer=count
|
|
22
|
+
|
|
23
|
+
Blast radius for KeycloakModelUtils.findUserByNameOrEmail — 163 affected functions, risk: high (+4 in tests, not counted in risk) (upstream 30, downstream 137)
|
|
24
|
+
```
|
|
25
|
+
<!-- source: runs/chg-10-count.txt (668 characters) -->
|
|
26
|
+
|
|
27
|
+
Upstream (what calls it) is what your change can break: 30 entries. Downstream (what it calls) is 137, but a change to this method's own behaviour does not change what those callees do.
|
|
28
|
+
|
|
29
|
+
### 2. The callers, by name and depth
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
niro ask get_blast_radius method_fqn=org.keycloak.models.utils.KeycloakModelUtils.findUserByNameOrEmail direction=upstream answer=names
|
|
33
|
+
|
|
34
|
+
upstream:
|
|
35
|
+
AbstractUsernameFormAuthenticator.getUserFromForm (depth 1)
|
|
36
|
+
AbstractUsernameFormAuthenticator.getUser (depth 2)
|
|
37
|
+
...
|
|
38
|
+
ValidateUsername.authenticate (depth 1)
|
|
39
|
+
OrganizationAuthenticator.resolveUser (depth 1)
|
|
40
|
+
DefaultCIBALoginUserResolver.getUserFromLoginHint (depth 1)
|
|
41
|
+
BackchannelAuthenticationEndpoint.authorizeClient (depth 3)
|
|
42
|
+
AuthenticationManager.lookupUserForBruteForceLog (depth 1)
|
|
43
|
+
ResourceOwnerPasswordCredentialsGrantType.process (depth 3)
|
|
44
|
+
PassThroughAuthenticator.authenticate [test] (depth 1)
|
|
45
|
+
UsernameOnlyAuthenticator.authenticate [test] (depth 1)
|
|
46
|
+
[shown all 30 entries; ask without answer= for signatures and code]
|
|
47
|
+
```
|
|
48
|
+
<!-- source: runs/chg-11-names.txt (2,403 characters, 30 entries) -->
|
|
49
|
+
|
|
50
|
+
So the change reaches the browser login form, direct grant (password) logins, X.509 logins, organization logins and CIBA (backchannel) logins. Those are the flows to test.
|
|
51
|
+
|
|
52
|
+
### 3. The places, to open one by one
|
|
53
|
+
|
|
54
|
+
`answer=locations` gives `file:line` and the exact id of each caller, which `get_source_code` takes as it is. For the direct callers only, use `get_reverse_call_chain` (it lists direct callers and, with `depth`, the levels above).
|
|
55
|
+
<!-- source: docs/answer-shapes.md; docs/reference-mcp-tools.md (get_reverse_call_chain) -->
|
|
56
|
+
|
|
57
|
+
### 4. Overloads are answered one by one
|
|
58
|
+
|
|
59
|
+
A short name that matches several methods is answered once per match when you give `answer`:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
niro ask get_reverse_call_chain method_fqn=ErrorResponse.error answer=count
|
|
63
|
+
|
|
64
|
+
## ErrorResponse.error(String, Response.Status) (overload 1 of 2)
|
|
65
|
+
ErrorResponse.error has 100 callers.
|
|
66
|
+
## ErrorResponse.error(String, Object[], Response.Status) (overload 2 of 2)
|
|
67
|
+
ErrorResponse.error has 3 callers.
|
|
68
|
+
```
|
|
69
|
+
<!-- source: runs/chg-20-overloads.txt (667 characters) -->
|
|
70
|
+
|
|
71
|
+
## Why the answer is complete, and where it is not
|
|
72
|
+
|
|
73
|
+
- **All direct callers are listed, including a bare call through a static import.** `OrganizationAuthenticator.resolveUser` calls `findUserByNameOrEmail(session, realm, username)` with no class name in front.
|
|
74
|
+
<!-- source: keycloak services/.../OrganizationAuthenticator.java:353 (bare call); runs/chg-11-names.txt (listed at depth 1) -->
|
|
75
|
+
- **"confidence: PARTIAL" on step 1** means the walk stopped at depth 3 with more callers beyond; it says how many entries were cut there. Ask `depth=4` or `depth=5` if the far end matters.
|
|
76
|
+
<!-- source: runs/chg-10-count.txt ("128 node(s) marked '[+N more beyond depth]'"); niro-local src/niro_local/mcp/impact.py:40-41 -->
|
|
77
|
+
- **Known gap: methods declared on a parent interface.** Asking about an implementation, or the declaring interface, can answer "0 callers" when callers use a child interface's name. Ask the name the callers use. See [faq-wrong-or-missing-answers.md](faq-wrong-or-missing-answers.md).
|
|
78
|
+
<!-- source: niro-local docs/NIRO-LOCAL-ISSUES.md entry "2026-11-10: a call to an interface method inherited from a parent interface lands on a stand-in, so the declaring method and its implementations read '0 callers, complete'" (OPEN, committed f1a9fe59); runs/chg-03-impl-full.txt and runs/chg-07-iface-loc.txt -->
|
|
79
|
+
|
|
80
|
+
## What grep would do instead
|
|
81
|
+
|
|
82
|
+
- **Level 1 works:** `grep -rn "findUserByNameOrEmail("` gives 10 lines in 9 files: the declaration, 7 production calls and 2 test-provider calls. But `grep "KeycloakModelUtils.findUserByNameOrEmail"`, the obvious search, finds `OrganizationAuthenticator` only through its `import static` line (68), not the call itself (353), which has no class name in front.
|
|
83
|
+
- **Level 2 and 3 do not scale.** The level-1 callers include `authenticate`, `getUser` and `action`. In Keycloak, `grep -F "getUser("` matches 560 lines in 237 files, and `grep -F "action("` 827 lines in 205 files, most of them unrelated methods of the same name.
|
|
84
|
+
- **Overloads look the same to grep.** `grep "ErrorResponse.error("` gives 184 lines in 47 files with no way to split them by overload. The graph's caller files for the two overloads together are the same 47 files, so the lists agree, and the graph adds which overload each one calls.
|
|
85
|
+
<!-- source: Keycloak 968726fe66, 2026-11-10: `grep -rn --include=*.java "findUserByNameOrEmail(" .` (10 lines, 9 files; OrganizationAuthenticator.java:353 has no class prefix, :68 is `import static org.keycloak.models.utils.KeycloakModelUtils.findUserByNameOrEmail`); `grep -F` counts ("getUser(" 560 lines, 237 files; "action(" 827 lines, 205 files); `grep -rn --include=*.java "ErrorResponse.error(" .` 184 lines in 47 files; caller files from runs/eval-01-loc.txt and runs/eval-02-loc-all.txt = 47, `comm` of the two sorted lists empty both ways -->
|
|
86
|
+
|
|
87
|
+
## Cost
|
|
88
|
+
|
|
89
|
+
What these steps cost depends on your code and your question. How Niro counts the size of its answers, and how to measure the cost on your own tasks, is in [tokens-speed-and-accuracy.md](tokens-speed-and-accuracy.md).
|
|
90
|
+
|
|
91
|
+
## Related
|
|
92
|
+
|
|
93
|
+
[playbook-debug-after-a-change.md](playbook-debug-after-a-change.md), [playbook-test-a-change.md](playbook-test-a-change.md), [lifecycle-plan-and-design.md](lifecycle-plan-and-design.md).
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Playbook: data stores and events
|
|
2
|
+
|
|
3
|
+
**The questions, in the user's words:** "We are renaming a column of the `orders` table: what reads or writes it, in which services?" and "I am changing the payload of the `orders.placed` topic: who publishes it, and who consumes it?"
|
|
4
|
+
|
|
5
|
+
**What you get at the end:** every method that reads, writes or deletes a store, across services and through ORM repositories; and for a message topic, its publishers and handlers, plus the events that have only one side.
|
|
6
|
+
<!-- source: outputs on this page: niro-local 43cce717 from /home/niraj/projects/github/niro-coverage-corpus/java-mono (68 synthetic services and a main that calls them; niro-coverage-corpus/README.md), scratch home, 2026-11-10 (scratchpad niro-cli-task-playbooks/runs/data-10-orders.txt, de-*.txt); `niro ask` runs the same engine command (src/commands/engineRoutes.js:28) -->
|
|
7
|
+
|
|
8
|
+
Words used on this page: an **event** is a message one piece of code publishes on a named **topic** (or queue) and other code handles; a **broker** is the server that carries it (Kafka, RabbitMQ, Redis and others).
|
|
9
|
+
|
|
10
|
+
In recorded agent sessions (mostly Niro's own development, labelled by hand by one agent, about plus or minus 10%), agents asked data and event questions with text search: 10 of 21 calls were `code_grep` and 1 was `list_data_resources`. The tools below answer these directly.
|
|
11
|
+
<!-- source: scratchpad niro-cli-task-playbooks/TASKS.md (hand labels of 794 recorded calls) -->
|
|
12
|
+
|
|
13
|
+
## A table across services
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
niro ask get_data_resource_usage resource_fqn=orders
|
|
17
|
+
|
|
18
|
+
sql:table:orders
|
|
19
|
+
read by (4):
|
|
20
|
+
LedgerDao.openOrders
|
|
21
|
+
OrderRepository.findByStatus
|
|
22
|
+
OrderRepository.findLate
|
|
23
|
+
OrderStoreService.open
|
|
24
|
+
written by (2):
|
|
25
|
+
SharedTables.createOrder
|
|
26
|
+
OrderStoreService.save
|
|
27
|
+
deleted by (1):
|
|
28
|
+
OrderStoreService.remove
|
|
29
|
+
```
|
|
30
|
+
<!-- source: runs/data-10-orders.txt (268 characters) -->
|
|
31
|
+
|
|
32
|
+
These are in three services: `main` (`SharedTables`, plain SQL), `jdbc` (`LedgerDao`, plain SQL) and `spring-data-jpa` (`OrderRepository` and `OrderStoreService`, through Spring Data). `findByStatus` is a derived query: its SQL comes from the method's name and the entity's `@Table(name = "orders")`, and no line of code names the table.
|
|
33
|
+
<!-- source: java-mono main/.../SharedTables.java:23 (INSERT INTO orders); services/jdbc/.../LedgerDao.java:54 (SELECT ... FROM orders); services/spring-data-jpa/.../OrderEntity.java:8 (@Table(name = "orders")), OrderRepository.java:10 (findByStatus), OrderStoreService.java:12-16; services/spring-data-jpa/EXPECTED.json (same five spring-data-jpa uses) -->
|
|
34
|
+
|
|
35
|
+
Then, for each method, `get_blast_radius method_fqn=... direction=upstream answer=names` gives what calls it.
|
|
36
|
+
|
|
37
|
+
**What grep would do instead:** `grep -rniw orders` prints 53 lines in the Java files, mostly route paths (`/api/orders`), host names (`orders-api`) and topic names. The SQL lines are in there, but `OrderStoreService.save` and `remove` are not: they call `repository.save(order)` and `repository.deleteById(id)`.
|
|
38
|
+
<!-- source: `grep -rniw "orders" java-mono --include=*.java | wc -l` = 53 (2026-11-10); OrderStoreService.java:14, :16 -->
|
|
39
|
+
|
|
40
|
+
## A topic: who publishes and who handles
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
niro ask get_event_producers_and_handlers name=invoices.created
|
|
44
|
+
|
|
45
|
+
rabbitmq:invoices.created [spring-amqp]
|
|
46
|
+
published by (1):
|
|
47
|
+
EventPublishers.invoiceCreated
|
|
48
|
+
handled by (1):
|
|
49
|
+
InvoiceListener.onInvoice
|
|
50
|
+
```
|
|
51
|
+
<!-- source: runs/de-03-ph.txt (149 characters) -->
|
|
52
|
+
|
|
53
|
+
The publisher is in `main` (`rabbitTemplate.convertAndSend("invoices.created", ...)`) and the handler in `spring-amqp` (`@RabbitListener(queues = "invoices.created")`).
|
|
54
|
+
<!-- source: java-mono main/.../EventPublishers.java:55; services/spring-amqp/.../InvoiceListener.java:14 -->
|
|
55
|
+
|
|
56
|
+
### A trap a text search shows you
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
niro ask get_event_producers_and_handlers name=orders.placed
|
|
60
|
+
|
|
61
|
+
kafka:orders.placed [kafka-clients]
|
|
62
|
+
published by (1):
|
|
63
|
+
EventPublishers.orderPlaced
|
|
64
|
+
handled by (1):
|
|
65
|
+
OrderAuditConsumer.run
|
|
66
|
+
```
|
|
67
|
+
<!-- source: runs/de-05-orders.txt -->
|
|
68
|
+
|
|
69
|
+
A text search shows a second line that looks like a publish: `TrapCaller.run` calls `outbox.send("orders.placed", "x")`. But `outbox` is a class of the project's own that happens to be called `KafkaTemplate`, not Spring's Kafka client. Niro recognises a publish by the type the call is made on, so it leaves this one out. An agent with only a text search has to read the class to rule it out; in the A/B (task p-07), the shell-only agents did, in both runs.
|
|
70
|
+
<!-- source: java-mono main/.../traps/TrapCaller.java:12, :20 ("Must not become a publish to orders.placed"); runs/ts-02-out.txt note ("A call is recognised by the TYPE of the thing it is called on"); scratchpad niro-cli-task-playbooks/ab/judged-p1/p-07-a.json and judged-p2/p-07-a.json (TrapCaller item FOUND) -->
|
|
71
|
+
|
|
72
|
+
### And where Niro misses, today
|
|
73
|
+
|
|
74
|
+
The same topic has two more consumers that this release does not read: a Kafka Streams topology (`builder.stream("orders.placed")`) and a Camel route (`from("kafka:orders.placed?...")`). Both are recorded gaps. A text search for the topic name finds them, so for a payload change, run `code_grep pattern=orders.placed answer=locations` as well.
|
|
75
|
+
<!-- source: java-mono services/kafka-clients/.../OrderEnrichmentTopology.java:9; services/camel/.../BillingRoutes.java:11; niro-coverage-corpus/catalog/java.json rows java.messaging.kafka-streams.builder and java.messaging.camel.route-uris (status pending, measured missed) -->
|
|
76
|
+
|
|
77
|
+
## Events with one side
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
niro ask find_orphaned_events limit=8
|
|
81
|
+
|
|
82
|
+
20 event(s) with only one side:
|
|
83
|
+
carts.recovered [MESSAGE_QUEUE, nats]
|
|
84
|
+
handled here, nothing publishes it
|
|
85
|
+
giftcards.issued [MESSAGE_PUBLISH, spring-kafka]
|
|
86
|
+
published here, nothing handles it
|
|
87
|
+
...
|
|
88
|
+
```
|
|
89
|
+
<!-- source: runs/de-02-orphans.txt -->
|
|
90
|
+
|
|
91
|
+
One side missing means one of three things, and the answer says so: the other side is outside this project, its library is not recognised, or the two sides name the event differently.
|
|
92
|
+
<!-- source: runs/de-02-orphans.txt (the explanatory paragraph) -->
|
|
93
|
+
|
|
94
|
+
## Why the answer is complete, and where it is not
|
|
95
|
+
|
|
96
|
+
- **Stores and events are read from known libraries and forms.** Each language page lists them, see [supported-overview.md](supported-overview.md). A form not listed (like the two consumers above) is missing without a warning.
|
|
97
|
+
- **Topics are matched by name.** A topic name built at run time, or read from a setting Niro has no value for, cannot be matched.
|
|
98
|
+
- **An in-process event** (a framework's own event bus) never leaves its service, and the answer marks it so.
|
|
99
|
+
<!-- source: runs/de-01-events.txt ("An event marked in-process never leaves the program that sends it"); docs/how-the-graph-works.md -->
|
|
100
|
+
|
|
101
|
+
## Cost
|
|
102
|
+
|
|
103
|
+
What these steps cost depends on your code and your question. How Niro counts the size of its answers, and how to measure the cost on your own tasks, is in [tokens-speed-and-accuracy.md](tokens-speed-and-accuracy.md).
|
|
104
|
+
|
|
105
|
+
## Related
|
|
106
|
+
|
|
107
|
+
[playbook-debug-wrong-value.md](playbook-debug-wrong-value.md), [playbook-onboard.md](playbook-onboard.md), [faq-wrong-or-missing-answers.md](faq-wrong-or-missing-answers.md).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Playbook: "it broke after my change"
|
|
2
|
+
|
|
3
|
+
**The question, in the user's words:** "I changed how credentials are created and removed, and now something else fails. What does my change reach?"
|
|
4
|
+
|
|
5
|
+
**What you get at the end:** everything that calls the changed methods (up to the depth you ask), which of those are tests, the routes and other services on the way, and what the changed methods call in turn.
|
|
6
|
+
<!-- source: Keycloak outputs on this page: niro-local 43cce717 as `niro-local ask` from /home/niraj/projects/github/keycloak at 968726fe66, scratch home, 2026-11-10 (scratchpad niro-cli-task-playbooks/runs/brk-*.txt); java-mono outputs from niro-coverage-corpus/java-mono (runs/xs-05-blast.txt); `niro ask` runs the same engine command (src/commands/engineRoutes.js:28) -->
|
|
7
|
+
|
|
8
|
+
Words used on this page: the **blast radius** of a change is everything it can affect: the methods that call the changed code (**upstream**) and the methods it calls (**downstream**).
|
|
9
|
+
|
|
10
|
+
## The steps
|
|
11
|
+
|
|
12
|
+
### 1. Name every method you changed, in one call
|
|
13
|
+
|
|
14
|
+
`method_fqns` takes a list, and treats it as one change. Start with the count, to see how big it is:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
niro ask get_blast_radius method_fqns='["org.keycloak.models.jpa.JpaUserCredentialStore.createCredentialEntity","org.keycloak.models.jpa.JpaUserCredentialStore.removeCredentialEntity"]' answer=count
|
|
18
|
+
|
|
19
|
+
Blast radius for 2 methods — 46 affected functions, risk: high (+6 in tests, not counted in risk) (upstream 27, downstream 25)
|
|
20
|
+
confidence: PARTIAL | has_more: true — ... What is cut is only the far end of the chain: 22 node(s) marked "[+N more beyond depth]" have callers further out that were not walked.
|
|
21
|
+
[counts only; ask without answer= for the list]
|
|
22
|
+
```
|
|
23
|
+
<!-- source: runs/brk-01-multi.txt (634 characters); method_fqns "Several methods analysed as ONE change, in one call" (docs/reference-mcp-tools.md, get_blast_radius) -->
|
|
24
|
+
|
|
25
|
+
"PARTIAL" here means the walk stopped at the depth you asked (3 by default), not that something was missed inside it.
|
|
26
|
+
<!-- source: runs/brk-01-multi.txt ("What is listed is correct. Every DIRECT dependent is listed"); niro-local src/niro_local/mcp/impact.py:40-41 (depth 1 to 5) -->
|
|
27
|
+
|
|
28
|
+
### 2. List the names, upstream only
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
niro ask get_blast_radius method_fqns='[...same two...]' direction=upstream answer=names
|
|
32
|
+
|
|
33
|
+
Blast radius for 2 methods — 21 affected functions, risk: high (+6 in tests, not counted in risk) (upstream 27)
|
|
34
|
+
upstream:
|
|
35
|
+
JpaUserCredentialStore.createCredential (depth 1)
|
|
36
|
+
UserCredentialManager.createStoredCredential (depth 2)
|
|
37
|
+
SubjectCredentialManagerCacheAdapter.createStoredCredential (depth 3)
|
|
38
|
+
PasswordCredentialProvider.createCredential (depth 3)
|
|
39
|
+
...
|
|
40
|
+
CredentialModelTest.testCredentialCRUD [test] (depth 3)
|
|
41
|
+
...
|
|
42
|
+
ResetOTP.authenticate (depth 3)
|
|
43
|
+
UserResource.removeCredential (depth 3)
|
|
44
|
+
JpaUserProvider.removeStoredCredential (depth 1)
|
|
45
|
+
[shown all 27 entries; ask without answer= for signatures and code]
|
|
46
|
+
```
|
|
47
|
+
<!-- source: runs/brk-02-names.txt (2,231 characters, 27 entries) -->
|
|
48
|
+
|
|
49
|
+
This is the list to check against the failure. Entries marked `[test]` are the tests that already reach the change: run those first. `UserResource.removeCredential` is an admin REST method, so the change reaches the admin API.
|
|
50
|
+
|
|
51
|
+
### 3. When the changed code is a route, other services count too
|
|
52
|
+
|
|
53
|
+
From the synthetic java-mono corpus, after a change to the `GET /api/shipments/{id}` handler:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
niro ask get_blast_radius method_fqn=com.example.shop.shipping.ShippingMain.shipment answer=names
|
|
57
|
+
|
|
58
|
+
Blast radius for ShippingMain.shipment — 3 affected functions, risk: low (upstream 3)
|
|
59
|
+
upstream:
|
|
60
|
+
ShippingMain.routing (depth 1)
|
|
61
|
+
ShippingMain.main (depth 2)
|
|
62
|
+
ApacheHttpClient5Calls.shipment over HTTP (depth 1)
|
|
63
|
+
```
|
|
64
|
+
<!-- source: runs/xs-05-blast.txt (371 characters) -->
|
|
65
|
+
|
|
66
|
+
"over HTTP" marks a caller in another service: here `main`, which calls the route by its address. `get_impact_analysis http_path=/api/shipments/{param}` gives the same list starting from the route.
|
|
67
|
+
<!-- source: runs/xs-06-impact.txt -->
|
|
68
|
+
|
|
69
|
+
### 4. Read only what the list points at
|
|
70
|
+
|
|
71
|
+
Each full entry carries `file:start-end`, so `get_source_code` or a ranged file read opens exactly that method. Ask for `answer=locations` when you want the places without the code.
|
|
72
|
+
<!-- source: runs/chg-03-impl-full.txt ("Layout: each method's line carries its <file>:<start>-<end>"); docs/answer-shapes.md -->
|
|
73
|
+
|
|
74
|
+
## Why the answer is complete, and where it is not
|
|
75
|
+
|
|
76
|
+
- **Every direct caller is listed, and every caller up to the depth.** The calls through an interface are followed: `SubjectCredentialManagerCacheAdapter` is reached although the tests only name `credentialManager()`, an interface.
|
|
77
|
+
<!-- source: runs/brk-02-names.txt; keycloak tests/base/.../CredentialModelTest.java:56 (`user.credentialManager().createStoredCredential(otp2)`) -->
|
|
78
|
+
- **Callers through a value whose type the code does not state** are counted as "Not listed" and named, so you can read them.
|
|
79
|
+
<!-- source: docs/faq-wrong-or-missing-answers.md "Not listed: N call(s) ... on a receiver of unknown type" -->
|
|
80
|
+
- **A method declared on a parent interface** can show fewer callers than it has, in this release. See [faq-wrong-or-missing-answers.md](faq-wrong-or-missing-answers.md).
|
|
81
|
+
<!-- source: niro-local docs/NIRO-LOCAL-ISSUES.md entry "2026-11-10: a call to an interface method inherited from a parent interface lands on a stand-in, so the declaring method and its implementations read '0 callers, complete'" (OPEN, committed f1a9fe59) -->
|
|
82
|
+
|
|
83
|
+
## What grep would do instead
|
|
84
|
+
|
|
85
|
+
- **One level costs one search per name.** `createCredentialEntity` has 2 direct callers; each of those has callers; at depth 3 there are 27 entries to find, so a grep route needs a search for each name it meets.
|
|
86
|
+
- **Common names explode.** Level 3 here includes `authenticate`, `action` and `createCredential`. In Keycloak, `grep -F "authenticate("` matches 108 lines in 79 files and `grep -F "action("` 827 lines in 205 files.
|
|
87
|
+
- **Interface calls do not name the class that runs.** In one of our Keycloak A/B sets, every item the shell-only agent missed was an implementation reached through an interface.
|
|
88
|
+
<!-- source: grep -rn --include=*.java -F counts on Keycloak 968726fe66, 2026-11-10 ("authenticate(" 108 lines in 79 files, "action(" 827 lines in 205 files); scratchpad niro-local-receipt-recheck/NATIVE-CHOICES.md ("Every one of the 21 c4 misses is 'Implementations of <Interface>.<method>'") -->
|
|
89
|
+
|
|
90
|
+
## Cost
|
|
91
|
+
|
|
92
|
+
Full entries are much larger than names, so ask for names first and the full entries only where you need them. What these steps cost depends on your code and your question. How Niro counts the size of its answers, and how to measure the cost on your own tasks, is in [tokens-speed-and-accuracy.md](tokens-speed-and-accuracy.md).
|
|
93
|
+
|
|
94
|
+
## Related
|
|
95
|
+
|
|
96
|
+
[playbook-change-safely.md](playbook-change-safely.md) (the same check before you edit), [playbook-test-a-change.md](playbook-test-a-change.md), [playbook-review-a-change.md](playbook-review-a-change.md).
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Playbook: debug config and environment
|
|
2
|
+
|
|
3
|
+
**The question, in the user's words:** "This call works on my machine and fails in staging. Where does its address come from, which settings does this service read, and does Niro know their values?"
|
|
4
|
+
|
|
5
|
+
**What you get at the end:** every setting a folder's code reads, where each is read, whether Niro has a value for it, and the lines in config files that set it, with secrets masked.
|
|
6
|
+
<!-- source: outputs on this page: niro-local 43cce717 from /home/niraj/projects/github/niro-coverage-corpus/java-mono (synthetic, placeholder credentials only: niro-coverage-corpus/README.md), scratch home, 2026-11-10 (scratchpad niro-cli-task-playbooks/runs/cfg-*.txt); `niro env`, `niro ask` and `niro route-rule` pass to the engine (src/commands/engineRoutes.js:28-33) -->
|
|
7
|
+
|
|
8
|
+
Words used on this page: a **setting** is a named value the code reads at run time, from an environment variable or a config file; a **base address** is the `scheme://host:port` a service is reached at.
|
|
9
|
+
|
|
10
|
+
## The steps
|
|
11
|
+
|
|
12
|
+
### 1. Which settings does the code read?
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
niro env list --path main
|
|
16
|
+
|
|
17
|
+
BASE_URL http://localhost:8100 computed
|
|
18
|
+
shop.orders.base-url no value main/src/main/java/com/example/shop/main/http/RestTemplateCalls.java:15
|
|
19
|
+
```
|
|
20
|
+
<!-- source: `niro-local env list --path main` in java-mono, 2026-11-10 -->
|
|
21
|
+
|
|
22
|
+
Each line is a setting, its value, and who set it ("computed" means Niro worked it out). `shop.orders.base-url` is read by `@Value("${shop.orders.base-url}")` at line 15, and Niro holds no value for it.
|
|
23
|
+
<!-- source: java-mono main/.../RestTemplateCalls.java:15; `niro env --help` ("list: name, where found, value, and who set it") -->
|
|
24
|
+
|
|
25
|
+
### 2. Where is it set?
|
|
26
|
+
|
|
27
|
+
`code_grep` also searches config, build and deploy files, with values masked:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
niro ask code_grep pattern="base-url" answer=locations
|
|
31
|
+
|
|
32
|
+
4 matching lines in 3 files
|
|
33
|
+
main/src/main/java/com/example/shop/main/http/RestTemplateCalls.java:15
|
|
34
|
+
main/EXPECTED.json:112
|
|
35
|
+
main/EXPECTED.json:168
|
|
36
|
+
main/src/main/resources/application.yml:5
|
|
37
|
+
```
|
|
38
|
+
<!-- source: runs/cfg-10-key.txt (258 characters) -->
|
|
39
|
+
|
|
40
|
+
Reading the config lines themselves shows the value, and masks what looks like a secret:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
niro ask code_grep pattern="base-url|password" path_pattern=main/src/main/resources/ match_mode=regex
|
|
44
|
+
|
|
45
|
+
[{"file":".../main/src/main/resources/application.yml","matches":[
|
|
46
|
+
{"line":5,"text":" base-url: http://orders-api:8101"},
|
|
47
|
+
{"line":15,"text":" password: <MASK_SECRET_002>"}],"truncated":false}]
|
|
48
|
+
```
|
|
49
|
+
<!-- source: runs/cfg-11-yml.txt; masking: docs/secrets-and-masking.md -->
|
|
50
|
+
|
|
51
|
+
In this run `application.yml` sets the value, but `niro env list` still said "no value". Give Niro the value for the environment you are debugging, so its links follow that environment:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
niro env set shop.orders.base-url=http://orders-api:8101
|
|
55
|
+
niro build
|
|
56
|
+
```
|
|
57
|
+
<!-- source: runs/cfg-10-key.txt and `niro-local env list --path main` (2026-11-10, value in application.yml:5 not shown by env list); `niro env --help` ("set: add or change values: KEY=VALUE, repeatable"; "Passwords and tokens are refused and never stored") -->
|
|
58
|
+
|
|
59
|
+
Niro refuses to store passwords and tokens: a graph needs addresses and names, not secrets.
|
|
60
|
+
<!-- source: `niro-local env --help` first line ("Passwords and tokens are refused and never stored: a graph needs addresses and names.") -->
|
|
61
|
+
|
|
62
|
+
### 3. Which calls have no address, or two possible targets?
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
niro route-rule unconnected
|
|
66
|
+
|
|
67
|
+
5 address(es) dialled that no service here answers on:
|
|
68
|
+
http://gift-cards-api:8113: 1 call(s) from java-mono/main; no service here declares their paths
|
|
69
|
+
...
|
|
70
|
+
1 call(s) name no address in the source, and no service here declares their path.
|
|
71
|
+
java-mono/main GET /{param}
|
|
72
|
+
```
|
|
73
|
+
<!-- source: `niro-local route-rule unconnected` in java-mono, 2026-11-10 -->
|
|
74
|
+
|
|
75
|
+
"No service here answers" means the address points outside the indexed code, or the address is wrong. "Name no address" means the URL is built from a setting or a variable Niro has no value for. The failing-call playbook shows how one rule settles a route two services declare: [playbook-debug-failing-service-call.md](playbook-debug-failing-service-call.md).
|
|
76
|
+
|
|
77
|
+
## Why the answer is complete, and where it is not
|
|
78
|
+
|
|
79
|
+
- **`niro env list` lists every setting the engine found the code reading.** It is the place to start, because it also says where each is read.
|
|
80
|
+
- **A value can be missing even when a config file sets it,** as above. Treat "no value" as "Niro has no value", not "nothing sets it", and check with `code_grep`.
|
|
81
|
+
- **Settings read through a name built at run time are not found.** The graph sees what the code states.
|
|
82
|
+
<!-- source: the run above; docs/how-the-graph-works.md "What it cannot see" -->
|
|
83
|
+
|
|
84
|
+
## What grep would do instead
|
|
85
|
+
|
|
86
|
+
grep finds `shop.orders.base-url` too. It does not say which calls use it, whether two services answer the path, or which addresses no service answers, and it prints `password: CHANGEME` in plain text, where `code_grep` prints `<MASK_SECRET_002>`.
|
|
87
|
+
<!-- source: java-mono main/src/main/resources/application.yml:15 (placeholder CHANGEME); runs/cfg-11-yml.txt -->
|
|
88
|
+
|
|
89
|
+
## Cost
|
|
90
|
+
|
|
91
|
+
The value of this playbook is the lists grep cannot make (unconnected calls, values Niro holds), not token savings. What these steps cost depends on your code and your question. How Niro counts the size of its answers, and how to measure the cost on your own tasks, is in [tokens-speed-and-accuracy.md](tokens-speed-and-accuracy.md).
|
|
92
|
+
|
|
93
|
+
## Related
|
|
94
|
+
|
|
95
|
+
[configuring-a-folder.md](configuring-a-folder.md) (env values, base address, route rules), [secrets-and-masking.md](secrets-and-masking.md), [reference-engine-commands.md](reference-engine-commands.md).
|