Skip to content

Commit 2d46e4b

Browse files
committed
Merge branch 'master' into sre/jp/add-playbook-for-voting
2 parents 13a12a2 + 94f65ea commit 2d46e4b

File tree

2,124 files changed

+91342
-49810
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2,124 files changed

+91342
-49810
lines changed

.cursor/rules/coding_conventions.mdc

Lines changed: 1 addition & 312 deletions
Original file line numberDiff line numberDiff line change
@@ -3,315 +3,4 @@ description: Coding Conventions
33
globs: *.go
44
alwaysApply: false
55
---
6-
# Coding Conventions
7-
8-
## High-Assurance Software Engineering Principles
9-
10-
Flow is a high-assurance software project where the cost of bugs that slip through can be catastrophically high. We consider all inputs to be potentially byzantine. This fundamentally shapes our approach to error handling and code correctness:
11-
12-
### Inversion of Default Safety Assumptions
13-
- Traditional software engineering often assumes code paths are safe unless proven dangerous
14-
- In Flow, we invert this: **no code path is considered safe unless explicitly proven and documented to be safe**
15-
- The mere absence of known failure cases is NOT sufficient evidence of safety
16-
- We require conclusive arguments for why each code path will always behave correctly
17-
18-
### Context-Dependent Error Classification
19-
20-
A critical rule in Flow's error handling is that **the same error type can be benign in one context but an exception in another**. Error classification depends on the caller's context, not the error's type.
21-
22-
Key principles:
23-
- An error type alone CANNOT determine whether it's benign or an exception
24-
- The caller's context and expectations determine the error's severity
25-
- The same error type may be handled differently in different contexts
26-
- Documentation *must* specify which errors are benign in which contexts
27-
28-
Example of context-dependent error handling, where `storage.ErrNotFound` is _benign_:
29-
```go
30-
// We're checking if we need to request a block from another node
31-
//
32-
// No Expected errors during normal operations.
33-
func (s *Synchronizer) checkBlockExists(blockID flow.Identifier) error {
34-
_, err := s.storage.ByBlockID(blockID)
35-
if errors.Is(err, storage.ErrNotFound) {
36-
// Expected during normal operation - request block from peer.
37-
return s.requestBlockFromPeer(blockID) // Expecting no errors from this call under normal operations
38-
}
39-
if err != nil {
40-
// Other storage errors are unexpected
41-
return fmt.Errorf("unexpected storage error: %w", err)
42-
}
43-
return nil
44-
}
45-
```
46-
47-
However, in this context, the same `storage.ErrNotFound` is not expected during normal operations (we term unexpected errors as "exceptions"):
48-
```go
49-
// We're trying to read a block we know was finalized
50-
//
51-
// No Expected errors during normal operations.
52-
func (s *State) GetFinalizedBlock(height uint64) (*flow.Block, error) {
53-
blockID, err := s.storage.FinalizedBlockID(height)
54-
if err != nil {
55-
return nil, fmt.Errorf("could not get finalized block ID: %w", err)
56-
}
57-
58-
// At this point, we KNOW the block should exist
59-
block, err := s.storage.ByBlockID(blockID)
60-
if err != nil {
61-
// Any error here (including ErrNotFound) indicates a bug or corruption
62-
return nil, irrecoverable.NewExceptionf(
63-
"storage corrupted - failed to get finalized block %v: %w",
64-
blockID, err)
65-
}
66-
return block, nil
67-
}
68-
```
69-
70-
### Rules for Error Classification
71-
72-
1. **Documentation Requirements**
73-
- Functions MUST document which error types are benign in their context
74-
- Documentation MUST explain WHY an error is considered benign
75-
- Absence of documentation means an error is treated as an exception
76-
77-
2. **Error Propagation**
78-
- When propagating errors, evaluate if they remain benign in the new context
79-
- If a benign error from a lower layer indicates a critical failure in your context, wrap it as an exception
80-
- Use `irrecoverable.NewExceptionf` when elevating a benign error to an exception
81-
82-
3. **Testing Requirements**
83-
- Tests MUST verify error handling in different contexts
84-
- Test that benign errors in one context are properly elevated to exceptions in another
85-
- Mock dependencies to test both benign and exceptional paths
86-
87-
### Error Handling Philosophy
88-
- All errors are considered potentially fatal by default
89-
- Only explicitly documented benign errors are safe to recover from
90-
- For any undocumented error case, we must assume the execution state is corrupted
91-
- Recovery from undocumented errors requires node restart from last known safe state
92-
- This conservative approach prioritizes safety over continuous operation
93-
94-
Example of proper high-assurance error handling:
95-
```go
96-
func (e *engine) process(event interface{}) error {
97-
// Step 1: type checking of input
98-
switch v := event.(type) {
99-
case *ValidEvent:
100-
// explicitly documented safe path
101-
return e.handleValidEvent(v)
102-
default:
103-
// undocumented event type - unsafe to proceed
104-
return fmt.Errorf("unexpected event type %T: %w", event, ErrInvalidEventType)
105-
}
106-
}
107-
108-
func (e *engine) Submit(event interface{}) {
109-
err := e.process(event)
110-
if errors.Is(err, ErrInvalidEventType) {
111-
// This is a documented benign error - safe to handle
112-
metrics.InvalidEventsCounter.Inc()
113-
return
114-
}
115-
if err != nil {
116-
// Any other error is potentially fatal
117-
// We cannot prove it's safe to continue
118-
e.log.Fatal().Err(err).Msg("potentially corrupted state - must restart")
119-
return
120-
}
121-
}
122-
```
123-
124-
## 1. Code Documentation
125-
- Every interface must have clear documentation
126-
- Copy and extend interface documentation in implementations
127-
- Include clear explanations for any deviations from conventions
128-
- Document all public functions individually
129-
- Document error handling strategies and expected error types
130-
131-
Example of proper error documentation:
132-
```go
133-
// foo does abc.
134-
// Expected errors during normal operations:
135-
// - ErrXFailed: if x failed
136-
func foo() err {
137-
...
138-
return fmt.Errorf("details about failure: %w", ErrXFailed)
139-
}
140-
```
141-
142-
## 2. Code Structure
143-
- Follow the component-based architecture
144-
- Each component must implement the `Component` interface
145-
- Clearly differentiate between trusted (internal) and untrusted (external) inputs
146-
- Components should have dedicated worker pools
147-
- Proper resource management with worker limits
148-
- Proper state management and recovery
149-
150-
## 3. Error Categories and Handling Philosophy
151-
152-
### a. Benign Errors
153-
- Component remains fully functional despite the error
154-
- Expected during normal operations
155-
- Must be handled within the component
156-
- Must be documented in the component's context
157-
- Must be represented as typed sentinel errors
158-
- Cannot be represented by generic/untyped errors unless explicitly documented as an optional simplification for components that solely return benign errors
159-
160-
Example of proper benign error handling:
161-
```go
162-
// Expected errors during normal operations:
163-
// * ErrXFailed: if x failed
164-
func benignErrorExample() error {
165-
err := foo()
166-
if err != nil {
167-
return fmt.Errorf("failed to do foo: %w", err)
168-
}
169-
return nil
170-
}
171-
```
172-
173-
### b. Exceptions
174-
- Potential symptoms of internal state corruption
175-
- Unexpected failures that may compromise component state
176-
- Should lead to component restart or node termination
177-
- Strongly encouraged to wrap with context when bubbling up
178-
179-
Example of proper exception handling:
180-
```go
181-
err := foo()
182-
if errors.Is(err, XFailedErr) {
183-
// expected error
184-
return
185-
}
186-
if err != nil {
187-
log.Fatal().Err(err).Msg("unexpected internal error")
188-
return
189-
}
190-
```
191-
192-
### c. Sentinel Error Requirements
193-
- Must be properly typed
194-
- Must be documented in GoDoc
195-
- Must avoid generic error formats
196-
- Must always wrap with context when bubbling up the call stack
197-
- Must document all expected error types
198-
- Must handle at the appropriate level where context is available
199-
- Must use proper error wrapping for stack traces
200-
201-
Example of proper sentinel error definition and usage:
202-
```go
203-
ErrXFailed := errors.New("x failed")
204-
205-
// bar does ...
206-
// Expected error returns during normal operations:
207-
// * XFailedErr: if x failed
208-
func bar() err {
209-
...
210-
err := foo()
211-
if err != nil {
212-
return fmt.Errorf("failed to do foo: %w", err)
213-
}
214-
...
215-
}
216-
```
217-
218-
## 4. Additional Best Practices
219-
- Prioritize safety over liveness
220-
- Don't continue on "best-effort" basis when encountering unexpected errors
221-
- Testing Error Handling:
222-
- Test both benign error cases and exceptions
223-
- Must verify that documented sentinel errors are returned in their specified situations
224-
- Must verify that unexpected errors (exceptions) from lower layers or their mocks are not misinterpreted as benign errors
225-
- Verify proper error propagation
226-
- Test component recovery from errors
227-
- Validate error handling in both trusted and untrusted contexts
228-
229-
Example of proper error handling in components:
230-
```go
231-
func (e *engine) process(event interface{}) error {
232-
switch v := event.(type) {
233-
...
234-
default:
235-
return fmt.Errorf("invalid input type %T: %w", event, InvalidMessageType)
236-
}
237-
}
238-
239-
func (e *engine) Process(chan network.Channel, originID flow.Identifier, event interface{}) error {
240-
err := e.process(event)
241-
if err != nil {
242-
if errors.Is(err, InvalidMessageType) {
243-
// this is EXPECTED during normal operations
244-
}
245-
// this is unexpected during normal operations
246-
e.log.Fatal().Err(err).Msg("unexpected internal error")
247-
}
248-
}
249-
250-
func (e *engine) ProcessLocal(event interface{}) {
251-
err := e.process(event)
252-
if err != nil {
253-
if errors.Is(err, InvalidMessageType) {
254-
// this is a CRITICAL BUG
255-
}
256-
// this is unexpected during normal operations
257-
e.log.Fatal().Err(err).Msg("unexpected internal error")
258-
}
259-
}
260-
```
261-
262-
## 5. Anti-patterns to Avoid
263-
- Don't use generic error logging without proper handling
264-
- Don't swallow errors silently
265-
- Don't continue execution after unexpected errors
266-
- Don't use untyped errors unless explicitly documented as benign
267-
268-
Example of an anti-pattern to avoid:
269-
```go
270-
// DON'T DO THIS:
271-
err := foo()
272-
if err != nil {
273-
log.Error().Err(err).Msg("foo failed")
274-
return
275-
}
276-
```
277-
278-
Instead, implement proper error handling:
279-
```go
280-
func (e *engine) Submit(chan network.Channel, originID flow.Identifier, event interface{}) {
281-
e.unit.Launch(func() {
282-
err := e.process(event)
283-
if errors.Is(err, InvalidMessageType) {
284-
// invalid input: ignore or slash
285-
return
286-
}
287-
if err != nil {
288-
// unexpected input: for now we prioritize safety over liveness and just crash
289-
// TODO: restart engine from known good state
290-
e.log.Fatal().Err(err).Msg("unexpected internal error")
291-
}
292-
})
293-
}
294-
```
295-
296-
## 6. Security Considerations
297-
- Treat all external inputs as potentially byzantine
298-
- Handle byzantine inputs gracefully
299-
- Prevent state corruption from malicious inputs
300-
- Use proper error types for security-related issues
301-
302-
Example of handling untrusted inputs:
303-
```go
304-
func (e *engine) Submit(event interface{}) {
305-
e.unit.Launch(func() {
306-
err := e.process(event)
307-
if errors.Is(err, InvalidMessageType) {
308-
// invalid input from external source: ignore or slash
309-
return
310-
}
311-
if err != nil {
312-
// unexpected input: prioritize safety over liveness
313-
e.log.Fatal().Err(err).Msg("unexpected internal error")
314-
}
315-
})
316-
}
317-
```
6+
@docs/agents/CodingConventions.md

.cursor/rules/core.mdc

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,4 @@ alwaysApply: true
55
---
66
# Cursor Operational Doctrine
77

8-
You are an AI with extensive expertise in byzantine-fault-tolerant, distributed software engineering. You will consider scalability, reliability, maintainability, and security in your recommendations.
9-
10-
You are working in a pair-programming setting with a senior engineer. Their time is valuable, so work time-efficiently. They prefer an iterative working style, where you take one step at a time, confirm the direction is correct and then proceed.
11-
Critically reflect on your work. Ask if you are not sure. Avoid confirmation bias - speak up (short and concisely reasoning, followed by tangible suggestions) if something should be changed or approached differently in your opinion.
12-
13-
## Primary directive
14-
15-
Your peer's instructions, questions, requests **always** take precedence over any general rules (such as the ones below).
16-
17-
## Interactions with your peer
18-
- Never use apologies.
19-
- Acknowledge if you missunderstood something, and concisely summarize what you have learned.
20-
- Only when explicitly requested, provide feedback about your understanding of comments, documentation, code
21-
- Don't show or discuss the current implementation unless specifically requested.
22-
- State which files have been modifed and very briefly in which regard. But don't provide excerpts of changes made.
23-
- Don't ask for confirmation of information already provided in the context.
24-
- Don't ask your peer to verify implementations that are visible in the provided context.
25-
- Always provide links to the real files, not just the names x.md.
26-
27-
## Verify Information
28-
- Always verify information before presenting it. Do not make assumptions or speculate without clear evidence.
29-
- For all changes you made, review your changes in the broader context of the component you are modifying.
30-
- internally, construct a correctness argument as evidence that the updated component will _always_ behave correctly
31-
- memorize your correctness argument, but do not immediately include it in your response unless specifically requested by your peer
32-
33-
## Software Design Approach
34-
- Leverage existing abstractions; refactor them judiciously.
35-
- Augment with tests, logging, and API exposition once the core business logic is robust.
36-
- Ensure new packages are modular, orthogonal, and future-proof.
37-
38-
## No Inventions
39-
Don't invent changes other than what's explicitly requested.
40-
41-
## No Unnecessary Updates
42-
- Don't remove unrelated code or functionalities.
43-
- Don't suggest updates or changes to files when there are no actual modifications needed.
44-
- Don't suggest whitespace changes.
45-
8+
@docs/agents/OperationalDoctrine.md

0 commit comments

Comments
 (0)