norn 0.0.1.pre.20260715152924
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.
- checksums.yaml +7 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +23 -0
- data/.gitignore +5 -0
- data/.tool-versions +1 -0
- data/ARCHITECTURE.md +271 -0
- data/COLLABORATIVE_WORKFLOW.md +185 -0
- data/GAPS.md +6 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +253 -0
- data/LICENSE +668 -0
- data/ONBOARDING.md +211 -0
- data/README.md +101 -0
- data/abstract_interfaces_plan.md +6 -0
- data/bin/norn +135 -0
- data/brainstorm.md +6 -0
- data/collapsible_terminal_outputs_plan.md +6 -0
- data/docs/superpowers/plans/2026-07-14-interactive-gatekeeper-prompts.md +595 -0
- data/docs/superpowers/plans/2026-07-14-session-level-command-approvals.md +465 -0
- data/docs/superpowers/plans/2026-07-14-statically-dangerous-session-approvals.md +190 -0
- data/docs/superpowers/plans/2026-07-15-gemify-norn.md +223 -0
- data/docs/superpowers/specs/2026-07-14-interactive-gatekeeper-prompts-design.md +114 -0
- data/docs/superpowers/specs/2026-07-14-session-level-command-approvals-design.md +94 -0
- data/docs/superpowers/specs/2026-07-14-statically-dangerous-session-approvals-design.md +81 -0
- data/dynamic_hooks_and_recursive_safety_plan.md +6 -0
- data/implementation_plan.md +6 -0
- data/lib/norn/cli.rb +70 -0
- data/lib/norn/config.rb +23 -0
- data/lib/norn/config_loader.rb +157 -0
- data/lib/norn/container.rb +16 -0
- data/lib/norn/diff_helper.rb +98 -0
- data/lib/norn/error_renderer.rb +41 -0
- data/lib/norn/errors.rb +72 -0
- data/lib/norn/global_options.rb +33 -0
- data/lib/norn/local_config_reader.rb +56 -0
- data/lib/norn/mode.rb +476 -0
- data/lib/norn/mode_registry.rb +52 -0
- data/lib/norn/modes/chat.rb +30 -0
- data/lib/norn/modes/dev.rb +34 -0
- data/lib/norn/modes/task.rb +34 -0
- data/lib/norn/plugin.rb +36 -0
- data/lib/norn/plugin_loader.rb +21 -0
- data/lib/norn/plugin_manager.rb +187 -0
- data/lib/norn/secret_scrubber.rb +50 -0
- data/lib/norn/skill.rb +143 -0
- data/lib/norn/skill_loader.rb +57 -0
- data/lib/norn/skill_registry.rb +111 -0
- data/lib/norn/tool.rb +89 -0
- data/lib/norn/tool_registry.rb +30 -0
- data/lib/norn/ui/gatekeeper.rb +127 -0
- data/lib/norn/version.rb +3 -0
- data/lib/norn.rb +48 -0
- data/mcp_capabilities_security_plan.md +6 -0
- data/norn.gemspec +39 -0
- data/norn_audit_and_expansion_plan.md +6 -0
- data/plugins/file_tools/path_helper.rb +29 -0
- data/plugins/file_tools/plugin.rb +165 -0
- data/plugins/gemini/client.rb +230 -0
- data/plugins/gemini/plugin.rb +13 -0
- data/plugins/git/plugin.rb +137 -0
- data/plugins/git_commit_message/plugin.rb +33 -0
- data/plugins/openai/client.rb +186 -0
- data/plugins/openai/plugin.rb +19 -0
- data/plugins/prompt_user/plugin.rb +128 -0
- data/plugins/rspec/plugin.rb +86 -0
- data/plugins/session/plugin.rb +77 -0
- data/plugins/session/session.rb +110 -0
- data/plugins/session_cli_formatter/plugin.rb +48 -0
- data/plugins/session_track_tokens/plugin.rb +26 -0
- data/plugins/session_track_tools/plugin.rb +24 -0
- data/plugins/skills/plugin.rb +187 -0
- data/plugins/slash_commands/plugin.rb +187 -0
- data/plugins/tty_markdown/plugin.rb +40 -0
- data/plugins/web_tools/plugin.rb +139 -0
- data/skills_loading_plan.md +6 -0
- metadata +296 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e31f003bf7e6fc8487ebc94d9f2fbbc88b2ad1daa26e7a6e50b1485208a49b4e
|
|
4
|
+
data.tar.gz: 65cb552ff6829cd2eb5a2c3067ddcff07b9ce787b02f64df3ebe80d7b3f8b485
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 3674c6a776c1772fe201792d21ae952cd7f2e2c459e6c13a9ad08220f67b54ce779b392fb2f78de3127933f85b8d241a45102777c0b83877dd273de2801499de
|
|
7
|
+
data.tar.gz: cb0492372d37188c27df36252d8b4955f84c35242fe99e411efe07979bb128f7adf691fbcc50f1e2226ce9e556da8c5651bc6d92b4bc5ad6a02a535d7888f058
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
tester:
|
|
11
|
+
name: Tester
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout code
|
|
15
|
+
uses: actions/checkout@v7
|
|
16
|
+
|
|
17
|
+
- name: Set up Ruby
|
|
18
|
+
uses: ruby/setup-ruby@v1
|
|
19
|
+
with:
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: bundle exec rspec
|
data/.gitignore
ADDED
data/.tool-versions
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby 4.0.5
|
data/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# Norn Architecture, Techniques, & Coding Practices Manual
|
|
2
|
+
|
|
3
|
+
Welcome to the Norn Architecture and Engineering Manual. This document serves as the primary technical specification of Norn's core system architecture, design decisions, safety mechanics, and coding standards.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Core Architectural Philosophy
|
|
8
|
+
|
|
9
|
+
Norn is built as a **stateless, extensible, dynamic software engineering agent**. Its engineering decisions are guided by three core pillars:
|
|
10
|
+
|
|
11
|
+
1. **Zero Native C-Dependency Bindings:** Eliminates installation fragility and cross-compilation failures (e.g., `node-gyp` compilation errors, SQLite/LanceDB ABI mismatches, OpenSSL library clashes) by relying entirely on Ruby's robust standard library and stable pure-Ruby gems.
|
|
12
|
+
2. **Object-Oriented Plugin-Centric Extension:** All major subsystems are completely decoupled. External LLM clients, tool libraries, and custom UI renderers register dynamically as self-contained classes.
|
|
13
|
+
3. **Fail-Fast Bootstrapping:** Interface contracts and subclassing dependencies are verified at load time rather than deep inside runtime loops, ensuring any development or configuration error is caught immediately during startup.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. Dependency Injection & Core Bootstrap Layout
|
|
18
|
+
|
|
19
|
+
Norn avoids global state and singleton class structures by managing all components through a central Dependency Injection (DI) Container powered by the `dry-rb` ecosystem.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌────────────────────────┐
|
|
23
|
+
│ bin/norn │
|
|
24
|
+
└───────────┬────────────┘
|
|
25
|
+
│
|
|
26
|
+
▼
|
|
27
|
+
┌────────────────────────┐
|
|
28
|
+
│ ConfigLoader.load │
|
|
29
|
+
└───────────┬────────────┘
|
|
30
|
+
│
|
|
31
|
+
▼
|
|
32
|
+
┌────────────────────────┐
|
|
33
|
+
│ PluginLoader.load_all │
|
|
34
|
+
└───────────┬────────────┘
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
┌────────────────────────┐
|
|
38
|
+
│ Normalize ARGV! │
|
|
39
|
+
└───────────┬────────────┘
|
|
40
|
+
│
|
|
41
|
+
▼
|
|
42
|
+
┌────────────────────────┐
|
|
43
|
+
│ Dry::CLI Dispatch │
|
|
44
|
+
└────────────────────────┘
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### The DI Container (`lib/norn/container.rb`)
|
|
48
|
+
* `Norn::Container` inherits from `Dry::System::Container` and manages standard components (like LLM clients under `llm.openai` or `llm.gemini`).
|
|
49
|
+
* Resolving clients dynamically from the container decouples the command loop from specific API clients:
|
|
50
|
+
```ruby
|
|
51
|
+
client = Norn::Container["llm.#{provider}"]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Dynamic Global Options Registry (`lib/norn/global_options.rb`)
|
|
55
|
+
To avoid hardcoding flags (like `--provider` or `--debug`) in Norn's core, external plugins can register their own command-line options dynamically during bootstrapping:
|
|
56
|
+
```ruby
|
|
57
|
+
Norn::GlobalOptions.register :provider,
|
|
58
|
+
type: :string,
|
|
59
|
+
aliases: ["-p"],
|
|
60
|
+
desc: "LLM provider to use"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Flexible ARGV Normalization (`bin/norn`)
|
|
64
|
+
To bypass `dry-cli`'s strict command-order limitations (which usually forces options *after* subcommands), an early-boot normalizer scans `ARGV`, maps registered global options dynamically, and moves them to the end of the argument array prior to dispatching:
|
|
65
|
+
`./bin/norn --provider gemini chat` $\rightarrow$ `./bin/norn chat --provider gemini`
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 3. Class-Based Plugin System (`lib/norn/plugin.rb`)
|
|
70
|
+
|
|
71
|
+
Norn's dynamic extensibility is powered by Object-Oriented class inheritance.
|
|
72
|
+
|
|
73
|
+
### The Subclass Registry Hook
|
|
74
|
+
```ruby
|
|
75
|
+
module Norn
|
|
76
|
+
class Plugin
|
|
77
|
+
@registered_plugins = []
|
|
78
|
+
|
|
79
|
+
def self.inherited(subclass)
|
|
80
|
+
super
|
|
81
|
+
@registered_plugins << subclass
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.registered_plugins
|
|
85
|
+
@registered_plugins
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
Any file loaded by the `PluginLoader` that subclasses `Norn::Plugin` is automatically registered in-memory on require.
|
|
91
|
+
|
|
92
|
+
### Hook-Bridging Dispatcher (`lib/norn/plugin_manager.rb`)
|
|
93
|
+
The `PluginManager` acts as a hybrid event-bus. When a lifecycle event is triggered (such as `:on_boot`, `:on_tool_register`, or `:on_render_response`), the manager:
|
|
94
|
+
1. Executes all legacy block-based subscriptions registered via `subscribe`.
|
|
95
|
+
2. Dynamically routes calls to matching instance methods on active `Norn::Plugin` objects, bridging OO designs with event-driven pipelines seamlessly.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 4. Railway-Oriented Programming (ROP) in Ruby
|
|
100
|
+
|
|
101
|
+
To eliminate fragile nested `begin/rescue/raise` loops, Norn implements **Railway-Oriented Programming** using `dry-monads` and `Dry::Monads::Do` notation.
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
Success track ──[Load Config]───►[Resolve Client]───►[LLM Call]───►[Execute Tool]──► Success(output)
|
|
105
|
+
│ │ │ │
|
|
106
|
+
Failure track ────────┴─────────────────┴───────────────┴───────────────┴──────────► Failure(payload)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### The Result Monad Pipeline
|
|
110
|
+
Every service, configuration loader, and LLM client returns a monadic `Result` (`Success` or `Failure`):
|
|
111
|
+
```ruby
|
|
112
|
+
# Example of flat, failure-safe loop execution in task.rb
|
|
113
|
+
def execute_loop_step(system_prompt)
|
|
114
|
+
client = yield resolve_provider_client(Norn.config.llm_provider)
|
|
115
|
+
|
|
116
|
+
response_result = client.call(active_messages, tools: Norn::ToolRegistry.registered_tools)
|
|
117
|
+
response = yield response_result.alt_map { |payload| payload.with_context(step: :llm_call) }
|
|
118
|
+
|
|
119
|
+
# ... proceed on the success track safely ...
|
|
120
|
+
end
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Contextual Failures (`lib/norn/errors.rb`)
|
|
124
|
+
Instead of propagating raw, unhelpful string messages, failures are wrapped inside a structured `Norn::FailurePayload`. This payload holds:
|
|
125
|
+
* The original Ruby exception object (preserving stack traces).
|
|
126
|
+
* A rich `context` Hash capturing the state (e.g., active provider, active prompt, step, original HTTP payloads) at the exact moment of failure, allowing for deep, diagnostic debugging.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 5. Security & Capability-Based Sandboxing
|
|
131
|
+
|
|
132
|
+
Norn implements a strict, robust **Capability-Based Security Model** to govern tool-calling and parameter accessibility:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
[Agent Session Starts]
|
|
136
|
+
│
|
|
137
|
+
├─► Pre-Flight Pruning: Only expose authorized tool schemas to LLM.
|
|
138
|
+
│
|
|
139
|
+
▼
|
|
140
|
+
[LLM decides to call a Tool]
|
|
141
|
+
│
|
|
142
|
+
├─► In-Flight Gatekeeper: Evaluate required capabilities dynamically.
|
|
143
|
+
│
|
|
144
|
+
├─► If Authorized: Execute tool.
|
|
145
|
+
│
|
|
146
|
+
└─► If Unauthorized:
|
|
147
|
+
├─► If Mode.interactive? ──► Prompt CLI for Y/N escalation.
|
|
148
|
+
└─► If Non-Interactive ──► Fail-Safe Block & return Failure.
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Standard Capabilities
|
|
152
|
+
* `sys:read`: Read-only local file operations (`file_read`, `glob`, `grep`, `prompt_user`).
|
|
153
|
+
* `sys:write`: Local filesystem writes and edits (`file_write`, `file_edit`).
|
|
154
|
+
* `sys:execute`: Subprocess executions and local command lines.
|
|
155
|
+
|
|
156
|
+
### Safe Chat Mode Tools
|
|
157
|
+
By classifying tools like `file_read`, `glob`, `grep`, and `prompt_user` under `sys:read` and restricting Chat Mode's permitted capabilities to `[:sys_read, :vcs_read]`, **Chat Mode is securely empowered with read-only capabilities.**
|
|
158
|
+
|
|
159
|
+
Chat Mode dynamically maps, reads, and explains files within your codebase safely without any risk of file corruption or write modifications.
|
|
160
|
+
|
|
161
|
+
### Pre-Flight Schema Pruning
|
|
162
|
+
Before invoking any LLM provider client, Norn selectively filters out any tool whose required capabilities exceed the active mode's permitted profile. This prevents the model from wasting tokens trying to call completely forbidden tools.
|
|
163
|
+
|
|
164
|
+
### Centralized In-Flight Gatekeeper (`lib/norn/mode.rb`)
|
|
165
|
+
Every mode inherits a secure `#execute_tool` block:
|
|
166
|
+
* If a tool requires unauthorized capabilities in an `interactive?` mode, Norn prompts the user on the CLI for **real-time escalation (Y/N)**. If approved, the permission is temporarily cached for that session.
|
|
167
|
+
* In non-interactive modes, Norn **blocks by default** and aborts execution immediately with a `Failure` monad.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 6. Dynamic In-Flight Danger Guards & Diff Previews
|
|
172
|
+
|
|
173
|
+
For interactive coding workflows, Norn features a **Dynamic Parametric Danger Gatekeeper** to safeguard your local codebase from unintended modifications.
|
|
174
|
+
|
|
175
|
+
### Parametric Danger Checking
|
|
176
|
+
Tools can be statically designated as dangerous (like `file_write` or `file_edit`) or dynamically override `#dangerous?(args)` to evaluate their parameters at runtime (e.g. blocking destructive version-control resets while allowing safe status queries).
|
|
177
|
+
|
|
178
|
+
### Unified Diff Color Helper (`lib/norn/diff_helper.rb`)
|
|
179
|
+
When a dangerous write/edit tool is triggered in an interactive mode, Norn halts execution and:
|
|
180
|
+
1. Resolves the proposed file modifications.
|
|
181
|
+
2. Computes a lightweight, pure-Ruby, unified line diff.
|
|
182
|
+
3. Renders the changes with **bold ANSI terminal formatting** (red `-` for removals, green `+` for additions) to let you audit the edits.
|
|
183
|
+
4. Intercepts terminal input, defaulting to **Yes/Approval** if the user presses Enter immediately (`[Y/n]` prompt) or aborting cleanly on `n`.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 7. Interactive Pairing & Pairing Mode (`lib/norn/modes/dev.rb`)
|
|
188
|
+
|
|
189
|
+
To bring these features together under an optimal developer workflow, Norn features **Interactive Dev Mode** (`Norn::Modes::Dev`), triggered via `./bin/norn dev`.
|
|
190
|
+
|
|
191
|
+
* **The Strategy:** Conforms to our full subclassing contracts, carrying full development capabilities (`[:sys_read, :sys_write, :sys_execute, :vcs_read, :vcs_write, :net_egress]`).
|
|
192
|
+
* **The Interaction:** Operates as a live REPL pairing loop. Because it is `interactive?`, all dangerous tool calls (like writing code or running shell scripts) triggered by Gemini/OpenAI are automatically intercepted in-flight by **Section 6 danger guards**, printing diffs and prompting for validation right in your console before execution.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 8. Dynamic Tool-Level System Instruction Injection
|
|
197
|
+
|
|
198
|
+
To maintain clean plugin decoupling, Norn implements **dynamic system prompt injection**.
|
|
199
|
+
|
|
200
|
+
### Decentralized Directives
|
|
201
|
+
Instead of hardcoding tool-use guidelines inside core files, **each tool defines its own instructions** upon initialization inside its respective plugin:
|
|
202
|
+
```ruby
|
|
203
|
+
Norn::Tool.new(
|
|
204
|
+
"prompt_user",
|
|
205
|
+
"Prompt the user...",
|
|
206
|
+
SCHEMA_JSON,
|
|
207
|
+
system_instructions: "If a user instruction is ambiguous, call the 'prompt_user' tool to obtain structured feedback..."
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### In-Flight Prompt Compilation (`lib/norn/mode.rb`)
|
|
212
|
+
Before dispatching a conversational turn to the LLM client, Norn calls `#compile_system_prompt` on the active Mode:
|
|
213
|
+
1. It identifies all tools **currently authorized** for the active Mode.
|
|
214
|
+
2. It collects only those tools' `system_instructions` and joins them under a clean `TOOL EXECUTION DIRECTIVES` block.
|
|
215
|
+
3. It appends this list to the core mode instructions and passes the fully-grounded instructions to the provider, keeping the LLM perfectly aware of its dynamic capabilities on every turn.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 9. Interactive Prompts & The PromptUser Plugin
|
|
220
|
+
|
|
221
|
+
To empower the LLM with the ability to query, ask, and present choices to the developer programmatically, Norn introduces the **`PromptUser` Plugin** (`plugins/prompt_user/plugin.rb`).
|
|
222
|
+
|
|
223
|
+
* **Widgets Supported:** Maps the `"prompt_user"` tool directly to native, highly styled terminal elements via `TTY::Prompt`:
|
|
224
|
+
- `confirm`: Colored binary selector (`yes`/`no`).
|
|
225
|
+
- `select`: Multi-option cursor selector. Fully supports **explicit single-turn custom write-ins** via the `allow_custom` parameter, which dynamically appends a `"Custom (type your own...)"` option and prompts for inline text input in a single, frictionless conversational step.
|
|
226
|
+
- `multi_select`: Spacebar checkbox option selector.
|
|
227
|
+
- `input`: Free-text interactive input.
|
|
228
|
+
* **Context Passing:** The Mode execution gatekeeper passes `self` (the active `Mode` instance) as the `context` parameter to the tool's block execution, enabling the plugin to cleanly access the REPL's `@input` and `@output` streams in a fully stateless, thread-safe manner.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 10. Dynamic Hook Policies & Recovery Track
|
|
233
|
+
|
|
234
|
+
We standardize Norn's lifecycle hooks and plugin events into two distinct categories:
|
|
235
|
+
|
|
236
|
+
### A. Notifications (Parallel / Fire-and-Forget)
|
|
237
|
+
Informational events that do not mutate state and execute in parallel (e.g., `:after_llm_call`).
|
|
238
|
+
|
|
239
|
+
### B. Middlewares (Sequential ROP / Kleisli Reduction)
|
|
240
|
+
Mutating pipelines where the output of one subscriber becomes the input of the next. The entire chain is wrapped in the `Success/Failure` monad track using a monadic `reduce` composed of `bind` operations.
|
|
241
|
+
|
|
242
|
+
### Recoverable Fail-Safe Policies (`lib/norn/plugin.rb`)
|
|
243
|
+
Plugins can declare their hook policies as `:recover` (recoverable/non-fatal) or `:fatal` (the default). If an optional `:recover` subscriber fails or throws an exception (such as `TTYMarkdownPlugin` parsing), Norn logs a dim grey console warning (`[Norn Warning] Optional hook... failed`) and safely recovers the pipeline, passing the last valid success payload forward.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## 11. Security & In-Memory Secret Scrubbing
|
|
248
|
+
|
|
249
|
+
Norn operates a strict, multi-layered security model to ensure sensitive developer tokens (such as Gemini or OpenAI keys) are never leaked to logs, CLI traces, or API queries.
|
|
250
|
+
|
|
251
|
+
### Multi-Layer Token Redactor (`lib/norn/secret_scrubber.rb`)
|
|
252
|
+
1. **Dynamic Caching:** When Norn starts up, it registers standard environment keys in-memory. When processing any string, it scans for pattern matches (like Google `key=...` query params or OpenAI `sk-...` signatures), extracts the raw keys, and adds them to a secure in-memory `Set`.
|
|
253
|
+
2. **Deep-Sanitization (`Norn::FailurePayload`):** Raw exceptions (such as `Faraday::BadRequestError`) are recursively traversed. Non-basic objects (like exception variables containing the raw request params with the API keys) are converted to string representations and sanitized before the context is stored.
|
|
254
|
+
3. **Console Boundary Protection (`Norn::ErrorRenderer`):** Just before debug metrics or backtraces are written to standard error, they are scrubbed of all registered secret values.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 12. Multimodal Part Preservation
|
|
259
|
+
|
|
260
|
+
Gemini's conversation structure relies on an array of `parts` where reasoning chains (thought signatures) and `functionCall` blocks are logically bound.
|
|
261
|
+
|
|
262
|
+
* **Unified Message Schema:** To prevent structure violations (`INVALID_ARGUMENT: Function call is missing a thought_signature`), the Gemini client extracts and returns the raw API response `parts` in the `Success` monad.
|
|
263
|
+
* **History Preservation:** The `Task` and `Chat` mode loops capture these raw `:parts` and store them in the conversation history, which the Gemini adapter echoes back exactly as received on subsequent turns, keeping Google's backend integrity checks satisfied without polluting other provider formats (like OpenAI).
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 13. Coding Practices & Testing Standards
|
|
268
|
+
|
|
269
|
+
1. **TDD Interface Isolation:** Unit tests (`rspec`) must mock external API requests to return monadic results (`Success(...)` / `Failure(...)`) to ensure perfect compatibility with ROP pipelines.
|
|
270
|
+
2. **Dynamic Spec Cleanups:** Test states are strictly isolated using `config.before(:each)` hooks inside `spec_helper.rb` to clear the `ToolRegistry`, `Plugin.registered_plugins`, and `PluginManager` state.
|
|
271
|
+
3. **Efficiency Overriding:** Directives are explicitly tuned to command immediate writes on file creation requests, preventing the LLM from executing redundant, token-heavy filesystem searches or double-reading files back from disk.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Collaborative Development Workflow (Humans & Agents)
|
|
2
|
+
|
|
3
|
+
This document defines the development process for this repository. It serves as a unified execution manual and collaboration standard for Human Developers and AI Agents alike. In our ecosystem, both are treated as co-equal team members (referred to as **Implementers**) who collaborate asynchronously through **Basecamp** as our tracking engine, using **Gherkin/Cucumber** as our shared, executable specifications language.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Core Principles
|
|
8
|
+
|
|
9
|
+
1. **Co-Equal Partnership**: Humans and agents use the same boards, follow the same pipeline, and are held to the same standards of test coverage, code quality, and documentation.
|
|
10
|
+
2. **Basecamp as the Single Source of Truth**: Requirements, active state, implementation checklists, discussions, and technical decisions live in Basecamp.
|
|
11
|
+
3. **Behavior-Driven Development (BDD)**: All non-trivial functional changes must be described using Gherkin syntax (Given-When-Then). Gherkin serves as both the unambiguous requirement specification and the automated test suite.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Basecamp Card Table Structure (The State Machine)
|
|
16
|
+
|
|
17
|
+
Our development pipeline is managed via a Basecamp Card Table. The columns represent distinct phases of development and establish clear transitions of ownership:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
[ Backlog ] ➔ [ Triage & Planning ] ➔ [ Up Next ] ➔ [ In Progress ] ➔ [ Blocked / On Hold ] ➔ [ Under Review ] ➔ [ Done ]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Column Definitions & Transition Rules
|
|
24
|
+
|
|
25
|
+
#### 1. Backlog
|
|
26
|
+
* **Purpose**: A holding zone for raw ideas, bug reports, feature requests, and refactoring notes.
|
|
27
|
+
* **Who can add**: Anyone (humans, monitoring tools, automation scripts).
|
|
28
|
+
* **Requirements**: No strict format is required; entries can be unstructured.
|
|
29
|
+
|
|
30
|
+
#### 2. Triage & Planning
|
|
31
|
+
* **Purpose**: Active refinement zone where raw Backlog ideas are transformed into actionable, specified tickets.
|
|
32
|
+
* **Who manages**: Tech Leads, Product Owners, or Senior Agents.
|
|
33
|
+
* **Transition rule**: Cards are moved here from the Backlog to be refined. Once Gherkin scenarios are completed and approved, the card moves to **Up Next**.
|
|
34
|
+
|
|
35
|
+
#### 3. Up Next (The Queue)
|
|
36
|
+
* **Purpose**: The prioritized queue of structured, actionable cards ready for execution.
|
|
37
|
+
* **Who manages**: Product Owners (prioritization).
|
|
38
|
+
* **Requirements to enter**: To be moved into *Up Next*, a card must have:
|
|
39
|
+
* An action-oriented **Title**.
|
|
40
|
+
* A **Description** containing business context and **Gherkin Specifications**.
|
|
41
|
+
* A high-level **Card Steps checklist** for core deliverables.
|
|
42
|
+
|
|
43
|
+
#### 4. In Progress
|
|
44
|
+
* **Purpose**: Active implementation phase.
|
|
45
|
+
* **Who manages**: The assigned Implementer (Human or Agent).
|
|
46
|
+
* **Transition rule**: An Implementer claims a card by **assigning it to themselves** and **moving it to In Progress** (moving out of *Up Next*).
|
|
47
|
+
|
|
48
|
+
#### 5. Blocked / On Hold (Human-in-the-Loop Gate)
|
|
49
|
+
* **Purpose**: A parking zone for active cards that cannot proceed due to ambiguity, missing environment secrets, upstream dependencies, or unresolved test failures.
|
|
50
|
+
* **Who manages**: The current Implementer + the requested Unblocker.
|
|
51
|
+
* **Transition rule**: When moving a card here from *In Progress*, the Implementer **must** leave a comment explaining the blocker in detail, provide diagnostic context (e.g., test logs, stack traces), and `@mention` the teammate (human or agent) needed to resolve the issue.
|
|
52
|
+
|
|
53
|
+
#### 6. Under Review
|
|
54
|
+
* **Purpose**: Code is complete, all local tests pass, and a Pull Request is ready for peer review.
|
|
55
|
+
* **Who manages**: The Peer Reviewer (Human or Agent).
|
|
56
|
+
* **Transition rule**: The Implementer pushes their branch, opens a PR, posts the PR link in a Basecamp card comment, and moves the card here from *In Progress*.
|
|
57
|
+
|
|
58
|
+
#### 7. Done
|
|
59
|
+
* **Purpose**: The Pull Request has been reviewed, approved, merged, and verified in production.
|
|
60
|
+
* **Who manages**: The Peer Reviewer, Tech Lead, or Deployment Automation.
|
|
61
|
+
* **Transition rule**: Moved here from *Under Review* after the code is merged and verified.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 3. Card Anatomy (Executable Specifications)
|
|
66
|
+
|
|
67
|
+
To ensure that both humans and AI agents understand requirements without ambiguity, every card in **Up Next** must conform to this structured layout.
|
|
68
|
+
|
|
69
|
+
### A. Clear, Action-Oriented Title
|
|
70
|
+
Avoid vague titles like "User Invitations". Instead, use active verbs: "Implement user invitation expiration logic".
|
|
71
|
+
|
|
72
|
+
### B. Business Context & Gherkin Scenarios
|
|
73
|
+
The description must frame the business value and define the acceptance criteria in Gherkin format.
|
|
74
|
+
|
|
75
|
+
```markdown
|
|
76
|
+
### Business Context
|
|
77
|
+
Currently, user invitation tokens last indefinitely. To improve security and clean up stale pending users, invitation tokens should expire 48 hours after they are generated.
|
|
78
|
+
|
|
79
|
+
### Acceptance Criteria
|
|
80
|
+
Feature: Expire User Invitations
|
|
81
|
+
|
|
82
|
+
Scenario: Invitation is still valid within 48 hours
|
|
83
|
+
Given a user invitation was created "24 hours ago"
|
|
84
|
+
When the user attempts to accept the invitation
|
|
85
|
+
Then the invitation should be accepted successfully
|
|
86
|
+
And the user account should be activated
|
|
87
|
+
|
|
88
|
+
Scenario: Invitation is expired after 48 hours
|
|
89
|
+
Given a user invitation was created "50 hours ago"
|
|
90
|
+
When the user attempts to accept the invitation
|
|
91
|
+
Then the system should refuse the invitation with an "expired" error
|
|
92
|
+
And the user account should remain inactive
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### C. Card Steps (The Checklist)
|
|
96
|
+
Steps serve as granular checkpoints. They provide visual tracking for teammates and structure the execution path for agents.
|
|
97
|
+
|
|
98
|
+
* [ ] Write regression/integration tests matching the Gherkin specifications
|
|
99
|
+
* [ ] Implement token generation timestamp in database migration
|
|
100
|
+
* [ ] Implement token expiration check in `UserInvitation` model
|
|
101
|
+
* [ ] Handle expired token exceptions in the UI/Controller
|
|
102
|
+
* [ ] Verify the entire test suite passes green locally
|
|
103
|
+
* [ ] Push branch and open Pull Request
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 4. The Collaborative Execution Loop
|
|
108
|
+
|
|
109
|
+
When an Implementer (Human or Agent) begins working on a card, they follow this exact, disciplined loop. This loop directly drives the state transitions of the Basecamp Card Table:
|
|
110
|
+
|
|
111
|
+
### Workflow State Matrix
|
|
112
|
+
|
|
113
|
+
| Execution Step | Starting Column | Ending Column | Key Action / Tooling |
|
|
114
|
+
| :--- | :--- | :--- | :--- |
|
|
115
|
+
| **Step 1: Claim & Branch** | `Up Next` | `In Progress` | Assign self, move card, checkout local branch |
|
|
116
|
+
| **Step 2: Refine Plan** | `In Progress` | `In Progress` | Expand/add sub-steps to Card checklist |
|
|
117
|
+
| **Step 3: Edit & Verify** | `In Progress` | `In Progress` | Write tests first, implement, verify locally |
|
|
118
|
+
| **Step 4: Block Escalation** *(Optional)* | `In Progress` | `Blocked / On Hold` | Comment with diagnostics, @mention supervisor, pause |
|
|
119
|
+
| **Step 5: PR & Hand-off** | `In Progress` | `Under Review` | Push branch, open PR, link PR in comment, move card |
|
|
120
|
+
| **Step 6: Merge & Close** | `Under Review` | `Done` | Peer review approval, merge PR, move card |
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### Step-by-Step Execution Guide
|
|
125
|
+
|
|
126
|
+
#### Step 1: Claiming & Branching
|
|
127
|
+
1. Take the top card from the **Up Next** column.
|
|
128
|
+
2. Assign the card to yourself.
|
|
129
|
+
3. Move the card to **In Progress**.
|
|
130
|
+
4. Check out a local feature branch named after the card (e.g., `feature/invitation-expiration-789`).
|
|
131
|
+
|
|
132
|
+
#### Step 2: Refining the Plan
|
|
133
|
+
* Read the Gherkin scenarios carefully.
|
|
134
|
+
* If the pre-authored **Card Steps** are too high-level, the Implementer should add sub-steps (e.g., `[ ] Generate ActiveRecord migration`, `[ ] Refactor token lookup query`) directly to the Basecamp Card Steps checklist. This acts as a collaborative implementation plan.
|
|
135
|
+
|
|
136
|
+
#### Step 3: Local Edit & Verify Loop (BDD Execution)
|
|
137
|
+
1. **Write Tests First**: Translate the Gherkin specs into automated executable test files (e.g., Cucumber features or RSpec integration specs). Run them and observe them fail.
|
|
138
|
+
2. **Implement Code**: Write the minimal code necessary to make the tests pass.
|
|
139
|
+
3. **Verify Locally**: Run the test suite and linters locally.
|
|
140
|
+
4. **Self-Correction**: If tests fail, analyze the error output, inspect logs, make adjustments, and rerun. The Implementer must iterate locally until all tests pass.
|
|
141
|
+
5. **Asynchronous Updates**: As steps are completed, check them off on the Basecamp card. This keeps the entire team aligned on progress in real time without distracting status meetings.
|
|
142
|
+
|
|
143
|
+
#### Step 4: Handling Blocks (The Escape Hatch)
|
|
144
|
+
If the Implementer cannot make a test pass, detects an edge-case contradiction in the Gherkin spec, or lacks access to a required key/secret:
|
|
145
|
+
1. Stop local execution.
|
|
146
|
+
2. Move the card to **Blocked / On Hold**.
|
|
147
|
+
3. Write a comment on the card detailing the block:
|
|
148
|
+
> 🤖 **AGENT UPDATE**: I have implemented the model logic, but the controller test is failing because the Mock API requires a client ID secret.
|
|
149
|
+
>
|
|
150
|
+
> **Blocker**: Missing `API_CLIENT_ID` in test environment.
|
|
151
|
+
> @Jane Smith, could you help me configure this or point me to where test credentials are stored?
|
|
152
|
+
4. Do not block yourself; return to **Up Next** and claim a different card.
|
|
153
|
+
|
|
154
|
+
#### Step 5: Submission & Hand-off
|
|
155
|
+
1. Verify all tests, linters, and type-checkers are completely green.
|
|
156
|
+
2. Commit changes with clean, atomic commit messages detailing *what* was done and *why*.
|
|
157
|
+
3. Push the feature branch to the remote repository.
|
|
158
|
+
4. Open a Pull Request.
|
|
159
|
+
5. Post a comment on the Basecamp card containing a brief summary of the solution and a link to the PR.
|
|
160
|
+
6. Move the card to **Under Review**.
|
|
161
|
+
|
|
162
|
+
#### Step 6: Merge & Close
|
|
163
|
+
1. Teammates review the Pull Request and offer feedback.
|
|
164
|
+
2. Once the PR is approved, it is merged into the main branch.
|
|
165
|
+
3. The card is moved to **Done** (and eventually archived).
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 5. Basecamp as a Project Knowledge Base
|
|
170
|
+
|
|
171
|
+
Beyond progress tracking, Basecamp acts as the central knowledge graph for our system. Developers and agents use it to document and retrieve high-level system state:
|
|
172
|
+
|
|
173
|
+
### A. Architecture Decision Records (ADRs)
|
|
174
|
+
* Major architectural decisions, design patterns, or database modeling choices must be documented in **Basecamp Docs** (within the project's **Vault**).
|
|
175
|
+
* ADRs must be assigned a unique ID (e.g., `ADR-004: Event-Driven Auditing`).
|
|
176
|
+
|
|
177
|
+
### B. Discussions & RFCs
|
|
178
|
+
* When a design requires open discussion, an Implementer posts a message on the **Basecamp Message Board** labeled as an RFC (Request for Comments).
|
|
179
|
+
* Human team members and AI agents participate in the comment thread to brainstorm, resolve design trade-offs, and reach consensus before code is written.
|
|
180
|
+
|
|
181
|
+
### C. Contextual Linking
|
|
182
|
+
* Every card in **Up Next** that depends on a specific design pattern must link to the corresponding ADR in its description.
|
|
183
|
+
* Conversely, once an ADR or discussion is closed with a consensus, the concluding message or document must link back to the resulting Basecamp card(s).
|
|
184
|
+
|
|
185
|
+
This bi-directional linking guarantees that human developers and agents have immediate, rich context for *every single line of code* they are asked to write.
|
data/GAPS.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Norn Architectural Gaps & Refactoring Registry
|
|
2
|
+
|
|
3
|
+
This backlog and gaps registry has been fully migrated to **Basecamp**, which serves as our co-equal async **Single Source of Truth** for requirements and planning.
|
|
4
|
+
|
|
5
|
+
Please refer to the active work items and specifications on our Basecamp Board:
|
|
6
|
+
👉 **[Norn Card Table](https://app.basecamp.com/5717936/buckets/48052540/card_tables/10089221034)**
|
data/Gemfile
ADDED