Organize workspace policies by task
This commit is contained in:
@@ -1,6 +1,17 @@
|
||||
# Workspace Conventions
|
||||
|
||||
## Working Conventions
|
||||
These conventions apply to all work in this workspace. Read the specialized
|
||||
policy for the work being performed as well:
|
||||
|
||||
- Read [the Ansible house style](ansible.md) before designing, implementing,
|
||||
or reviewing Ansible roles or collections, including their configuration
|
||||
interfaces.
|
||||
- Read [the testing house style](testing.md) before creating, changing,
|
||||
reviewing, or auditing tests.
|
||||
- Read [the planning policy](planning.md) before preparing or managing a
|
||||
feature roadmap, implementation plan, or active work stream.
|
||||
|
||||
## Workspace-Wide Conventions
|
||||
|
||||
- Confirm the target repository before editing; never treat this directory as
|
||||
a monorepo.
|
||||
@@ -20,141 +31,3 @@
|
||||
run.
|
||||
- Do not duplicate an application contract in workspace documentation when a
|
||||
link and a concise deployment consequence are sufficient.
|
||||
|
||||
## Ansible House Style (Normative)
|
||||
|
||||
- Always use the smallest clear sequence of declarative tasks that reaches the
|
||||
required end state.
|
||||
- Prefer `ansible.builtin` modules where they directly fit the task.
|
||||
`community.general` is an approved, stable dependency and its modules may be
|
||||
selected freely when appropriate; declare and pin the collection wherever a
|
||||
role or collection depends on it.
|
||||
- Do not strengthen the specification. Implement the requested invariants
|
||||
without inventing additional guarantees for integrity, provenance, drift
|
||||
recovery, corruption detection, rollback, or similar behavior.
|
||||
- Prefer convergence over state inspection. Declare what should be true and
|
||||
let the relevant module reconcile it.
|
||||
- Trust Ansible's abstraction boundary. A module that manages state owns the
|
||||
checks necessary to manage that state. Inspect it beforehand only when the
|
||||
observed state is independently required.
|
||||
- State inspection is justified when its result is necessary to decide whether
|
||||
an imperative operation must run, such as checking whether an arbitrary
|
||||
build command's output exists.
|
||||
- Production role tasks establish required application state; they do not test
|
||||
the role's own implementation. Put implementation verification in
|
||||
repository-local tests unless the specification explicitly requires runtime
|
||||
validation during convergence.
|
||||
- For an unavoidable `command` or `shell` task, declare the operation's change
|
||||
semantics as required by ansible-lint. A conditionally executed mutation may
|
||||
use `changed_when: true`; a read-only command should use
|
||||
`changed_when: false`. This describes an imperative operation and does not
|
||||
replace the idempotence of a state-management module.
|
||||
- Apply `become: true` to individual tasks that require elevated privileges.
|
||||
Do not require callers to escalate an entire play merely to use a role.
|
||||
- Complexity requires a requirement. Every additional task, conditional,
|
||||
registered variable, or state probe must correspond to behavior the role is
|
||||
required to provide.
|
||||
- Persisted role-private state is a design smell. Fingerprints, checksum
|
||||
databases, stamps, manifests, and similar bookkeeping require specific
|
||||
justification.
|
||||
- **Assume convergence first.** Before adding machinery for an edge case,
|
||||
determine whether rerunning the ordinary desired-state operations already
|
||||
repairs it.
|
||||
- Consider whether a conditional task or a notified handler best expresses the
|
||||
dependency. A `when` condition keeps triggers visible at the action site but
|
||||
grows with their number. Notifications keep the action independent of the
|
||||
number of notifiers, but distribute the control flow and defer execution
|
||||
until the next handler flush. An explicit
|
||||
`ansible.builtin.meta: flush_handlers` affects all pending handlers in the
|
||||
play.
|
||||
- Treat `rakestraw-ansible` as a style reference for direct tasks, shallow
|
||||
control flow, and concise variable use, while retaining current practices
|
||||
such as fully qualified collection names.
|
||||
|
||||
## Testing House Style (Normative)
|
||||
|
||||
- Use a classical state-based approach, also commonly described as Chicago or
|
||||
Detroit style. Exercise the real role through its public interface and
|
||||
observe the resulting deployed system.
|
||||
- Test behavior owned by the role, not the current arrangement of tasks that
|
||||
implements it. A useful test should normally remain valid after an internal
|
||||
refactor that preserves the same public behavior.
|
||||
- Require an immediate second role application to report no changes. Clean
|
||||
repeated convergence is a first-class role outcome, not a test of any one
|
||||
module.
|
||||
- Distinguish configurable behavior from current defaults. To prove version
|
||||
selection, explicitly request a version and verify that the application
|
||||
reports that version. Do not assert that the same version remains the role's
|
||||
default. For example, a useful test sets `audita_version: v0.9.0` and verifies
|
||||
that the installed Audita reports `v0.9.0`; a harmful test asserts that
|
||||
`v0.9.0` is the role's current default. The former continues to prove version
|
||||
selection after the default advances to `v0.10.0`, while the latter fails
|
||||
merely because of the intended update.
|
||||
- Use a default invocation as a smoke test when useful, but do not snapshot
|
||||
exact default values, selected commits, complete default configuration
|
||||
documents, or counts of default variables. Those assertions are change
|
||||
detectors that turn intentional updates into unrelated test failures.
|
||||
- Do not assert task names, task order, individual `changed` or `skipped`
|
||||
results, or other implementation traces. Inspect the final system and the
|
||||
overall clean-repeat result instead.
|
||||
- Do not test documented behavior of `ansible.builtin`, widely used
|
||||
`community.general`, or other trusted modules. In particular, do not
|
||||
independently verify file existence, ownership, permissions, package state,
|
||||
checkout state, or template deployment merely to repeat module arguments.
|
||||
- Test role-authored decisions only when they express meaningful public
|
||||
behavior. Examples include an explicitly selected application version, a
|
||||
caller-supplied build executable, configuration rendering with representative
|
||||
non-null and falsey values, and composition of caller-supplied mappings.
|
||||
- Verify application outcomes with the application's native interface when
|
||||
practical. Prefer executing the installed application or its configuration
|
||||
validator over reconstructing its rules in Ansible assertions.
|
||||
- Self-evident declarative guards, including straightforward platform
|
||||
assertions, do not require a negative test matrix.
|
||||
- Prefer one representative case for a behavior over exhaustive combinations.
|
||||
Every additional test must protect a requirement or a plausible regression,
|
||||
not merely increase coverage.
|
||||
- Keep syntax checking and linting as static validation. They complement the
|
||||
behavioral suite but do not justify tests that freeze implementation details.
|
||||
|
||||
## Application Configuration Interfaces
|
||||
|
||||
- Expose application configuration through documented, field-specific role
|
||||
variables suitable for `host_vars`, `group_vars`, play variables, and
|
||||
composing roles.
|
||||
- Give structurally required fields concrete defaults. Define optional
|
||||
application settings as `null` unless the role intentionally owns the
|
||||
corresponding policy.
|
||||
- Treat a null optional variable as "omit this field from the rendered
|
||||
configuration." Do not copy documented upstream defaults into role defaults
|
||||
merely to write them back to the application.
|
||||
- Let the application supply its built-in defaults for omitted fields. This
|
||||
avoids freezing a duplicate defaults database in Ansible and permits upstream
|
||||
default changes to take effect naturally.
|
||||
- Use `is not none` when conditionally rendering optional values. Do not use
|
||||
truthiness, because false, zero, an empty string, or an empty collection may
|
||||
be an intentional value.
|
||||
- Emit a nested configuration section only when at least one of its descendant
|
||||
variables is non-null.
|
||||
- Serialize strings and collections with appropriate Ansible or Jinja filters.
|
||||
Do not construct YAML quoting manually.
|
||||
- Keep argument specifications aligned with the public variables. Optional
|
||||
nullable variables are not required; use a permissive type only when the
|
||||
native application accepts more than one scalar type.
|
||||
- Prefer one field-specific interface. Do not provide a parallel raw mapping,
|
||||
template override, or merge mechanism unless a concrete requirement defines
|
||||
its precedence and behavior.
|
||||
- Application roles expose native configuration choices. The suite-level role
|
||||
supplies cross-application and deployment-specific policy through those
|
||||
variables.
|
||||
- Manage the resulting file with one ordinary `ansible.builtin.template` task.
|
||||
Do not add configuration assembly facts, preliminary file inspection, or
|
||||
production validation unless independently required.
|
||||
- Configuration tests should use explicitly supplied representative values to
|
||||
exercise conditional rendering, null omission, intentional falsey values,
|
||||
native configuration validity, and caller-facing composition boundaries.
|
||||
They should follow the testing house style above rather than snapshotting the
|
||||
current default document.
|
||||
|
||||
Role defaults define what Ansible intends to manage, not a mirror of every
|
||||
application default. A concrete default means the role deliberately owns that
|
||||
policy; a null default leaves the policy with the application.
|
||||
|
||||
Reference in New Issue
Block a user