* Document design workflow process and update CLAUDE.md
- Add comprehensive design workflow documentation in docs/dev/workflow/design-workflow.md
- Update CLAUDE.md with design work documentation workflow section
- Establishes data/design/issue-{number}/ pattern for local work
- Documents GitHub issue attachment vs comment organization
- Provides reusable process for future design-focused issues
- Maintains repository cleanliness while enabling design iteration
* Snapshpt fo semi-working new entity and attribute edit modal.
* Refactored new forms to remove duplication. Fixed some textarea bugs.
* Fixed textarea attributes to prevent data corruption.
* Fixed new attribute validation error handling.
* Moved Entity attribute formset non-file filtering closer to def.
* Styling impovements.
* Added attribute value history browsing and restoring.
* Fixed styling on new entity edit modal header area.
* Refactored the messy and confusing new entity edit modal.
* Fixed icon in new attruibute form.
* Attribute Edit style changes.
* Fixed bad claude factoring. Fixed CSRF file upload issue.
* Added scroll-to in antinode.js whic helped fix file upload UX issue.
* Fixed styling of the add new attribute card for the 'mark as secret'.
* Added modified field forms styling for new entity edit modal.
* Fixed bug in secret attribute readonly editing logic.
* Added file attribute value editing to new entity edit modal.
* Removed legacy EntityEditView and related code.
* Refactor to remove EntityEditData.
* Refactor to rename: EntityDetailsData -> EntityEditModeData
* More refactoring for name change from "details" to "edit mode"
* Removed debug log messages. Doc typo fix.
* Refactored entity views to add helper classes.
* Coded cleanup and test fixes.
* Refactored to replace use of hardcoded DOM ids, classes, selectors.
* Refactorings: better naming removed debug messages.
* Renamed "property" to "attribute".
* Fixed unit test gaps.
* Replaced hardcoded form field prefixes with common access.
* Added EntityTransitionType to replace brittle "magic" strings.
* Tweaks on display names for entity edit modal.
* Added missing __init__.py to nested test dirs. (New failures found.)
* Working on fixing unit tests: checkpoint. WIP
* Fixed units tests
* Removed mocking from soem unit tests.
* Removed partial V2 implementation mistake from entity edit work.
* Added testing doc about lessons learned.
* Added a bunch of testing-related documentation.
* Fixed test expectation for LocationAttributeUploadView invalid upload
Updated test to expect 200 status with modal form response instead of 400 error, aligning with new form error handling behavior from entity attribute redesign.
8.2 KiB
Coding Standards
Code Organization and Conventions
Directory Structure
Top-level Directory
src: application source codedeploy: helper scripts and files for deploying and setting up the applicationpackage: extra items that need to be packaged up to support running the application in DockerMakefile: provides convenience wrappers around commands for building, packaging and runningdocs: all documentation suitable to be in markdown files
The src Directory
hi: entry point urls/views and some app-wide helper classeshi/templates: For top-level views and app-wide common base templateshi/apps/${APPNAME}: For normal application moduleshi/integrations: Code for dealing with integration not related to a specific integrationhi/services/${SERVICENAME}: Code for a particular integrationhi/simulator: The code for the separate simulator helper apphi/settings: Django settings, including runtime population from environment variableshi/requirements: Python package dependenciescustom: Custom user model and other general customizationsbin: Helper scripts needed with the code to run inside a Docker container
Coding Style
No "magic" strings
We do not use "magic" or hard-coded strings when needing multiple references. Any string that need to be used in two or more places is a risk of them being mismatched. This includes, but is not limited to:
- All references to urls must use the Django url name and reverse() to construct.
- Use enum values or constants/class variables to act as coordination point and provide some amount of compiler help in identifying mismatches. Most needed strings will already have a LabeledEnum type defined, but we should add new ones as needed. See "Enums" in Back End Guidelines.
- All DOM ids and class strings that are shared between client and server must adhere to our
DIVIDpattern. See "Client-Server Namespace Sharing" in Front End Guidelines.
Type Hints
- We add type hints to dataclass fields, method parameters and method return values.
- We do not add type hints to locally declared method variables.
- Some allowed, but not required exceptions:
- The
requestparameter when appearing in a Django view class. - Single parameter methods where the method name or parameter name makes its type unambiguous.
- The
Method Parameter Formatting
For readability, besides adding type hints to method parameters, we adhere to the following formatting conventions:
- For methods with a single parameter, or parameters of native types, they can appear in one line with the method name.
- If more than one parameter and app-defined types, then use a multiple line declaration.
- For methods with three or more parameters, we use one line per parameter and align the type names.
Good Examples
def set_entity( self, entity_id : int ) -> EntityPath:
def set_entity_order( self, entity_id : int, rank : int ) -> EntityPath:
def set_entity_path( self,
entity_id : int,
location : Location,
svg_path_str : str ) -> EntityPath:
Bad Examples
def set_entity_type( self, entity_id : int, entity_type : EntityType ) -> EntityPath:
def set_entity_path( self,
entity_id : int,
location : Location,
svg_path_str: str ) -> EntityPath:
def set_entity_path( self, entity_id : int,
location : Location, svg_path_str: str ) -> EntityPath:
Explicit Booleans
We prefer to wrap all expression that evaluate to a boolean in bool() to make it explicit what type we are expecting:
Good
my_variable = bool( len(my_list) > 4 )
Bad*
my_variable = len(my_list) == 4
Complex Boolean Expressions
- For boolean clauses and conditionals where there are multiple clauses, we prefer to explicitly enclose each clause with parentheses in order to make the intention clear.
- We do not rely on the user having a deep understanding of the compiler's ordeer of precedence.
- We use one line per clause unless the combined clauses are very short and obvious.
- Single boolean typed variables or methods that return a boolean do not need paretheses.
Good
if is_editing and location_view:
pass
if (( hass_state.domain == HassApi.SWITCH_DOMAIN )
and ( HassApi.LIGHT_DOMAIN in prefixes_seen )):
pass
if ( HassApi.BINARY_SENSOR_DOMAIN in domain_set
and device_class_set.intersection( HassApi.OPEN_CLOSE_DEVICE_CLASS_SET )):
pass
Bad*
if hass_state.domain == HassApi.SWITCH_DOMAIN and HassApi.LIGHT_DOMAIN == 'foo':
pass
Flake8 Configurations
The project uses two different flake8 configurations:
Development Configuration (.flake8)
Our preferred style for daily development work, with specific whitespace deviations from PEP8 for enhanced readability:
[flake8]
max-line-length = 110
# Disabled for enhanced readability with spaces:
# E129, E201, E202, E203, E221, E231, E251, W293, W291, W391, W503
ignore = E129,D203,E201,E202,E203,E221,E231,E251,W293,W291,W391,W503
CI Configuration (.flake8-ci)
Hard requirement for pull request approval. GitHub Actions enforces these standards and blocks PR merging if violations exist.
Before submitting any PR, you must run: flake8 --config=src/.flake8-ci src/ and ensure it passes with no violations.
Generated Code Standards
Linting
All generated code (including AI-generated code) must comply with .flake8-ci configuration:
- Final Newlines: All files must end with a single newline character
- Unused Imports: Remove all unused import statements
- Unused Variables: Remove or prefix unused variables with underscore (
_variable) - Line Continuation: Proper indentation for multi-line statements following PEP 8
- Line Length: Respect maximum line length limits defined in
.flake8-ci
Control Flow Statements
- Always include explicit
continuestatements in loops - Always include explicit
returnstatements in functions - This improves code readability and makes control flow intentions explicit
Example:
def process_items(items):
results = []
for item in items:
if not item.valid:
continue # Explicit continue for invalid items
if item.needs_processing:
result = process(item)
results.append(result)
continue # Explicit continue after processing
# Handle non-processing case
results.append(item.default_value)
continue # Explicit continue at end of loop
return results # Explicit return at end of function
Operator Spacing
- Use spaces around assignment operators and most other operators in expressions
- Examples:
x = y + z,result += value,if count == 0 - Exception: Don't add spaces in function keyword arguments (
func(x=y)) or type annotations
Boolean Expressions
When assigning or returning boolean values, wrap expressions in bool() to make intent explicit:
# Good - explicit boolean conversion
is_active = bool(user.last_login)
in_modal_context = bool(request.POST.get('context') == 'modal')
# Avoid - implicit boolean conversion
is_active = user.last_login
in_modal_context = request.POST.get('context') == 'modal'
Related Documentation
- Testing standards: Testing Guidelines
- Backend patterns: Backend Guidelines
- Frontend standards: Frontend Guidelines
- Workflow and commits: Workflow Guidelines
Commenting
- We avoid over-commenting and let the code variable/method naming be clear on the intent.
- We believe in the philosophy: "Before add a comment, ask yourself how the code can be changed to make this comment unnecessary."
- Do not add comments that are not timeless and refer to work in progress or future work. i.e., it must make sense for future readers of the code.