diff --git a/doc/.vitepress/config.mts b/doc/.vitepress/config.mts index 7038c81..d5f6b32 100644 --- a/doc/.vitepress/config.mts +++ b/doc/.vitepress/config.mts @@ -14,6 +14,11 @@ export default defineConfig({ description: "A transparent guide to OSBR’s culture, values, and workflows.", head: [ ["link", { rel: "icon", href: "favicon.svg", type: "image/svg+xml" }], + // Belt-and-suspenders LLM hint for agents that read (the visible + // line in index.md is the load-bearing one; a static host can't set the + // matching HTTP Link: header). + ["link", { rel: "alternate", type: "text/plain", href: "/llms.txt", title: "llms.txt (machine-readable handbook index)" }], + ["link", { rel: "alternate", type: "text/plain", href: "/llms-full.txt", title: "llms-full.txt (full handbook text for LLMs)" }], [ "script", { @@ -79,7 +84,67 @@ export default defineConfig({ { text: "Terraform", link: "/style-guide-terraform" }, ], }, + { + text: "Design Guidelines", + link: "/design-guidelines", + collapsed: true, + items: [ + { text: "Accessibility", link: "/accessibility" }, + { text: "Self-Explanatory UI", link: "/self-explanatory-ui" }, + { text: "Modeless Design", link: "/modeless-design" }, + { text: "Interaction Design", link: "/interaction-design" }, + ], + }, { text: "Database Guidelines", link: "/database-guidelines" }, + { + text: "Planning & Shaping", + link: "/development-guide#_2-4-planning-shaping", + collapsed: true, + items: [ + { text: "Market Research", link: "/market-research" }, + { text: "Requirements Modeling", link: "/requirements-modeling" }, + { text: "Verify Before Building", link: "/verify-before-building" }, + { text: "Cost Estimation", link: "/cost-estimation" }, + { text: "IT Investment Evaluation", link: "/it-investment-evaluation" }, + { text: "Legal Compliance", link: "/legal-compliance" }, + { text: "Domain Terminology", link: "/domain-terminology" }, + { text: "Capability over Track Record", link: "/capability-over-track-record" }, + ], + }, + { + text: "Quality Gate", + link: "/quality-gate", + collapsed: true, + items: [ + { text: "Testing Standards", link: "/testing-standards" }, + { text: "Observability & Resilience", link: "/observability-resilience" }, + { text: "Incident Management", link: "/incident-management" }, + { text: "Code Review", link: "/code-review" }, + { text: "CI/CD Pipeline", link: "/ci-cd-pipeline" }, + { text: "Application Security", link: "/application-security" }, + { text: "Access Control", link: "/access-control" }, + { text: "Data Protection", link: "/data-protection" }, + { text: "Supply Chain & Risk", link: "/supply-chain-risk" }, + { text: "Architecture Standards", link: "/architecture-standards" }, + { text: "Repository & Documentation Standards", link: "/repository-documentation-standards" }, + { text: "API Design", link: "/api-design" }, + ], + }, + { + text: "AI Usage Guideline", + link: "/ai-usage-guideline", + collapsed: true, + items: [ + { text: "AI Data-Handling", link: "/ai-data-handling" }, + { text: "Multiple AI Agents", link: "/multiple-ai-agents" }, + { text: "Overnight AI Operation", link: "/overnight-ai" }, + { text: "Weekly AI Quota", link: "/weekly-ai-quota" }, + { text: "Voice Input", link: "/voice-input" }, + { text: "Meeting Recording", link: "/meeting-recording" }, + { text: "Policies as Plugins", link: "/policies-as-plugins" }, + { text: "Building for AI Users", link: "/building-for-ai-users" }, + ], + }, ], }, { @@ -97,6 +162,9 @@ export default defineConfig({ text: "Infrastructure Planning Policy", link: "/infra-planning-policy", }, + { text: "Security Policy", link: "/security-policy" }, + { text: "Ethical Design Policy", link: "/ethical-design-policy" }, + { text: "Privacy Policy", link: "/privacy-policy" }, ], }, ], diff --git a/doc/access-control.md b/doc/access-control.md new file mode 100644 index 0000000..c1706a2 --- /dev/null +++ b/doc/access-control.md @@ -0,0 +1,416 @@ +# Access Control + +This is the standard the [Quality Gate](/quality-gate)'s **Security** lens holds +work to for knowing *who a user is* and deciding *what they may do*. It expands +the [Security Policy](/security-policy) — which sets the organisational rules +(account MFA, least privilege, quarterly access review) — into a working +standard for the authentication and authorization built *inside* an application: +how we procure it, how we model it, and how we wall off the most dangerous +surface of all, the admin plane. It sits under the [Infrastructure Planning +Policy](/infra-planning-policy)'s Zero Trust posture ("never trust, always +verify") and works alongside the [Database Guidelines](/database-guidelines), +because the last line of defence for row-level rules is the database itself. +Deviations are allowed, but — as everywhere in the handbook — they must be +deliberate and justified in the project's design notes. + +Access control is where OSBR's values point outward, at the people whose data we +hold. **Be Kind**: a home-grown login screen can look identical to a bought one +and be one missing check away from leaking every account, so we do not make users +the test subjects for auth code a hardened solution would have gotten right. +**Be Strong**: we choose the boring, adversarially-tested path over the flattering +one of building it ourselves, and we hold the admin boundary as a hard wall, not a +role flag. **Be Nice**: the authorization model is documentation a teammate reads +to learn who may touch what, so it lives in one legible place, in the same pull +request as the data it guards. Humans and AI agents build these controls as +collaborators, and both are held to exactly the same bar. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the [Coding Style + Guide](/style-guide). **MUST** / **MUST NOT** are absolute. **SHOULD** / + **SHOULD NOT** state a strong default overridable only with a documented + reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry standard, it is named + inline and cited under [References](#references). We adopt the *criteria* of + large-scale practices — PAM, control-plane/data-plane separation, the NIST + control families — and right-size them for an SME, taking their criteria + without their headcount. + +[[TOC]] + +## 1. Goal + +Every protected action in an OSBR system is guarded by an access decision that +is **procured responsibly, modelled at the lowest sufficient power, enforced +where it cannot be bypassed, and — for administrative capability — entered as a +separate, logged, deliberate act.** Concretely: + +- **Authentication is bought, not built.** Knowing *who a user is* reaches for a + hardened existing solution first; custom auth is the exception that must + justify itself in writing. +- **Authorization is the simplest sufficient model.** The decision expresses the + actual access rules the product requires and no more, lives in one auditable + layer, and denies by default. +- **Row-scoped rules reach the database.** Access scoped to rows is enforced by + the database, not only by application code a forgotten `WHERE` clause can + bypass. +- **The admin surface is a separate place.** Administrative capability lives on + its own surface, behind its own authentication event, with every write left as + an immutable, attributable audit record. + +A control that does not serve one of these goals is either missing or waste. We +optimise for a decision a reviewer can read end to end, not for a policy engine +nobody yet needs — unread complexity someone debugs at 3am is not "secure". + +## 2. Responsibility + +- The **author of a change owns its access control.** "Done" includes the + authorization model: a pull request that adds or changes a table MUST state, + in the same PR, who may read/write those rows, under what rule, and where the + check is enforced. A schema change with no authz note is incomplete. This is + the same implementer-owns-quality rule the [Quality Gate](/quality-gate) + states — verification is planned at design, not handed to a later stage. +- The **reviewer** (per the [Security Policy](/security-policy)'s mandatory + security review) verifies that the chosen model is the *simplest sufficient* + one, that the check sits in one place, that row-level rules reach the database, + and — for auth — that a buy-vs-build evaluation exists. +- The **project lead / architect** owns the admin boundary. The admin surface is + designed as a separate plane from day one; retrofitting isolation onto a shared + origin is expensive and error-prone. +- The **infrastructure owner** provisions the network restriction, the immutable + audit store, the separate identity configuration, and the break-glass path — all + as code (see the [Infrastructure Planning Policy](/infra-planning-policy)). +- **AI agents** are first-class contributors of access-control code and are held + to exactly the same bar; the human who merges an agent's work owns it. + +## 3. Practices — Procure authentication before you build it + +### 3-1. Buy-before-build is the default; the burden of proof is on building + +The oldest rule in the field applies: **do not roll your own auth or crypto.** +The failure modes — credential theft, session fixation, account takeover, +privilege escalation — land on *users*, not on us. + +- Before any custom authentication or authorization is scoped, the project MUST + produce a **short written evaluation** naming its requirements (identity + sources, MFA/AAL target, SSO/federation needs, user volume, data residency, + budget) and scoring candidate solutions against them. +- If the decision is to build, the evaluation MUST document **why every candidate + was rejected** — a specific unmet requirement per candidate, not a general + "none felt right." Absent that record, buy. +- "Build" means writing our own password hashing, session issuance, token + signing, MFA enrolment, or federation handling. Configuring, theming, and + integrating a bought solution is **not** building — it is the expected work. +- Build-vs-buy is a **TCO and security-surface argument, not a licence-fee one.** + The cost of building is perpetual: patching the CVE class you now own, staffing + the on-call for the 3am account-takeover, passing the audit for code only you + have reviewed. A subscription that moves that surface to a vendor whose whole + business is defending it is usually cheaper *and* safer. + +### 3-2. Express auth against named standards, not improvisation + +Auth requirements and designs MUST be grounded in named standards so the +evaluation is checkable rather than improvised: + +- **OWASP ASVS** — the requirements checklist. Its Authentication and Session + Management chapters define what any solution, bought or built, must satisfy; a + custom build would have to meet them line by line. +- **NIST SP 800-63B** — the assurance-level yardstick. The project MUST state a + target **Authenticator Assurance Level (AAL1 / AAL2 / AAL3)** and pick a + solution that meets it. Handling personal or client-privileged data (see the + [Security Policy](/security-policy) protected-assets list) generally means + **AAL2** (MFA required); high-value admin paths lean **AAL3** (hardware-backed, + phishing-resistant). +- **OAuth 2.0 / OpenID Connect** — the protocols for delegated authorization and + federated authentication. Prefer these for third-party login and for + issuing/validating tokens; do not invent a token format or grant flow. +- **SAML 2.0** — the enterprise-SSO protocol many client identity providers still + speak. A client SAML mandate is a *buy* signal; a hand-rolled SAML + implementation is a classic source of critical bugs. +- **Passkeys / WebAuthn / FIDO2** — the phishing-resistant, passwordless + authenticators the [Security Policy](/security-policy) already mandates for + internal accounts. User-facing auth SHOULD offer passkeys, and the chosen + solution SHOULD support WebAuthn natively rather than us building the ceremony. + +### 3-3. Keep authentication separate from authorization + +The two concerns MUST NOT be conflated in design or procurement: + +- **Authentication** answers *who is this?* — login, MFA, sessions, tokens. This + is the part we almost always **buy**, because it holds the crypto and the + well-known attack surface. +- **Authorization** answers *what may they do?* — roles, permissions, tenant + isolation, resource ownership. This is usually **application-domain logic** we + own, because it encodes *our* business rules, though the enforcement primitives + (scopes, claims, policy engines) can still be bought. +- A bought authentication provider gives you an identity and a token; it does + **not** know your domain's permission model. The project MUST state where the + authentication boundary ends and its own authorization logic begins, so no one + assumes the provider is enforcing rules it was never told about. + +### 3-4. Document what the chosen solution does not cover + +Every auth decision MUST record its **gaps** — what the solution explicitly does +*not* handle — so those gaps are owned rather than silently assumed away. State +coverage for, at minimum: authorization / fine-grained permissions (usually ours, +not the provider's); account recovery and de-provisioning; audit logging of auth +events; session revocation and token lifetime; data residency of the identity +store and PII; and rate limiting / brute-force and enumeration protection at *our* +edges. A gap that is written down is a task; a gap that is assumed covered is an +incident. + +### 3-5. If you must build, treat the primitives as library, not project, code + +If custom auth is genuinely unavoidable, the crypto and session/token primitives +are **library code, not project code** (per [Style Guide](/style-guide)): use +vetted, standard implementations (a maintained OIDC library, a standard +password-hash such as Argon2 via its reference library), never a bespoke +algorithm. Reserve original code for the *authorization* domain logic, which is +legitimately ours. The bar to clear before any custom auth code is scoped: the +written evaluation (§3-1), an AAL target (§3-2), an authn/authz boundary +statement (§3-3), and a documented gap list (§3-4) — all four must exist first. + +## 4. Practices — Choose and place the authorization model + +### 4-1. Climb the escalation ladder; stop at the first rung that states the rule + +Authorization models trade expressiveness for complexity. Pick the **least +powerful model that can state your rule** and escalate only when a concrete, +named rule cannot be expressed at the current rung — least privilege applied to +the *mechanism itself*, not just the grants. + +1. **Owner check** — "the actor owns this row / is the actor." A single ownership + predicate (`resource.owner_id == actor.id`). Most CRUD apps never need more. + Start here. +2. **RBAC (roles)** — permissions attach to named roles, users hold roles. Reach + for it when access depends on a *job function* (admin, editor, viewer) rather + than ownership. This is the NIST RBAC model (ANSI INCITS 359): roles, role + hierarchies, and permission assignments. +3. **Admin escape hatch** — a coarse "staff can see everything" capability is fine + as an explicit, audited RBAC role (and see §5). Keep it deny-by-default for + everyone else. +4. **ABAC / ReBAC / policy engine** — only when a rule depends on *attributes* + (time, location, resource state, clearance) or on *relationships between + objects* that roles cannot enumerate ("editors of the parent folder", "members + of the owning team"). ABAC is defined in NIST SP 800-162; relationship-based + access at scale is the Zanzibar model. Externalise policy with a tool such as + Open Policy Agent only when policy genuinely needs to live outside application + code (multiple services sharing one policy, non-developer policy authors). + +The trigger to move up a rung is a **specific rule you cannot currently express** +("a contractor may edit a document only during the project window, and only if +they belong to the owning team"). "We might need ABAC later" is not a trigger. +YAGNI applies to authorization models too. + +### 4-2. Deny by default + +The absence of an explicit grant is a denial. New endpoints, new columns, and new +resource types are **inaccessible until a rule says otherwise** — never accessible +until a rule forbids them. This is fail-safe defaults (Saltzer & Schroeder) and +the baseline expectation of OWASP ASVS: access control fails closed and is +enforced on the server. + +### 4-3. One auditable authorization layer + +The authorization decision lives in **one place** a reviewer can point to — a +single middleware, guard, or policy module — not smeared across controllers, view +templates, and query builders. A reviewer MUST be able to answer "where is this +checked?" with one file, and "what does it decide?" by reading it. + +- Controllers and UI call *into* the authorization layer; they MUST NOT + re-implement the decision. +- The UI hiding a button is **not** authorization — it is convenience. The real + check is server-side (OWASP ASVS). Client-side hiding is never the control. + +### 4-4. Enforce row-level rules at the database + +When access is scoped to *rows* ("a user sees only their own orders", "a tenant +sees only its own data"), enforce it in the database with row-level security (for +example, PostgreSQL Row-Level Security), not only in application queries. +Application-only filtering is one forgotten `WHERE` clause away from a cross-tenant +leak; a database policy holds even when a query forgets. This is defence in depth, +consistent with the [Infrastructure Planning Policy](/infra-planning-policy)'s +"assume breach". Row-level rules belong in the database as a *backstop* — coarser +role and ownership decisions can still live in the single application layer (§4-3). +Use both: the app layer for the readable decision, the database for the guarantee. + +### 4-5. Document the authz model in the same PR as the schema + +The authorization model is part of the data model. When a PR adds or changes a +table, that same PR states, in prose or a short table: **who may read/write these +rows, under what rule, and where the check is enforced** (owner predicate, RBAC +role, database policy, external policy). Schema and its access rules are reviewed +together or not at all. Grants SHOULD be reviewed periodically and the unused ones +removed, per the [Security Policy](/security-policy)'s quarterly access review. + +## 5. Practices — Isolate administrative functions + +An admin panel is not "the app with more buttons." It is a different blast radius: +a stolen session on the user surface exposes one account; the same on the admin +surface exposes the whole tenant. We model the admin surface as the system's +**control plane** and keep it architecturally distinct from the **data plane** +that serves users. + +### 5-1. A separate surface, not a route prefix + +Administrative functions MUST be served from a surface distinct from the user +application: + +- A **separate domain or subdomain** (e.g. `admin.example.com`), never merely + `example.com/admin` sharing the user origin, session cookie, and code path. + Sharing an origin means the admin panel inherits every XSS, CSRF, dependency, + and session weakness of the far larger user app. +- The admin surface SHOULD be **network-restricted** — reachable only from company + access paths (IP allow-list / Zero Trust access), consistent with the + staging/production access rules in the [Security Policy](/security-policy). A + public login page is a bigger attack surface than no login page. +- Admin and user surfaces SHOULD NOT share an authentication session or token + audience. A user session token MUST NOT be silently promotable to admin by + adding a claim. This is the OWASP ASVS position on administrative interfaces: + isolate them and protect them more strongly than the rest of the application. + +### 5-2. A separate authentication event (step-up, MFA minimum) + +Reaching the admin surface MUST require its **own authentication event**: + +- **MFA is the minimum** — a phishing-resistant second factor (passkey / WebAuthn + preferred), per the [Security Policy](/security-policy). A role check on an + already-established user session is **NOT** sufficient. +- Sensitive or destructive admin actions SHOULD trigger **step-up + (re-authentication)** even within an admin session — the operator proves + presence again before a high-impact write, following NIST SP 800-63B guidance + on re-authentication for sensitive operations. +- Admin sessions SHOULD be short-lived and idle-timeout aggressively; they are + more valuable than user sessions, so they should live shorter. + +### 5-3. Just-in-time, not standing privilege + +Follow **Privileged Access Management (PAM)** practice: privilege is granted when +needed and expires, not held permanently. + +- Admins SHOULD hold no standing admin rights by default; they **elevate + just-in-time (JIT)** for a bounded window, and the grant drops automatically. +- Grants SHOULD be scoped to the task (least privilege, NIST SP 800-53 **AC-6**) — + the narrowest role that does the job, for the shortest time. +- Privileged accounts MUST be **separate identities** from the same person's + ordinary user account (NIST SP 800-53 **AC-2**, **AC-6(2)/(5)**): one human, two + accounts, so routine activity never runs with admin rights. + +### 5-4. Break-glass (emergency access) + +There MUST be a documented **break-glass** path for when normal admin auth is +unavailable (identity-provider outage, locked-out operator): + +- Break-glass credentials are highly privileged, rarely used, and stored + **offline / sealed** (e.g. in a sealed secret, not a daily-driver vault entry). +- Using break-glass MUST raise an alert and generate an audit record the same as + any other admin action — ideally a louder one. Any use is reviewed after the + fact. +- The procedure — who, when, and how it is re-sealed and rotated afterwards — MUST + be written down before it is needed. + +### 5-5. Audit every admin write — immutable, attributable, retained ≥ 1 year + +Every administrative **create / update / delete** MUST produce an audit record. +The boundary is only trustworthy if we can prove, after the fact, who did what. + +- Each record MUST capture, at minimum (NIST SP 800-53 **AU-2 / AU-3 / AU-12**): + **who** (the named human identity, not a shared "admin" account), **what** (the + action and its target), **when** (a trustworthy timestamp), **where/how** + (source IP / access path, and whether it was a normal or break-glass session), + and **outcome** (success or failure — failed admin attempts are logged too). +- Records MUST be written to an **append-only / immutable** store — WORM semantics + such as object-lock, an immutable log bucket, or an equivalent the admin + application itself cannot rewrite or delete (NIST SP 800-53 **AU-9**). The + admins being logged MUST NOT be able to edit their own trail. +- Records MUST be retained for **at least one year** (NIST SP 800-53 **AU-11**), + longer where a client contract or law requires it. The audit store SHOULD be + isolated from the admin surface — a compromise of the admin plane must not also + grant delete over its own evidence — shipped as event streams to a separate + store (see the [Infrastructure Planning Policy](/infra-planning-policy)). +- High-risk actions (bulk export, permission grants, break-glass use, mass delete) + SHOULD **alert in real time**, not merely sit in a log nobody reads, and audit + logs SHOULD be reviewed as part of the quarterly access review the + [Security Policy](/security-policy) already mandates. + +"Immutable" is right-sized: object-lock / WORM on the log store plus an isolated +account is enough. You do not need a dedicated SIEM to satisfy this policy — you +need a trail the logged party cannot alter and a retention clock of at least a +year. + +## 6. Anti-patterns + +The failure modes this policy exists to prevent: + +- ❌ A home-grown login screen — custom password hashing, session issuance, or + token signing — shipped without a written buy-vs-build evaluation. +- ❌ Assuming the bought identity provider enforces your permission model. It + authenticates; your domain authorizes. +- ❌ Scattered ad-hoc `if (user.role === ...)` checks across controllers, + templates, and queries instead of one auditable layer. +- ❌ Row-scoped access enforced only by application `WHERE` clauses, one forgotten + filter away from a cross-tenant leak. +- ❌ `/admin` on the same origin, guarded only by `if (user.role === "admin")` on + the user session. One session token, one blast radius. +- ❌ A shared `admin@` login used by several people — no attribution, so the audit + trail cannot name a human. +- ❌ Standing, permanent admin rights on daily-driver accounts. +- ❌ Audit logs writable (or deletable) by the same admin role they record, or + logging that captures reads but not the destructive writes. + +## References + +**Foundations & least privilege** + +- Saltzer, J. H. & Schroeder, M. D., *The Protection of Information in Computer + Systems* (1975) — least privilege, fail-safe defaults — + +- OWASP Application Security Verification Standard (ASVS) — authentication, + session management, authorization, and logging chapters; administrative-interface + isolation — +- OWASP Top 10 — + +**Authentication & identity (NIST / protocols)** + +- NIST SP 800-63B, *Digital Identity Guidelines* — Authenticator Assurance Levels, + re-authentication — +- OAuth 2.0 — — and OpenID Connect + — +- SAML 2.0 — +- WebAuthn / FIDO2 passkeys — + +**Authorization models** + +- NIST, *Role-Based Access Control (RBAC)* / ANSI INCITS 359 — + +- NIST SP 800-162, *Guide to Attribute Based Access Control (ABAC)* — + +- Google, *Zanzibar: Google's Consistent, Global Authorization System* (USENIX ATC + 2019) — relationship-based access (ReBAC) — + +- Open Policy Agent — policy-as-code engine — +- PostgreSQL — Row Security Policies — + + +**Privileged access & audit** + +- Privileged Access Management (PAM) — just-in-time elevation, session isolation, + credential vaulting (NIST references under AC-6 / AC-2). +- NIST SP 800-53 Rev. 5 — AC family (AC-2 account management, AC-6 least privilege + incl. AC-6(2)/(5)) and AU family (AU-2/AU-3/AU-12 logging, AU-9 protection of + audit info, AU-11 retention) — + +- NIST SP 800-207, *Zero Trust Architecture* — + + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Security lens this standard serves. +- [Security Policy](/security-policy) — account MFA/passkey rules, access paths, + quarterly access review, protected assets. +- [Infrastructure Planning Policy](/infra-planning-policy) — Zero Trust posture, + least-privilege credentials, immutable log streams, IaC for the isolation. +- [Database Guidelines](/database-guidelines) — where row-level security policies + live. +- [Coding Style Guide](/style-guide) — library vs project code, RFC 2119 levels. +- [Development Guide](/development-guide) — the pull-request review surface. diff --git a/doc/accessibility.md b/doc/accessibility.md new file mode 100644 index 0000000..3a14da4 --- /dev/null +++ b/doc/accessibility.md @@ -0,0 +1,181 @@ +# Accessibility + +We put **accessibility at the start of the experience, not at the end of it.** +This page sets the floor every OSBR product clears for the people who use it, and +it extends that same floor in a direction many teams still skip: a product that +is genuinely reachable must be reachable by **AI agents too** — as operators +acting on a user's behalf, and as consumers of the information the product holds. +It builds on the concrete rules in the [Design Guidelines](/design-guidelines), +shares its machine-access half with [Building for AI +Users](/building-for-ai-users), and is held to work by the [Quality +Gate](/quality-gate)'s review. Deviations are allowed, but — as everywhere in the +handbook — they must be deliberate and justified in the project's design notes. + +Accessibility is where our values meet the person, or agent, actually in front of +the product. **Be Nice**: meet people through the tools they already rely on — a +screen reader, a keyboard, an agent — rather than the one input we happened to +design for. **Be Kind**: assume the user reaching for a feature may be a person +*or* the AI acting for them, and let both in without a fight. **Be Strong**: hold +a real, tested floor rather than "accessible enough" — the failure we guard +against is invisible to the person who never has to hit it. This commitment runs +through **human ⇄ AI** cooperation the same way it runs through human use. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as elsewhere in the handbook. **MUST** / + **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a strong default + overridable only with a documented reason. **MAY** marks a free choice. +* **Named standards.** Where a rule adopts an external standard (WCAG, WAI-ARIA), + the standard is named inline and cited under [References](#references). We adopt + its *criteria* and right-size the process around them. + +[[TOC]] + +## 1. Goal + +Every OSBR product is usable, from the first interaction, by: + +- **people using assistive technology** — screen readers, keyboard-only + navigation, switch devices, magnification — and +- **AI agents** acting as operators on a user's behalf, or reading the product as + a source of information, + +**without either being forced to reverse-engineer a UI built only for a sighted +mouse user.** Accessibility is designed in at the start, not bolted on as +late-stage polish. + +The reasoning is one idea, not two. An interface that states its meaning and its +operations explicitly — semantic structure for people, declared tools for +machines — is reachable by whoever, or whatever, shows up. A screen reader and an +AI agent both consume a product through its *stated* structure, never its pixels; +build well for the one and you have most of the other. This is the +inclusive-design bargain: designing for the edges makes the centre better for +everyone. + +## 2. Responsibility + +- **Every engineer and designer** owns accessibility for the surfaces they build. + It is not a specialist's job handed off at the end, and it is not the reviewer's + to discover after the fact — the same implementer-owns-quality rule the [Quality + Gate](/quality-gate) states everywhere. +- **Designers** account for keyboard order, focus, contrast, and non-visual + meaning in the design itself, before implementation begins. +- **Engineers** build from semantic HTML, verify with a real screen reader and + keyboard, and expose machine-callable operations where the product does real + work. +- **Reviewers** treat a missing label, an unreachable control, or an unexposed + core operation as a defect, not a nice-to-have — part of the reviewable surface, + exactly like tests. +- **Project leads** scope the accessibility floor and the machine-access surface + into the work from the start, so neither is deferred to a later "accessibility + pass" that never arrives. + +## 3. Practices + +### 3-1. Accessibility comes first, not last + +Accessibility is a **starting constraint**, alongside "does it work" — never a +finishing touch. A control that cannot be reached by keyboard, or an image with no +text alternative, is an **unfinished feature**, exactly as a broken button is. + +- Accessibility MUST be scoped into a feature from the start; it MUST NOT be + deferred to a later pass. +- Designs SHOULD be reviewed for keyboard order, focus, and non-visual meaning + *before* implementation begins, per the [Development Guide](/development-guide). + +### 3-2. WCAG 2.2 AA is the floor + +[WCAG 2.2 AA](https://www.w3.org/TR/WCAG22/) is the **minimum**, not the target — +the target is a product people actually find easy. 2.2 is the current W3C +Recommendation and supersedes 2.1; the handbook is standardized on **2.2 AA**, and +the [Design Guidelines](/design-guidelines) carry the concrete rules that meet it. + +- Every product MUST meet **WCAG 2.2 AA** as its floor. + +### 3-3. Verify with a screen reader and keyboard — not just a linter + +Automated checkers ([axe](https://www.deque.com/axe/), Lighthouse) catch perhaps a +third of issues. They cannot tell us whether a flow *makes sense* announced aloud +or driven by Tab alone — and that is the part that decides whether the product is +actually usable. + +- Every UI-bearing feature MUST be verified before it ships: + - **keyboard-only** — every control reachable, operable, and showing a visible + focus indicator, with no keyboard traps; and + - **with a screen reader** (VoiceOver, NVDA, or Orca) — every control announcing + a sensible name and role, and the reading order matching the visual order. +- Automated tooling MAY gate the obvious regressions, but MUST NOT be the only + check. + +### 3-4. Build from semantic HTML; ARIA only to fill gaps + +Native semantic elements carry role, state, focus, and keyboard behaviour for +free. We reach for them first. + +- UI MUST be built from **semantic HTML first**. +- [WAI-ARIA](https://www.w3.org/WAI/ARIA/apg/) MUST be used only to *supplement* + what no native element can express — never to re-implement a native control on a + `
`. The first rule of ARIA is: don't use ARIA when a native element already + does the job. +- The [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/) is the + reference for the rare patterns that genuinely need it; the concrete HTML/CSS + rules live in the [Design Guidelines](/design-guidelines). + +### 3-5. Open operations to AI agents + +Where a product performs real operations — search, create, update, submit, +query — we expose those operations as **machine-callable tools** so an AI agent +can operate the product and read its information without scraping the DOM or +guessing from screenshots. This is the same accessibility problem as a screen +reader operating the product: meaning must be *stated*, not painted. The full +agent-facing standard lives in [Building for AI Users](/building-for-ai-users); +this page states the floor it shares with human accessibility. + +- Products that perform real operations SHOULD expose them as + [MCP](https://modelcontextprotocol.io/)-compatible tool definitions — each a + named tool with a declared input schema, a declared output, and a description of + what it does. +- Web products SHOULD prefer the browser-native + [WebMCP](https://webmachinelearning.github.io/webmcp/) surface + (`navigator.modelContext`) where the platform supports it, so the page itself + declares its tools to an in-browser agent — the site *is* the server, no + separate backend required. +- These tools SHOULD cover **both read and write**: an agent is an information + *consumer* (read) and an *operator* (write); a read-only surface leaves it + half-blind, a write-only one leaves it unable to check its own work. +- The agent surface MUST enforce the **same authentication and authorization** as + the human surface. Opening to AI is not opening a side door — an agent acts *as* + a user and inherits exactly that user's permissions, nothing more. +- Teams SHOULD add at least one flow driven **end-to-end by an AI agent** to the + acceptance checks for agent-facing surfaces, mirroring the screen-reader + walk-through and the [Development Guide](/development-guide)'s test plan. + +*Same principle, one rung further: declared tools serve the machine consumer the +way semantic HTML serves the assistive-technology consumer.* + +## References + +**Accessibility standards & guidance** + +- WCAG 2.2 (W3C Recommendation) — +- WAI-ARIA Authoring Practices Guide (APG) — +- W3C WAI — Accessibility Fundamentals & Inclusive Design — +- HTML — the semantic elements (MDN) — + +**Verification** + +- Deque axe (automated checks) — +- NVDA screen reader — +- Apple VoiceOver — + +**Machine access (AI agents)** + +- Model Context Protocol (MCP) — +- WebMCP (`navigator.modelContext`, W3C Web Machine Learning Community Group) — + +**Related handbook pages** + +- [Design Guidelines](/design-guidelines) — the concrete HTML/CSS accessibility rules. +- [Building for AI Users](/building-for-ai-users) — the agent-facing surface this page shares its machine-access half with. +- [Quality Gate](/quality-gate) — the review that holds this floor. +- [Development Guide](/development-guide) — pull-request specification, test plan, and acceptance checks. diff --git a/doc/ai-data-handling.md b/doc/ai-data-handling.md new file mode 100644 index 0000000..df67bb8 --- /dev/null +++ b/doc/ai-data-handling.md @@ -0,0 +1,213 @@ +# AI Data-Handling + +This is the highest-priority page of OSBR's AI operating model, and the one the +[Quality Gate](/quality-gate)'s **Security** lens holds work to whenever a project +uses AI. The [AI Usage Guideline](/ai-usage-guideline) is the operating-model +overview; this page is its load-bearing core. AI-assisted work is OSBR's **default** +way of building — humans and AI agents together, not an exception a project opts +into — and that default is only safe when one thing is settled before the first +commit: **a written, client-agreed, version-controlled record of which data classes +may enter which AI providers, under what configuration.** No developer, human or +AI, should ever have to guess whether a given file, secret, or dataset is allowed +into a given model. The record answers that in advance, for everyone. It builds on +the [Security Policy](/security-policy) and the [Data Protection](/data-protection) +standard, and — as everywhere in the handbook — deviations must be deliberate and +justified in the project's design notes. + +This is where OSBR's values become a boundary the machine can see. **Be Kind**: +we protect a client's data — their code, their users' personal data, their secrets +— as if it were our own, so it never leaks into a service that could learn from it +or retain it. **Be Strong**: we do not rely on good intentions or a developer's +memory in the moment; we draw the boundary of what the AI may see *in writing, +agreed with the client, before the first commit*, and hold generation, review, and +audit to that same standard. **Be Nice**: the record is documentation first — one +document a new teammate opens to learn, without asking anyone, exactly what is +permitted where. Cooperation between humans and AI is only trustworthy when that +boundary is drawn deliberately and written down. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as across the handbook. **MUST** / + **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a strong default + overridable only with a documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry instrument — a Data + Processing Agreement, a data-classification scheme, a risk framework — the + instrument is named inline and cited under [References](#references). We adopt + the *criteria* of large-enterprise practice and right-size them for an SME; we do + not adopt the headcount or bureaucracy behind their reference setups. + +[[TOC]] + +## 1. Goal + +The goal is that on any OSBR project a developer — human or AI agent — can open +**one document** and see, without asking anyone: what data classes exist, which are +permitted into which providers, under which data-handling configuration +(no-training / minimal-retention / region-pinned), and which are prohibited +outright. Concretely: + +- Make the boundary **legible**: the answer to "may this data enter this model?" + exists in writing before anyone needs it, not as a judgment call made under + deadline. +- Make it **enforceable, not decorative**: the record is expressed against the same + policy plugins that generation, review, and audit already run under, so it is + machinery held to from the first commit — not a paragraph in a proposal nobody + reads again. +- Make it **accountable to the client**: the record is agreed with them in writing + and is the standing authorization for every AI provider that touches their data. + +This is standard large-enterprise practice at OSBR's scale. A **Data Processing +Agreement (DPA)** governs a processor's use of a controller's data +([GDPR Art. 28](https://gdpr-info.eu/art-28-gdpr/)); a **data-classification +scheme** decides how each class may be handled; and an **AI Acceptable Use Policy** +binds those two to the specific AI services in play. We adopt the same three +instruments and right-size them into a single record. + +## 2. Responsibility + +**Before a project starts, the AI Data-Handling Record MUST exist, be agreed with +the client in writing, and be consultable by every developer on the project.** It +is a precondition of the first commit — the same gate class as the security +onboarding in the [Security Policy](/security-policy). A project that has not +produced this record is not ready to write code. + +The record is OSBR's project-level **AI Acceptable Use Policy**, and it sits inside +(or is referenced by) the client **DPA**. It MUST state, per project: + +1. **Data-classification tiers** — every data class the project touches, sorted + into named tiers (e.g. *Public → Internal → Confidential → Restricted*, the + conventional 3–4 tier model behind ISO/IEC 27001 information classification). + Personal data, secrets/credentials, and regulated data are called out + explicitly. +2. **Permitted providers** — which AI services are approved, named exactly, and for + which tiers. +3. **Required configuration per provider** — the data-handling posture each + approved provider MUST run under: **no-training / opt-out**, **zero or minimal + retention**, **model/tenant isolation**, and **data residency / region**. Each + posture MUST cite the provider's *current* enterprise data-handling terms as the + authoritative source, not memory. +4. **Prohibited data classes** — the classes that MUST NOT enter any AI service + under any configuration (secrets/credentials, and any Restricted-tier or + regulated data the client has not cleared). + +**Accountability follows the GDPR processor model:** the client is the data +controller, OSBR is a processor, and any AI provider is a **sub-processor**. Under +[GDPR Art. 28](https://gdpr-info.eu/art-28-gdpr/) a processor MUST NOT engage a +sub-processor without the controller's authorization and MUST bind it by contract +to equivalent protection. The AI Data-Handling Record is that authorization, made +explicit. Where client data falls under OSBR's home regime — Malaysia's Personal Data +Protection Act 2010 (PDPA) — or another regime (e.g. Japan's APPI for cross-border +transfer and third-party handling, or the EU's GDPR), the record MUST satisfy it +too — see [Data Protection](/data-protection). + +Every developer is responsible for **staying inside the record**. If a task seems +to need data or a provider the record does not permit, the answer is never to +proceed and work around it — it is to stop and get the record **amended and +re-agreed** with the client first. + +## 3. Practices + +### 3-1. The record itself — committed, version-controlled, plugin-expressed + +- The AI Data-Handling Record **MUST** be committed to the project repository (or + linked from it) and version-controlled, so it is consultable by all developers + and its change history is auditable — the same reasoning as tracking any other + standard the [Development Guide](/development-guide) treats as part of the repo. +- It **MUST** be agreed with the client in writing before the first commit, and + **MUST** be re-agreed before any change to permitted providers, configuration, or + data classes takes effect. +- It **MUST** be expressed against the **policy plugins**, so AI generation, + automated review, and audit all evaluate against the same standard from commit + one — not a separate document that quietly drifts from what the tooling enforces. +- It **SHOULD** map each data class to its tier and each tier to its permitted + providers in a single table a developer can read at a glance. + +### 3-2. Data classification — every class tiered before it touches a model + +- Every data class the project handles **MUST** be assigned a tier before it is used + with any AI service. Unclassified data defaults to the **most restrictive** tier + and MUST NOT enter an AI service until classified. +- Secrets and credentials (API keys, tokens, connection strings, private keys) + **MUST** be treated as prohibited from AI services, consistent with the + [Security Policy](/security-policy)'s ban on committing credentials. This is + absolute: no configuration makes a secret admissible. +- Personal data and any client-designated regulated data **MUST NOT** enter an AI + service unless the record explicitly permits it under a named no-training, + zero-retention configuration **and** the client has agreed in writing. The + handling of such data also answers to [Data Protection](/data-protection). + +### 3-3. Provider configuration — no-training, minimal-retention, region-pinned + +- Approved providers **MUST** run under enterprise/commercial terms that + contractually exclude training on client data and offer retention controls. The + record **MUST** cite the provider's own *current* data-handling documentation as + the source of truth — verified against the provider, never asserted from memory. + For example: + - **Anthropic** — [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms) + (inputs/outputs from commercial use are not used to train models) and + [zero-data-retention](https://privacy.anthropic.com/) options. + - **OpenAI** — [Enterprise privacy / API data usage](https://openai.com/enterprise-privacy/) + (API data not used to train by default; retention controls and ZDR available). + - **Google Cloud Vertex AI** — [data governance](https://cloud.google.com/vertex-ai/generative-ai/docs/data-governance) + (customer data not used to train foundation models; in-region processing). + - **AWS Bedrock** — [data protection](https://docs.aws.amazon.com/bedrock/latest/userguide/data-protection.html) + (prompts/completions not used to train base models; stays within the account + and region). +- Where a provider offers it, developers **SHOULD** prefer the isolated-tenant, + region-pinned, zero-retention configuration for anything above the Internal tier. +- Consumer or free-tier AI products whose terms permit training on user input + **MUST NOT** be used with any non-Public client data. + +### 3-4. Vendor / sub-processor assurance + +- An AI provider **SHOULD** be selected in part on independent assurance of its + controls — an **ISO/IEC 27001** certificate and/or a **SOC 2 Type II** report + (AICPA Trust Services Criteria) covering the service. Prefer providers that also + hold **ISO/IEC 27701** (privacy) or **ISO/IEC 42001** (AI management system) where + available. +- The record **MUST** name each AI provider as a **sub-processor** and keep the list + current. Adding a sub-processor requires client re-agreement (GDPR Art. 28) — the + same amend-and-re-agree gate as §2, not a silent addition. + +### 3-5. Governance — mapped risk, reviewed cadence + +- Project AI use **SHOULD** be governed against the [NIST AI Risk Management + Framework (AI 100-1)](https://www.nist.gov/itl/ai-risk-management-framework) — its + *Map / Measure / Manage / Govern* functions — so risks are identified and owned, + not discovered after a leak. +- The record **SHOULD** be reviewed at the same cadence as the access review in the + [Security Policy](/security-policy), and **MUST** be revisited whenever a provider + changes its data-handling terms — the citations in §3-3 are only as good as their + currency. + +## References + +**Data protection & processor obligations** + +- GDPR Article 28 (processor / sub-processor obligations) — + +**AI governance & management standards** + +- NIST AI Risk Management Framework (AI 100-1) — +- ISO/IEC 42001 (AI management system) — + +**Vendor / sub-processor assurance** + +- ISO/IEC 27001 (information security management) — +- ISO/IEC 27701 (privacy information management) — +- AICPA SOC 2 (Trust Services Criteria) — + +**Provider enterprise data-handling terms** + +- Anthropic Commercial Terms of Service — +- OpenAI Enterprise privacy — +- Google Cloud Vertex AI data governance — +- AWS Bedrock data protection — + +**Related OSBR standards** + +- [AI Usage Guideline](/ai-usage-guideline) — the AI operating-model overview this page anchors. +- [Quality Gate](/quality-gate) — the Security lens this standard serves. +- [Security Policy](/security-policy) — credential handling, onboarding gate, access-review cadence. +- [Data Protection](/data-protection) — personal and regulated data, cross-border handling. +- [Development Guide](/development-guide) — how project standards live in the repo. diff --git a/doc/ai-usage-guideline.md b/doc/ai-usage-guideline.md new file mode 100644 index 0000000..a9ca70b --- /dev/null +++ b/doc/ai-usage-guideline.md @@ -0,0 +1,52 @@ +# AI Usage Guideline + +This is how OSBR works with AI: one engineer, with AI beside them, carrying a +whole piece of work — and the standards that keep that way of working safe, +resilient, and honest. This page is the overview; each part below has a full +standard behind it. + +**In depth:** [AI Data-Handling](/ai-data-handling) · [Multiple AI +Agents](/multiple-ai-agents) · [Overnight AI Operation](/overnight-ai) · [Weekly +AI Quota](/weekly-ai-quota) · [Voice Input](/voice-input) · [Meeting +Recording](/meeting-recording) · [Policies as +Plugins](/policies-as-plugins) · [Building for AI Users](/building-for-ai-users) + +[[TOC]] + +## One engineer owns the whole + +**One engineer owns the whole of a piece of work — and AI is what puts the whole within one person's reach.** The same person shapes the interaction, writes the code, stands up the deploy, and proves it with a test; there is no wall to toss the hard part over. When a defect lives in the seam between two layers — a design assumption that only bites at runtime, a gap only someone who holds both sides would notice — the person who found it closes it, because there is no one to hand it to. This is our **Be Strong** value made structural: own the whole seam. It holds at today's quality bar, not just today's speed, because AI supplies breadth on demand — the ops step, the missing test case, the layer you are shallow in — that used to need a second and third specialist. The AI is the multiplier; the human stays accountable for the slice and reviews the AI's cross-layer work the way they would a specialist's. + +## AI is our default, bounded in writing + +**AI-assisted work is our default, and its boundary is drawn in writing before the first commit.** Building with AI is how we work, not an exception a project opts into — but a default is only safe when everyone knows what may be shown to which model. Every project carries a client-agreed, version-controlled record of its data: which classes are permitted into which providers, under what configuration, and which are prohibited outright. Approved providers run under terms that exclude training on client data and hold retention to zero or near it, pinned to a known region, and the record cites each provider's own current terms as the source of truth rather than our memory of them. Secrets, credentials, and any regulated data the client has not cleared do not enter an AI service at all. If a task seems to need data or a provider the record does not cover, we stop and get the record amended — we do not proceed and hope. + +## We depend on no single provider + +**We depend on no single provider.** Every developer keeps at least two coding agents warm — authenticated, permissioned, pointed at the same repository — and actually exercised, because a backup nobody has run in a month is a cold spare that fails at the worst moment. Tickets, commands, and procedures are written to the task and the repository, not to one vendor's quirks, so either agent can pick them up with no rewrite. When one provider is down, throttled, or quietly regresses after a model update, the work moves to the other and continues. Because both agents are held to the same policy plugins, those plugins must stay at the same version across every agent, bumped together — divergent versions mean the same code passes on one agent and fails on the other. + +## The day is for judgement, the night is for execution + +**The day is for judgement; the night is for execution.** Daytime — when a human can still be asked — is spent turning fuzzy intentions into well-formed, independently runnable tickets, pre-answering the forks an agent would otherwise guess at, and setting the guardrails: what it may touch, what it must never touch, where it must stop and leave a question. The night is when agents run those tickets unattended on an isolated, reversible surface. Nothing they produce is trusted until a human has looked: every day opens by reviewing the night's work before merging or building on any of it, and by working the queue of judgement calls the agents parked rather than guessed. A ticket that produced a bad night earns a clearer specification, not a patched output. The weekly AI capacity is prepaid and perishable — unused at reset is spent and wasted — so we keep a standing backlog of genuinely valuable, non-urgent work (refactoring, security passes, research, tests and docs) to point spare capacity at, usually overnight. But utilisation is a diagnostic, never a target: we never run the meter up on valueless processing to feel productive, and when no worthwhile work remains we let the capacity lapse. Idle beats busywork. + +## We speak to the AI as readily as we type + +**We speak to the AI as readily as we type to it.** We think faster than we type, and voice closes that gap: we lean on dictation to get design intent, first-pass ideas, and the opening brief to an agent out of our heads and into the work, and reach for the keyboard where precision rules — short corrections, code, exact syntax, tightening our own draft. This is encouraged, never required; the room, the moment, and a person's own comfort decide, and we judge the output, never the input method. + +## Our policies reach the AI too + +**Our policies reach the AI, not only the humans.** Each engineering policy also ships as a plugin the agents load, so the standard is in the model's context at the moment of generation, not discovered in review after the violation is already written. Because the human page and the agent plugins are renderings of one policy, they move together: a change updates the handbook article and every agent plugin in the same pull request, or it does not merge. And a plugin that raised no objection is not proof of compliance — silence is unknown, not clean. Compliance is still earned the way it always is, through executable checks and human review; the plugin shifts the standard left into generation, it does not replace the gate on the right. + +## We build for AI users too + +**We build for AI users too, not only with AI.** The software we ship will be driven by both people and autonomous agents, so we treat an agent as a first-class user from the planning stage: capabilities an agent should use are exposed through machine-consumable, typed, least-privilege interfaces — a documented API or an MCP/A2A surface — never only a human GUI. And every autonomous flow keeps a human able to observe what it is doing, interpret why, interrupt it cleanly without corrupting state, and take over to finish or reverse the task. There is no level of autonomy at which a human loses that path, and the higher the stakes, the closer we sit to the human confirming each step. + +## In short + +- We **MUST** hold a client-agreed, version-controlled record of permitted data classes, providers, and configurations before the first commit, and stay inside it — amending and re-agreeing it rather than working around it. +- We **MUST NOT** let secrets, credentials, or uncleared regulated data enter any AI service, and **MUST** run approved providers under no-training, minimal-retention, region-pinned terms cited from the provider's own documentation. +- We **MUST** keep at least two coding agents warm and exercised, write work agent-agnostically, and keep policy plugins at the same version across all of them. +- We **MUST** review every unattended run before merging, deploying, or building on it, and work the agent's parked judgement calls rather than accept a blank cheque. +- We **MUST** update the handbook article and every agent plugin for a policy in the same pull request, and **MUST NOT** read a silent plugin as a pass — compliance is still established by checks and human review. +- We **MUST** build the observe / interpret / interrupt / take-over path into every autonomous flow, and expose agent-facing capabilities through typed, least-privilege, machine-consumable interfaces. +- We **SHOULD** spend prepaid spare capacity on real backlog value — refactoring, security, research — never on busywork to run the meter up, and **SHOULD** prefer voice for conveying intent and the keyboard for precise correction. diff --git a/doc/api-design.md b/doc/api-design.md new file mode 100644 index 0000000..7e72fb6 --- /dev/null +++ b/doc/api-design.md @@ -0,0 +1,313 @@ +# API Design + +This is the standard the [Quality Gate](/quality-gate)'s **Sustainability** +lens holds HTTP API work to. Where the [Development Guide](/development-guide) +covers how a change is proposed and reviewed, and the [Architecture +Standards](/architecture-standards) cover how a service is shaped internally, +this page covers the one thing consumers actually depend on: the **contract** +we expose over the wire. An API is the longest-lived promise most services +make — sustaining it means designing it once, consistently, so it can grow for +years without every addition becoming a new dialect. Deviations are allowed, +but — as everywhere in the handbook — they must be deliberate and justified in +the project's design notes. + +This standard leans on the public HTTP standards and the enterprise API style +guides that already exist — [Fielding's REST +constraints](https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm), +[HTTP Semantics (RFC 9110)](https://www.rfc-editor.org/rfc/rfc9110), the +[Microsoft REST API Guidelines](https://github.com/microsoft/api-guidelines), +the [Google API Design Guide (AIPs)](https://google.aip.dev/), and the [Zalando +RESTful API Guidelines](https://opensource.zalando.com/restful-api-guidelines/) +— and **right-sizes them for an SME.** We adopt their *criteria* without +adopting their scale. + +An API is where OSBR's values become a public interface. **Be Kind**: an API is +a promise you make to everyone downstream — client developers, integration +partners, your own future services, and the AI agents that call it without a +human reading the docs first — so a predictable, uniform surface that lets a +consumer reason about an endpoint by analogy with one it already knows is a +kindness owed to all of them. **Be Strong**: a uniform contract is the +load-bearing structure that lets the system grow without every addition +becoming a special case, and it is designed to fail safely under the retries +and partial outages real networks produce. **Be Nice**: the contract and its +definition are documentation a teammate — human or AI — reads to learn what the +system promises, so both must read plainly and stay honest. + +## How to read this policy + +* **Requirement levels** follow RFC 2119. **MUST** / **MUST NOT** are absolute. + **SHOULD** / **SHOULD NOT** state a strong default overridable only with a + documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry standard or style guide, + it is named inline and cited under [References](#references). We adopt the + criteria of large-scale guides and right-size them; we do not adopt the + headcount or platform behind their reference setups. + +[[TOC]] + +## 1. Goal + +Every business concept is exposed as a **resource**, and the *same* concept is +always found at the *same* URL, reached with the *same* method, at the *same* +granularity, and returned in the *same* representation — everywhere in the API. +A consumer who has learned one part of the API can predict the rest. + +An inconsistent API forces every consumer to special-case every corner, and +that is where 3am incidents come from. This matters more, not less, as AI +agents become callers: an agent cannot ask a colleague "oh, that one endpoint +is weird" — it infers behaviour from patterns. When `GET /orders/{id}` and +`GET /invoices/{id}` behave identically in shape, status codes, pagination, and +errors, an agent that learned one can safely drive the other. Consistent with +OSBR building for AI users, uniformity here is not just developer ergonomics — +it is machine-readability, and human⇄AI cooperation depends on it. + +## 2. Responsibility + +- **The API author** owns the contract. Before adding or changing an endpoint, + you are responsible for checking how neighbouring endpoints already behave and + carrying those conventions over (§3-3). You do not get to invent a local + convention. +- **The reviewer** rejects endpoints that break established conventions without + a recorded reason, exactly as they would reject a security or schema + violation. This is the AI code review the [Quality + Gate](/quality-gate) requires. +- **The API definition** — the [OpenAPI](https://spec.openapis.org/) document + (§3-9) — is the source of truth. Anything not captured there does not exist as + far as consumers are concerned. Any RPC-style deviation, non-obvious read + semantics, or idempotency guarantee MUST be recorded there. +- **AI agents** author and review API changes here as first-class contributors, + held to exactly this bar; the human who merges an agent's change owns it. + +## 3. Practices + +### 3-1. Model business concepts as resources + +- **MUST** model the API around **nouns (resources)**, not verbs (actions). A + resource is a business concept — `order`, `invoice`, `member` — identified by + a stable URL. +- **MUST** use plural collection names and address individual members by + identifier: a collection at `/orders`, a member at `/orders/{id}`. This mirrors + the ubiquitous language one-to-one, the same singular-entity / plural-collection + discipline we hold the domain model to. +- **SHOULD** express relationships as sub-resources when a child only exists in + the context of a parent — `/orders/{id}/line-items` — and as top-level + resources with a reference field when the child has independent identity. +- **MUST NOT** encode actions in the path as a default (`/getOrder`, + `/createOrder`, `/orders/{id}/doCancel`). The method carries the verb; the path + carries the noun. (Exception: §3-5.) + +This is levels 1 and 2 of the **[Richardson Maturity +Model](https://martinfowler.com/articles/richardsonMaturityModel.html)** +(Leonard Richardson; popularised by Martin Fowler): level 1 introduces +resources, level 2 uses HTTP verbs and status codes correctly. **Level 2 is the +OSBR baseline.** Level 3 (hypermedia / HATEOAS) is encouraged where it earns its +keep but is not mandated. + +### 3-2. Same concept, same shape + +The core rule. For any given business concept, these MUST be identical +everywhere it appears: + +- **Path** — the concept lives at one canonical URL. `member` is `/members/{id}`; + it is not `/members/{id}` in one service and `/users/{id}` in another for the + same thing. +- **Method** — the same kind of operation uses the same verb across all resources + (see §3-4). +- **Granularity** — if `order` is addressable as a whole, every comparable concept + is addressable as a whole; you do not expose one concept field-by-field and + another only as a monolithic blob without a reason. +- **Type / representation** — a concept serialises to the same JSON shape, with + the same field names, casing, date format, money format, and null conventions, + in every response that embeds it. An `order` embedded in a `GET /orders/{id}` + looks like an `order` embedded in a `GET /customers/{id}/orders`. + +Naming and representation are **house decisions, not per-endpoint choices.** +Field casing (`snake_case` vs `camelCase`), timestamp format (RFC 3339 / ISO +8601, UTC), money (integer minor units or decimal string — never a binary +float), enum spelling, and null-vs-absent semantics are decided **once per API** +and never re-litigated per endpoint. Pick the convention your primary style +guide dictates ([Google AIP-140/142](https://google.aip.dev/) and +[Zalando](https://opensource.zalando.com/restful-api-guidelines/) both give +concrete rulings) and hold every endpoint to it. + +### 3-3. Confirm neighbouring conventions before adding an endpoint + +- **MUST**, before adding an endpoint, read the nearest existing endpoints in the + same API and carry their conventions over: pagination style, filtering syntax, + error shape, authentication (per the [Application + Security](/application-security) standard), status-code choices, naming, and + versioning. The default answer to "how should this behave?" is "the way its + neighbours already behave." +- **MUST** record any *deliberate* departure from a neighbouring convention in the + API definition, with the reason. An undocumented departure is a bug. +- **SHOULD** treat the first endpoint of a new kind as setting precedent — design + it knowing everything after it will copy it. + +An endpoint is not a fresh design surface; it is another instance of an +already-agreed pattern. Consistency is a property you protect on every addition, +not one you can add back later. + +### 3-4. Use HTTP semantics as defined + +Follow **[HTTP Semantics (RFC 9110)](https://www.rfc-editor.org/rfc/rfc9110)** +and the [Fielding +constraints](https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) +(client–server, **stateless**, cacheable, uniform interface, layered system) as +written — do not invent local meanings for standard machinery. + +- **Methods MUST carry their standard semantics:** + - `GET` — read, **safe** (no side effects) and **idempotent**. Never mutate + state on a `GET`. + - `PUT` — full replace, **idempotent**. + - `PATCH` — partial update. + - `DELETE` — remove, **idempotent**. + - `POST` — create / non-idempotent submission (see §3-6 for making it safe to + retry). +- **Status codes MUST be used for their defined meaning** — `200/201/204` for + success, `400/401/403/404/409/422` for client faults, `5xx` for server faults. + Do not return `200` with an error body. +- **MUST** keep the server **stateless**: every request carries what it needs to + be understood; no server-side session affinity. This is what lets a service + scale horizontally and to zero, per the [Architecture + Standards](/architecture-standards). +- **SHOULD** respect caching and concurrency headers where they apply — `ETag` / + `If-None-Match` for conditional reads, `If-Match` for optimistic concurrency on + writes. + +### 3-5. RPC-style endpoints only where a resource form distorts meaning + +Some operations are genuinely verbs — `POST /orders/{id}:cancel`, +`POST /payments/{id}:refund`, `POST /reports:export`. Forcing these into pure +resource CRUD (e.g. inventing a `cancellation` resource nobody in the business +talks about) can distort the domain more than it clarifies it. A **custom +method** is then acceptable. + +- **SHOULD** prefer a resource + state field first: modelling cancellation as + `PATCH /orders/{id}` with `{ "status": "cancelled" }` is often the honest + model. Use a custom method only when the action is not well-described as a state + field — because it has side effects beyond the resource, or is a process rather + than a state. +- **MUST**, when using a custom method, follow the house convention for it + consistently. The [Google AIP-136 custom-methods](https://google.aip.dev/136) + form `POST /resource/{id}:verb` (colon-delimited verb) is a good default; + whatever you pick, every custom method in the API uses the same shape. +- **MUST** record, in the API definition for that endpoint: whether it **reads or + changes state** (and what state), its **idempotency** (can the caller safely + retry? — §3-6), and any side effects a consumer cannot infer from the resource + alone. +- **MUST NOT** reach for a custom method to avoid learning the correct HTTP verb. + "It's easier to POST everything" is not a distortion of meaning; it is + Richardson level 0 ("the swamp of POX") and is not permitted. + +The bar is "a resource form distorts meaning" — **not** "a resource form is +slightly more typing." Custom methods are the documented exception, not an +escape hatch. Every one you add is a thing consumers and agents cannot predict +by analogy, so each MUST earn its place in writing. + +### 3-6. Idempotency and safe retries + +Networks retry. An API that double-charges on a retry is not being kind to +anyone. + +- **MUST** make `GET`, `PUT`, `DELETE` naturally idempotent (§3-4). +- **SHOULD**, for non-idempotent `POST` and custom methods that create or move + money/state, accept an **idempotency key** — a client-supplied unique token + (conventionally the `Idempotency-Key` request header, per the [IETF + Idempotency-Key header + draft](https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/) + and [Stripe's idempotency + pattern](https://docs.stripe.com/api/idempotent_requests)) — so a retried + request returns the original result instead of performing the operation twice. +- **MUST** document, per write endpoint, whether it is idempotent and whether it + honours an idempotency key. + +### 3-7. Collections: filtering, sorting, pagination + +- **MUST** paginate any collection that can grow unbounded. **SHOULD** default to + **cursor (keyset) pagination** — an opaque `cursor` / `next` token — over + offset/limit, for stable results under concurrent writes and no deep-offset + performance cliff. ([Zalando](https://opensource.zalando.com/restful-api-guidelines/) + and [Google AIP-158](https://google.aip.dev/158) both codify this.) +- **MUST** keep the pagination, filtering, and sorting **syntax identical across + every collection** in the API (§3-2). One collection using `?sort=` and another + using `?order_by=` is exactly the inconsistency this policy exists to prevent. +- **SHOULD** return pagination metadata (next cursor, and total only if it is + cheap to compute) in a consistent envelope across all list responses. + +### 3-8. Errors: one machine-readable shape + +- **MUST** return errors as **[Problem Details for HTTP APIs (RFC + 9457)](https://www.rfc-editor.org/rfc/rfc9457)** — the + `application/problem+json` body with `type`, `title`, `status`, `detail`, + `instance` — used **uniformly** across the whole API. (RFC 9457 obsoletes RFC + 7807; use 9457.) +- **MUST NOT** invent a bespoke error shape per service. One error contract, + everywhere, is what lets a consumer — and an agent — handle failures + programmatically instead of string-matching prose. +- **SHOULD** include a stable, documented machine-readable error code (in `type` + or an extension member) so consumers branch on a code, not on human-readable + `detail` text. Error bodies MUST NOT leak internal detail (stack traces, + queries, credentials) — see the [Application + Security](/application-security) standard. + +### 3-9. The OpenAPI contract + +- **MUST** describe every API with an **[OpenAPI + 3.x](https://spec.openapis.org/)** document, kept in the repo and reviewed with + the code. The contract is not documentation-after-the-fact; it is the + specification the implementation must satisfy. +- **MUST** capture in it: every resource, method, status code, the shared + representations (as reusable `components/schemas`), the error shape (§3-8), + pagination, and any §3-5 custom-method deviation with its read/write/idempotency + notes. +- **SHOULD** reuse shared schemas by reference (`$ref`) rather than re-declaring a + concept's shape per endpoint — the schema is where "same concept, same type" + (§3-2) is mechanically enforced. +- **SHOULD**, where a project adopts a full body convention, follow + **[JSON:API](https://jsonapi.org/)** or a documented house profile — but + consistency within one API always outranks conformance to any external profile. + +### 3-10. Versioning + +- **MUST** version the API and treat the contract as a published promise governed + by **[Semantic Versioning](https://semver.org/)**: breaking changes require a + new major version; additive, backward-compatible changes do not. +- **SHOULD** prefer **additive, non-breaking evolution** — add fields and + endpoints; do not repurpose or remove existing ones — so consumers rarely have + to move. This is the same expand / migrate / contract discipline we apply to + schema evolution. +- **MUST NOT** make a breaking change to a concept's shape, path, or semantics + under the same version. Changing what an existing field means silently is the + most consumer-hostile thing an API can do. +- **SHOULD** carry the versioning *mechanism* (URL prefix `/v1`, or a header) + consistently across the whole API — never mix styles. + +## References + +**REST & HTTP foundations** + +- Roy Fielding — Architectural Styles and the Design of Network-based Software Architectures (REST constraints) — +- Richardson Maturity Model (Martin Fowler) — +- RFC 9110 — HTTP Semantics — +- RFC 9457 — Problem Details for HTTP APIs — + +**Enterprise API style guides** + +- Microsoft REST API Guidelines — +- Google API Design Guide / AIPs — +- Zalando RESTful API Guidelines — + +**Contract, format & conventions** + +- OpenAPI Specification 3.x — +- JSON:API — +- Semantic Versioning — +- IETF draft — The Idempotency-Key HTTP Header Field — +- Stripe — Idempotent requests — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Sustainability lens this standard serves. +- [Architecture Standards](/architecture-standards) — statelessness, scaling, and service shape. +- [Application Security](/application-security) — authentication conventions and safe error surfaces. +- [Development Guide](/development-guide) — how an API change is proposed and reviewed. diff --git a/doc/application-security.md b/doc/application-security.md new file mode 100644 index 0000000..d5e40fe --- /dev/null +++ b/doc/application-security.md @@ -0,0 +1,536 @@ +# Application Security + +This is the standard the [Quality Gate](/quality-gate)'s **Security** lens holds +work to for the running application. It is the application-layer complement to +the [Security Policy](/security-policy): where that policy governs *device and +account conduct* — how developers work, how they hold credentials — this page +starts at the first line of request-handling code and follows it through to the +logged response. It answers two questions together: *is each control correct?* +and *what still holds when a control fails?* Deviations are allowed, but — as +everywhere in the handbook — they must be deliberate and justified in the +project's design notes. + +Like the rest of OSBR's engineering guidance, this leans on standards that +already exist rather than inventing a house security model. **OSBR adopts the +[OWASP Application Security Verification Standard (ASVS)](https://owasp.org/www-project-application-security-verification-standard/) +as its application-layer baseline** (§1), and grounds the practices below in the +[OWASP Top 10](https://owasp.org/Top10/), the +[OWASP Proactive Controls](https://owasp.org/www-project-proactive-controls/), +the [Cheat Sheet Series](https://cheatsheetseries.owasp.org/), the +[CWE Top 25](https://cwe.mitre.org/top25/), and +[NIST SSDF (SP 800-218)](https://csrc.nist.gov/pubs/sp/800/218/final). We adopt +their *criteria* and right-size them for an SME — we do not adopt the headcount +or infrastructure behind their reference setups. + +Application security is where OSBR's values hold a wall. **Be Nice** to the +operator paged at 3am: an app that fails closed, leaks nothing, and logs what +happened is a far kinder thing to debug than one that fails open and silently. +**Be Kind** to the users whose data lives behind that boundary and to the next +engineer — human or AI — who extends the code: secure-by-default helpers are +defences they inherit rather than re-derive, and no user ever agreed to bet +their data on our getting a single regex or token check exactly right. **Be +Strong** on the boundary, and hold *more than one*: an attacker only has to win +once, so we plan to lose a layer and keep the breach contained rather than +catastrophic. AI agents increasingly both call these applications and write +their request-handling code — and neither can be trusted to infer an unwritten +convention, which is why the defence has to live in the framework and the shared +helpers, not in one developer's head. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the rest of the handbook. + **MUST** / **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a + strong default overridable only with a documented reason. **MAY** marks a free + choice. +* **Named practice.** Where a rule adopts an industry practice or a specific + ASVS/OWASP/NIST control, it is named inline and cited under + [References](#references). ASVS is *how we check we did them* — every practice + below maps to an ASVS requirement. + +[[TOC]] + +## 1. Goal + +Every OSBR application is **secure by default at runtime, and every important +asset is reachable only through multiple independent boundaries that each start +closed.** No single control — no one firewall rule, auth check, input filter, or +output encoder — is the only thing between an attacker and the data; when one +layer fails, the next, on a different lineage, still holds. Concretely, an OSBR +application meets this policy when: + +- Its application-layer security is **verifiable against OWASP ASVS** at the + level its data sensitivity demands (§1), not asserted by feeling. +- **Authentication and sessions** are strong, phishing-resistant, and correctly + invalidated (§3-6, §3-7). +- **All input is validated at the trust boundary and all output is encoded for + its sink** (§3-8, §3-9), closing the injection classes — SQLi, XSS, CSRF, + SSRF, command/template injection (§3-10). +- **Access control is enforced server-side, deny-by-default, on every request** + (§3-11). +- **Secrets are never in code or logs** and are read from a secret store at + runtime (§3-12). +- **Defaults are safe, errors leak nothing, and security events are logged** + (§3-13, §3-14, §3-15). +- Each asset sits behind **two or more independent boundaries** that **start + closed** (§3-3, §3-4), and **every deliberate relaxation is recorded** with its + scope and duration (§3-16). + +This is the **Swiss-cheese model**: each layer has holes, but if the layers are +independent the holes rarely line up, and a threat only passes when a hole in +*every* slice aligns. Our job is to keep the slices independent so the holes +stay misaligned — the defence-in-depth arrangement NIST and the NSA have +advocated for decades precisely because no single safeguard is ever perfect. + +## 2. Responsibility + +- **The developer** who writes a request handler owns its application-layer + security. Before adding an endpoint you route it through the shared auth, + validation, and access-control machinery — you do not get to invent a local + security convention any more than a local API convention — and you keep + defaults restrictive and escaping on, adding the redundant check even when it + feels redundant, because that redundancy *is* the policy. +- **The reviewer** — the AI-automated review gate and any human reviewer — + rejects changes that regress an ASVS control, add an unvalidated input sink, + introduce an un-parameterised query, or quietly remove a layer, the same way + they reject a broken build. Approving a relaxation is an act of **separation of + duties**: the person who wants a hole is not the only one who decides it opens. +- **The project lead / architect** owns the **target ASVS level** (§1) and the + **layering** — that each important asset has more than one independent boundary + and that the layers do not secretly collapse onto a shared dependency (§3-4). + They approve and record relaxations. +- **The design note** is the source of truth for the chosen ASVS level, any + control marked *not applicable*, any deliberate deviation, and any relaxation + accepted as risk. An undocumented deviation is a bug. + +This page governs the **application**. The [Security Policy](/security-policy) +still governs the **developer and the account**; the two are read together. + +## 3. Practices + +### 3-1. Verify against OWASP ASVS — choose a level + +ASVS is a catalogue of testable requirements, organised into three ascending +assurance levels. We use it as the yardstick and the audit checklist. + +- **Level 1 (L1)** — the baseline every OSBR application **MUST** meet. + Low-cost, fully black-box-testable controls; the floor, not the goal. +- **Level 2 (L2)** — **MUST** for any application handling **personal data, + authentication, money, or client-confidential data** (which, per the Security + Policy's protected assets, is most OSBR work). L2 is the effective OSBR + default. +- **Level 3 (L3)** — for the **highest-assurance** systems (payments, health, + high-value admin planes). Targeted where a client contract or the blast radius + demands it. + +Rules: + +- Every project **MUST** record its **target ASVS level** in the design notes at + project start, chosen by data sensitivity, not convenience. +- An ASVS requirement at the chosen level is a **MUST** for that project. A + requirement judged *not applicable* **MUST** be recorded as N/A with a one-line + rationale — never silently skipped. +- A project **SHOULD** run an ASVS-structured audit before a release that changes + the security surface, recording which levels each chapter meets. (The + `asvs-audit` tooling exists for exactly this; security-relevant behaviour is + also tested per the [Testing Standards](/testing-standards).) +- Adopt controls **above** the chosen level opportunistically when they are cheap + — levels are a floor, not a ceiling. + +Use the **OWASP Top 10** and the **CWE Top 25** to understand *what goes wrong +and why*; use **ASVS** to verify *you closed it*; use the **Cheat Sheet Series** +for the *how-to* on each control. + +### 3-2. Build on the OWASP Proactive Controls + +The **Proactive Controls** are the positive, build-time counterpart to the Top +10's list of failures — the techniques applied by default. They frame the rest +of this section: define security requirements as ASVS levels (§1); **leverage +vetted frameworks and libraries** rather than hand-rolling crypto, session +handling, or output encoding (hand-rolled security is the single most common +source of the bugs this page prevents); validate all input (§3-8); encode and +escape output (§3-9); secure database access (§3-10); implement digital identity +(§3-6, §3-7); enforce access controls (§3-11); protect data everywhere; and +handle all errors and log security events (§3-14, §3-15). This maps onto **NIST +SSDF** practice group **PW (Produce Well-Secured Software)** — standardised, +vetted components and secure-by-default settings over bespoke security code — +with our CI gate and AI-automated review acting as the SSDF **PW.7/PW.8** +review-and-test practices. + +### 3-3. Start closed: restrictive defaults, fail-secure + +Every boundary **MUST** default to the safe state and open only by explicit, +recorded decision. A protection you must remember to enable is off in every +place someone forgot; a protection on by default is off only where someone +deliberately — and, under §3-16, *visibly* — turned it off. Defaults decide the +security of the code nobody reviewed closely. + +- Access **MUST** be **deny-by-default** — allow-lists, not deny-lists. A new + route, bucket, port, or table starts unreachable and is opened deliberately + (NIST SP 800-53 **AC-3 / SC-7**; the OWASP secure-defaults position). The + server-side enforcement of this is §3-11. +- Controls **MUST** be **fail-secure / fail-closed**: when a check errors, times + out, or a dependency is unavailable, the system **denies** — it never falls + through to allow. An auth service that is down must lock the door, not prop it + open (Saltzer & Schroeder, *fail-safe defaults*). +- **Output escaping / encoding MUST be on by default** — contextual encoding is + the framework default, and turning it off is the recorded exception (§3-16), + never the ambient state. The sink-specific detail is §3-9. +- Restrictive defaults extend to headers, CORS, cookie flags, TLS, and + permissions: the secure value is the default; the permissive value is a + deliberate, recorded exception (concrete list in §3-13). + +### 3-4. Layer independent boundaries + +An important asset **MUST** be reachable only through **multiple** boundaries, so +that breaching one is not breaching all — and the layers only add up if they +fail **independently**. + +- Combine controls at **different layers** — network (restrict who can reach it), + identity (authenticate, §3-6), authorization (least privilege on what they may + do, §3-11), and application (validate input §3-8, escape output §3-9). Each is + a slice; the asset sits behind the stack. +- **Do not remove a check because another layer "already covers it."** The WAF + does not excuse input validation; the network restriction does not excuse + authentication; the ORM does not excuse least-privilege DB grants. Removing a + layer is a relaxation and **MUST** be recorded (§3-16). +- Layers **SHOULD** sit on **different implementation, vendor, or operator + lineages** so a single CVE, misconfiguration, stolen credential, or vendor + outage cannot open all of them at once. Beware **common-mode failure**: a + shared secret, base image, admin account, or dependency behind two "separate" + layers collapses them into one. When you draw the layers, ask what they have in + common — that commonality is the real single point of failure. +- **Least privilege is a layer in its own right** (NIST SP 800-53 **AC-6**): even + an attacker who passes authentication reaches only the narrow scope the + identity was granted, so the breach is bounded, not total. **Separation of + duties** (**AC-5**) is the human-layer version — no single person both makes a + change and approves it — which is why change goes through review. + +This is the layered complement to our **Zero Trust** posture (NIST SP 800-207, +"assume breach"). Zero Trust tells us to distrust every *request*; defence in +depth tells us to distrust every *control*. A verified request that slips one +check must still meet the next. + +### 3-5. Contain the blast radius + +Because we assume a layer will fall, we design so the fall is survivable. + +- **Segment** so a breach of one component, tenant, or surface does not reach the + others. +- Keep **detection and audit on an independent layer**: a compromise of the thing + being watched must not also grant control over the evidence (§3-15). +- Degrade safely — one breached layer should mean a contained, detected, + recoverable incident, not silent, total damage. + +### 3-6. Authentication + +Authentication proves *who* is calling; getting it wrong is Top 10 A07 +(Identification and Authentication Failures). + +- **MUST** authenticate through a **vetted framework or identity provider** — + never a hand-rolled login (see the OWASP Authentication Cheat Sheet and + [NIST SP 800-63B](https://pages.nist.gov/800-63-3/sp800-63b.html)). +- **MUST** store passwords, where the app holds them at all, with a **strong, + salted, adaptive hash** (Argon2id, scrypt, or bcrypt) — never plain text, never + a fast/general-purpose hash. +- **MUST** offer **phishing-resistant MFA** (passkeys / WebAuthn preferred), + consistent with the passkey/MFA rule the [Security Policy](/security-policy) + already mandates for developer accounts — user accounts get the same posture. +- **MUST** implement **anti-automation** on authentication endpoints — rate + limiting and lockout/backoff — against credential stuffing and brute force + (CWE-307). +- **MUST NOT** leak account existence through login, registration, or + password-reset responses or timing (generic "invalid credentials" only). +- **SHOULD** re-authenticate (step-up) before sensitive operations, and force a + fresh authentication event to reach any administrative surface. + +### 3-7. Session management + +Once authenticated, the session *is* the credential — the thing an attacker +wants to steal or fixate. + +- **MUST** generate session tokens with a **CSPRNG**, of sufficient length, and + treat them as opaque secrets. +- **MUST**, for cookie-based sessions, set `HttpOnly`, `Secure`, and an + appropriate `SameSite`; scope the cookie to the narrowest path/domain. +- **MUST regenerate the session identifier on privilege change** — login, + step-up, role elevation — to defeat session fixation (CWE-384). +- **MUST invalidate the session server-side on logout and on idle/absolute + timeout.** A logout that only drops the client cookie is not a logout. Idle and + absolute lifetimes SHOULD be short for high-value sessions. +- **SHOULD**, for stateless tokens (e.g. JWT), verify signature and algorithm + explicitly (reject `alg: none`), enforce `exp`/`aud`/`iss`, and keep lifetimes + short with a server-side revocation path. + +### 3-8. Input validation + +**All input from any trust boundary is untrusted** — request bodies, query and +path parameters, headers, cookies, file uploads, webhook payloads, and responses +from upstream services. + +- **MUST validate at the trust boundary**, as close to entry as possible, using + **positive (allow-list) validation** — assert the input matches an expected + type, range, length, and format; reject what does not. Deny-list filtering of + "bad" input is not sufficient. +- **MUST** validate structured input (JSON body, form) against a **schema** — the + same schema the API contract publishes — and reject unexpected fields rather + than silently ignoring them. +- **MUST** enforce type at the boundary in a way that survives to the query and + render sinks — a validated integer id cannot become an injection string. +- **MUST** treat **file uploads** as hostile: validate type/size, store outside + the web root, never trust the client-supplied filename or content type. +- **MUST NOT** rely on input validation *alone* to stop injection — validation + reduces the attack surface; the sink-specific defence (parameterisation, + encoding — §3-9, §3-10) is what actually closes it. They are layers, not + alternatives. + +### 3-9. Output encoding + +Injection is ultimately an **output** problem: data crosses into an interpreter +(HTML, SQL, a shell, a URL) that mis-reads it as code. **Encode/escape data for +the specific sink it is written to, at the moment of output.** + +- **MUST** contextually encode all untrusted data written into HTML — HTML body, + attribute, JavaScript, CSS, and URL contexts each need their *own* encoding. + Prefer a framework's auto-escaping template engine and do not defeat it. +- **MUST** encode **for the sink, not once generically** — the encoding for a SQL + identifier, an OS command argument, an HTTP header, and an HTML attribute are + all different. +- **SHOULD** deploy a strong **Content-Security-Policy** as defence-in-depth + *behind* output encoding, never as a replacement for it. + +### 3-10. Injection defences (SQLi, XSS, CSRF, SSRF, command/template) + +Top 10 A03 (Injection) is a class, not a single bug. Each variant has a +**standard, non-negotiable defence** — use it, do not improvise. + +- **SQL / NoSQL injection** — **MUST** use **parameterised queries / prepared + statements** (or a vetted query builder / ORM that parameterises) for *every* + query with a variable part. String-concatenating user input into a query is + prohibited. Where an identifier (table/column) must be dynamic, allow-list it + against a fixed set (CWE-89). +- **Cross-Site Scripting (XSS)** — **MUST** apply contextual output encoding + (§3-9) plus framework auto-escaping; for user-supplied HTML, sanitise with a + vetted library (e.g. DOMPurify) against an allow-list. Never build DOM from + untrusted strings via `innerHTML`/`eval` (CWE-79). +- **Cross-Site Request Forgery (CSRF)** — **MUST** protect state-changing + requests with **anti-CSRF tokens** (synchroniser or double-submit) and/or + `SameSite` cookies; do not rely on a single mechanism. `GET` never mutates + state (CWE-352). +- **Server-Side Request Forgery (SSRF)** — **MUST**, when the app fetches a URL + derived from user input, **allow-list** the permitted hosts/schemes, resolve + and validate the target against internal-range/metadata-endpoint blocks, and + disable unneeded redirects (Top 10 A10; CWE-918). +- **OS command / template / LDAP / XML injection** — **MUST** avoid passing + untrusted input to a shell, template engine, or expression evaluator at all; + where unavoidable, use the safe API (argument arrays, not a shell string; + sandboxed/logic-less templates; disabled XML external entities) (CWE-78, + CWE-1336, CWE-611). + +### 3-11. Access-control enforcement (authorization) + +Top 10 A01 (Broken Access Control) is the #1 web application risk. +Authentication says *who you are*; access control says *what you may do* — and it +is checked far too often on the client, or not at all. This is the server-side +enforcement of the deny-by-default rule in §3-3, and it also anchors the +[Access Control](/access-control) standard. + +- **MUST enforce access control on the server, on every request**, at the point + the resource is accessed — never trust a hidden field, a client-side check, or + a UI that "doesn't show the button." +- **MUST deny by default** — access is granted only by an explicit, positive + rule; the absence of a rule is a denial. +- **MUST check object-level ownership** on every access to a specific record — + that *this* user may act on *this* object — to close IDOR / broken object-level + authorization (CWE-639/CWE-284). Guessing another id must not reveal another + user's data. +- **MUST** enforce **least privilege** — every identity, and the app's own + service identity, holds the narrowest permissions that do the job. +- **SHOULD** centralise authorization in shared middleware/helpers so a new + endpoint is access-controlled by default rather than by the author remembering + to add a check. +- Administrative functions have their **own, stronger** rules (separate origin, a + fresh authentication event, immutable audit logs); this section is the baseline + for ordinary user endpoints. + +### 3-12. Secrets handling at runtime + +The [Security Policy](/security-policy) already forbids committing credentials +and long-lived high-privilege keys. This is the **runtime** counterpart — how the +live app holds and uses secrets — and part of the broader +[Data Protection](/data-protection) standard. + +- **MUST** read secrets at runtime from **environment configuration or a secret + manager** (AWS Secrets Manager / SSM Parameter Store, GCP Secret Manager, or + equivalent) — never hard-coded, never committed (Top 10 A05; CWE-798). +- **MUST** prefer **workload identity** over stored secrets where the platform + offers it — IAM roles attached to the compute, OIDC for CI. The best-held + secret is the one that does not exist. +- **MUST NOT** log secrets, tokens, session ids, or full credentials — scrub them + from logs, error messages, and traces (§3-14, §3-15). +- **SHOULD** support **rotation** without a redeploy, and hold secrets in memory + no longer than needed. + +### 3-13. Secure defaults + +Top 10 A05 (Security Misconfiguration): the app is only as safe as its least-safe +default. **Secure-by-default** (a NIST SSDF principle and a CISA Secure-by-Design +tenet) means the safe configuration is the one you get without opting in — the +concrete extension of §3-3. + +- **MUST** ship with **debug/developer modes, verbose errors, default + credentials, and sample/admin endpoints disabled** in production. +- **MUST** set security response headers by default — `Content-Security-Policy`, + `Strict-Transport-Security`, `X-Content-Type-Options: nosniff`, a restrictive + `Referrer-Policy`, and appropriate framing controls (see the OWASP Secure + Headers Project). +- **MUST** enforce **TLS in transit**, disable insecure protocol/cipher + fallbacks, and encrypt sensitive data at rest. +- **MUST** keep dependencies patched and free of known-vulnerable versions (Top + 10 A06; NIST SSDF **PW.4**) — enforced by the CI supply-chain scan, per the + [Supply-Chain Risk](/supply-chain-risk) standard. +- **SHOULD** minimise the attack surface — no unused features, ports, or services + enabled "just in case." + +### 3-14. Error handling that doesn't leak internals + +How an app fails is a security property. A leaked stack trace, SQL error, or +internal path is reconnaissance handed to an attacker (CWE-209). + +- **MUST fail closed** — on an unexpected error, deny the operation; never fall + through to an authenticated/authorised state on failure (this is §3-3's + fail-secure rule at the code level). +- **MUST** return a **generic error** to the client (a stable error shape) while + logging the full detail **server-side only**. Stack traces, SQL/driver + messages, internal hostnames, and framework versions never reach the client. +- **MUST** centralise error handling so one boundary handler enforces this — + matching OSBR's functional error-handling stance: expected failures returned as + values, unexpected ones crashed loud and logged, caught only at the boundary. + +### 3-15. Security logging and monitoring + +Top 10 A09 (Security Logging and Monitoring Failures): an attack you cannot see +is an attack you cannot stop or explain afterwards. + +- **MUST** log **security-relevant events** — authentication success/failure, + access-control denials, input-validation rejections, and changes to permissions + or sensitive data — with enough context (who, what, when, source) to + investigate. +- **MUST NOT** log secrets, credentials, session tokens, or unnecessary personal + data — logs are a protected asset and a breach target; neutralise log-injection + by encoding untrusted values written to logs (CWE-117). +- **MUST** protect and retain logs appropriately, and keep the audit trail on an + **independent** layer (§3-5) so a compromise of the system does not also grant + control over its evidence; administrative actions carry the stronger immutable, + ≥1-year audit-log rules. +- **SHOULD** alert on the high-signal events (repeated auth failure, denial + spikes, break-glass use) rather than only storing them — NIST SSDF **RV + (Respond to Vulnerabilities)** depends on detection. + +### 3-16. Record every deliberate relaxation + +Layers and defaults will sometimes be relaxed for a real reason. That is allowed +— **silently** relaxing them is not. An undocumented exception is +indistinguishable from a bug, so every relaxation **MUST** be recorded so the set +of deliberately-open holes is always known. + +- Any **opened path, disabled escaping, widened default, or removed layer** + **MUST** be captured in a **PR or ADR** (Architecture Decision Record) at the + time it is made — not reconstructed later. +- Each record **MUST** state **what** is relaxed, its **scope** (where it applies, + what asset it exposes), its **duration** (until when, and what closes it — + "permanent" is a decision to be argued, not a default), and **why** (the reason, + and which other layers still stand behind it). +- A relaxation with a stated duration **SHOULD** have a mechanism that surfaces it + when the duration expires (a tracked issue, a review date), so temporary holes + do not become permanent by neglect. +- The reviewer approving the PR/ADR exercises **separation of duties** (§3-4) — + the relaxation is not the sole decision of the person who wants it. The audit + trail *is* the layer that catches the other layers being lowered. + +## 4. Anti-patterns + +The failure modes this policy exists to prevent: + +- ❌ **One wall.** A single auth check (or firewall rule, or input filter) is the + only thing between the internet and the data. +- ❌ **Fail-open.** When the auth service errors, the request is allowed through + "so we don't block users." A down check must deny, not admit. +- ❌ **Escaping off by default.** Raw output with escaping opt-in, so every + forgotten call site is an injection hole. +- ❌ **Fake independence.** Two "layers" sharing one secret, library, admin + account, or vendor — they fall together. +- ❌ **Silent relaxation.** Someone disables escaping or opens a path "just for + now" and nobody records it, so the hole is permanent and invisible. +- ❌ **"The other layer covers it."** A control removed because a different layer + supposedly handles it — collapsing depth back to a single point of failure. +- ❌ A login, session, or crypto routine **hand-rolled** instead of using a vetted + library. +- ❌ A SQL query built by **string concatenation** "because it's just an internal + admin screen." +- ❌ **Access control by hidden button** — no server-side check, so changing the + id in the URL reaches another user's record. +- ❌ Untrusted HTML rendered via `innerHTML` with no encoding or sanitisation. +- ❌ A server that fetches a **user-supplied URL** with no host allow-list (SSRF to + the cloud metadata endpoint). +- ❌ A **stack trace or SQL error** returned to the client on failure. +- ❌ **Secrets** in the repo, in a config file, or printed into the logs. +- ❌ **"We'll add the security tests / ASVS review later"** — later never comes, + and the app ships unverified. + +## References + +Named standards and practice this policy draws on, chosen because they are +published, testable, and adoptable by a small team. + +**Verification standard (adopted)** + +- OWASP Application Security Verification Standard (ASVS) — L1/L2/L3 + application-layer requirements — + +**Risk catalogues — the "what goes wrong"** + +- OWASP Top 10 — +- CWE Top 25 Most Dangerous Software Weaknesses — + +**Positive controls & how-to** + +- OWASP Top 10 Proactive Controls — +- OWASP Cheat Sheet Series — +- OWASP Secure Headers Project — + +**Defence in depth & layered controls** + +- NIST SP 800-53 Rev. 5 — AC-3 (access enforcement), AC-5 (separation of duties), + AC-6 (least privilege), SC-7 (boundary protection) — + +- NSA — Defense in Depth guidance (layered, independent safeguards) — + +- James Reason — the Swiss-cheese model of accident causation — + + +**Zero Trust, least privilege & fail-secure** + +- NIST SP 800-207 — Zero Trust Architecture ("assume breach") — + +- Saltzer & Schroeder — *The Protection of Information in Computer Systems* (least + privilege; fail-safe defaults; separation of privilege) — + + +**Process & secure-by-default** + +- NIST SSDF — Secure Software Development Framework, SP 800-218 — + +- NIST SP 800-63B — Digital Identity (authentication) — + +- CISA Secure by Design — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Security lens this standard serves. +- [Security Policy](/security-policy) — the device/account complement: developer + conduct, credential hygiene, MFA/passkeys, protected assets. +- [Testing Standards](/testing-standards) — how security-relevant behaviour is + tested and kept green. +- [Access Control](/access-control) — the authorization surface §3-11 anchors. +- [Data Protection](/data-protection) — encryption and secret handling behind §3-12. +- [Supply-Chain Risk](/supply-chain-risk) — the dependency and CI scan enforcing §3-13. diff --git a/doc/architecture-standards.md b/doc/architecture-standards.md new file mode 100644 index 0000000..69dfba1 --- /dev/null +++ b/doc/architecture-standards.md @@ -0,0 +1,545 @@ +# Architecture Standards + +This is the standard the [Quality Gate](/quality-gate) holds structural and +architectural decisions to: how a system is deployed, how a package is arranged +inside, how boundaries are drawn so parts can be rebuilt, how tenants are walled +off, and how external dependencies are chosen and contained. It expands the +architectural side of the one-line rule the [Coding Style Guide](/style-guide) +carries — *dependencies point inward, ports and adapters at the edge* — into a +working standard for the shapes above that rule. It builds on the +[Infrastructure Planning Policy](/infra-planning-policy) (which prefers managed, +stateless, disposable services and weighs lock-in and exit cost) and shares the +"assume breach" instinct of [Application Security](/application-security). +Deviations are allowed, but — as everywhere in the handbook — they must be +deliberate and justified in the project's design notes or an ADR. + +Architecture is where OSBR's values become load-bearing. **Be Nice**: a teammate +can run the whole system on a laptop, change two modules in one commit, read a +package in the client's own vocabulary, and find the reasoning for every hard +choice written down beside the code. **Be Kind**: we hand the next maintainer — +human or AI — one process to reason about instead of a call graph across the +network, a dependency they can swap without asking the whole codebase, and an +exit written before it is needed rather than discovered at the worst moment. +**Be Strong**: we refuse the false sophistication of a distributed system nobody +needs yet, we keep the freedom to switch vendors, and we stay willing to throw +away and rebuild code we now judge wrong — without ego or sunk-cost paralysis. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the [Coding Style + Guide](/style-guide). **MUST** / **MUST NOT** are absolute. **SHOULD** / + **SHOULD NOT** state a strong default overridable only with a documented + reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). We adopt the *criteria* + of large-scale practices and right-size them for an SME — we do not adopt the + headcount or infrastructure behind their reference setups. +* **Architecture Decision Records (ADRs).** Several rules here require a decision + to be recorded. An ADR is a short, versioned note kept in the repository (per + the [Repository Documentation Standards](/repository-documentation-standards)) + stating the decision, its driver, the alternatives rejected, and when to + revisit it. An ADR is immutable once accepted; a later reversal is a *new* ADR + that supersedes it, so the causal history stays legible. + +[[TOC]] + +## 1. Goal + +The goal of OSBR's architecture is **software a small team can ship, change, and +hand over at the speed it actually has — without paying for sophistication it has +no use for yet.** Concretely: + +- **One deployment unit by default.** A new system is a single modular monolith + that builds, tests, deploys, and rolls back as one artifact. There is one place + to look when something breaks. Distribution is a cost taken on only against a + written, approved need. +- **Uncorrupted domain.** Every package reads in the client's vocabulary, with + frameworks, SDKs, and databases contained at the edge — so the business is + legible and the vendor is replaceable. +- **Rebuildable parts.** Boundaries are drawn along units that can be deleted and + regrown from what the repo already holds, so "rewrite this part" sits beside + "extend this part" as an ordinary engineering choice. +- **Isolation built in, not retrofitted.** Multi-tenant separation and the freedom + to switch vendors are decided proactively and recorded, because both are + dramatically cheaper built in than bolted on later. + +A structure that does not serve one of these goals is ceremony. We optimise for +the option to change cheaply, not for the appearance of a grown-up architecture. + +## 2. Responsibility + +- **Whoever starts a new system** starts it as one modular monolith with named, + in-process module boundaries and the package layout of §3-6. The single unit is + the default and needs no justification; a distributed or layer-first start does. +- **Every developer adding a feature** keeps it inside the monolith, in the module + and responsibility it belongs to, reaching external dependencies only through + their contained adapter. New code goes where the rules below put it, not where + it is quickest to paste. +- **Whoever proposes a split, a rebuild, a new dependency, or a tenant-isolation + model** records the decision — the concrete need and the cost accepted — in a PR + or ADR *before* it ships. An undocumented architectural change is an + architectural defect, not a preference. +- **Code reviewers** treat structure as reviewable surface, per the AI code + review the [Quality Gate](/quality-gate) requires: they reject an undocumented + split, a leaked vendor type, a layer-first tree, or a tenant-scoped table with no + isolation decision. Security-sensitive structure (tenant boundaries especially) + is verified under [Application Security](/application-security)'s mandatory + review. +- **AI agents** are first-class contributors of architecture and are held to + exactly the same bar. The human who merges an agent's structural change owns it + — and agents make the "write the reasoning down" discipline (§3-15, §3-17) more + important, not less, because an agent regenerates from what is written, never + from a teammate it can ask. + +This is not a job for a separate "architect" gatekeeping a catalogue. In a small +team the person building the feature is the person who keeps the system in one +piece; the defaults below are what make the sound choice the path of least +resistance. + +## 3. Practices + +### Deployment shape — modular monolith first + +We stand on named, published practice: Fowler's **MonolithFirst** and +**Microservice Premium**, the **Modular Monolith** (Simon Brown, Kamil Grzybek), +the **Majestic Monolith** (Shopify, DHH), DDD **bounded contexts** as in-process +modules, and the **fallacies of distributed computing**. + +#### 3-1. Start as one modular monolith + +- A new system MUST start as **one deployment unit** — a single artifact that + builds, tests, deploys, and rolls back together. Splitting into separately + deployed services from day one is prohibited unless §3-4's documented-need bar is + met before the first release. +- The monolith MUST be **modular from the first commit**: divided into modules + matching bounded contexts and the ubiquitous language, each owning its own logic + and data (§3-2). A monolith without internal boundaries — a "big ball of mud" — + is *not* what this endorses; the modularity is the point. +- Follow **MonolithFirst**: almost all successful microservice systems began as a + monolith that was later split, and most microservices-first builds ran into + trouble. You cannot design good service boundaries for a domain you have not yet + built. Learn the boundaries from a running monolith, then extract if the evidence + says to. + +#### 3-2. Give modules real in-process boundaries + +- Modules MUST interact only through **explicit, published in-process interfaces** + — a module's public functions — never by importing another module's internals. +- Each module SHOULD **own its own data**. No module reads or writes another's + tables directly; it asks through the owning module's interface. Shared mutable + tables across modules are the coupling that makes a future split impossible. +- A call between two of our modules SHOULD be an **ordinary function call**, not a + network request. In-process calls are synchronous, fast, transactional, and + cannot half-fail — none of which survives a network hop. The clean boundary is + the asset: a module that already exposes a narrow interface and owns its data can + be lifted into its own service mechanically; a tangled one cannot be split at any + price short of a rewrite. + +#### 3-3. Treat distribution as a cost, not a default + +- Before introducing a network boundary between our own code, the developer MUST + account for the **fallacies of distributed computing**: the network is not + reliable, not zero-latency, not infinite-bandwidth, not secure, and topology, + ordering, and administration are not free. Each becomes your code's problem the + moment the hop exists. +- A split MUST be recognised as buying **partial failure, network latency, eventual + consistency, distributed debugging, and independent deploy coordination** — the + *Microservice Premium*, paid up front whether or not the split ever delivers a + benefit. Below the complexity threshold where most SME systems live, a + distributed design is slower to build, change, and operate than the monolith it + replaced. +- Do NOT reach for a separate service, or an async queue between our own modules, + to achieve **code organisation** — that is what modules inside the monolith are + for. Distribution is a runtime-topology decision, not a tidiness decision. + +#### 3-4. Split only on a documented, concrete need + +- A module MAY be extracted into its own deployed service, but the proposal MUST + document a **concrete, present need** of one of these kinds: + - **Independent scaling** — this module's load profile genuinely differs and + scaling it inside the monolith is demonstrably wasteful or infeasible. + - **Independent deploy cadence** — this module must ship on a materially + different schedule (regulatory, or a much faster/slower rhythm) than the rest. + - **Fault isolation** — a failure here must be prevented from taking the rest of + the system down, and in-process isolation cannot achieve it. +- The need MUST be recorded in a **PR or ADR** with its evidence and the cost + accepted, and ships only after approval. Speculation ("might need to scale", + "microservices are best practice") does NOT meet this bar. +- Extraction SHOULD happen along an **existing clean module boundary** (§3-2) — you + split what is already a clean module; you do not first tangle things and then try + to cut. If the boundary is not clean yet, fix it in the monolith first. + +#### 3-5. Keep managed services as edge scaffolding + +- A managed queue, scheduler, object store, or database MAY be used as **plumbing + at the edge** of the monolith. It does not, by itself, split the domain — the + domain still lives in one deployable unit. This is consistent with the + [Infrastructure Planning Policy](/infra-planning-policy)'s preference for managed, + serverless services for a small team. +- Do NOT let a managed service become a **backdoor topology**: two of our modules + coordinating through a queue purely to feel decoupled, when an in-process call + would do, is a network hop in a managed-service costume — and it pays §3-3's + premium just the same. +- Reach every managed service through **one contained adapter** (§3-7, §3-10) so + the monolith is not welded to it and stays portable. + +### Package internals — anti-corruption structure + +We stand on named practice: Evans's **Anti-Corruption Layer**, Cockburn's +**Hexagonal Architecture (Ports & Adapters)**, Martin's **Dependency Rule**, +Palermo's **Onion Architecture**, Bernhardt's **functional core / imperative +shell**, and **package-by-feature**. This is the code-level expression of the +same [Style Guide](/style-guide) dependency rules. + +#### 3-6. Divide the package by domain (package-by-feature) + +- A package MUST be divided into **domain sections** that match the ubiquitous + language and bounded contexts (`billing`, `scheduling`, `consignment`), each + self-contained. A `scheduling` folder holds scheduling's model, service, and + adapters together. +- Group code that changes for the same reason together — Single Responsibility read + as cohesion. A change to how billing works should touch the `billing` section and + little else. +- Do NOT create a **layer-first tree** (`controllers/`, `models/`, `dao/`) as the + primary division. Layer-first scatters one feature across the repo and lets the + payment SDK leak across the whole `dao/` layer, so no boundary can promise "the + vendor is spoken to *here* and nowhere else." + +#### 3-7. Split each domain section into three responsibilities + +Inside a domain section there are exactly three homes for code — Onion/Clean +collapsed to the smallest honest number of rings, mapping onto *functional core, +imperative shell*: + +| Responsibility | Holds | Purity | +| -------------- | ----- | ------ | +| **model** | Domain data (value objects, entities) and pure functions over them — the rules and calculations | Pure: no IO, no side effects, no framework types | +| **service** | Outward procedures — the use cases that orchestrate the model to get something done | Effectful, but only via *ports* it declares; no concrete SDK | +| **dependency implementation** | The one place an external dependency (DB, SDK, HTTP API) is actually called; translates SDK/DB shapes to and from domain types | Effectful and technology-specific; the SDK lives here | + +- The **model** MUST be pure and MUST NOT import the service, the dependency + implementation, or any external library beyond the language's own value types. It + is testable with no mocks. +- The **service** MUST express what it needs from the outside world as a *port* — a + function signature or a record of functions it declares — and MUST receive the + concrete implementation by injection, not by importing it (§3-8). +- The **dependency implementation** MUST be the *only* code in the section that + imports the external SDK/driver, and it MUST return domain types (§3-10). +- Three responsibilities is the whole model. Do not add "domain services", + "mappers", and "DTOs" as ceremonial extra layers unless a section genuinely needs + them — the rings exist to protect the domain, not to be counted. + +#### 3-8. Point every dependency toward the domain + +``` +dependency-implementation ──▶ service ──▶ model + (adapters) (use cases) (pure core) +``` + +- Source-code dependencies MUST point **inward only**, per the Dependency Rule. The + model is imported by the service and the adapter; it imports neither. +- Where a service needs an effect (persist, fetch, call an API), the dependency MUST + be **inverted**: the service declares the port; the dependency-implementation + satisfies it. Control points outward at runtime, but source-code dependency still + points inward. +- **Litmus test:** could you delete the `dependency-implementation` folder and still + compile the model and service? If yes, the direction is correct. If deleting the + adapter breaks the domain, the dependency has leaked inward. + +#### 3-9. Keep entry points as thin external shells + +- `main`, HTTP handlers, message-queue consumers, CLI commands, and scheduled-job + entry functions are **shells**: read input, call a service, translate the result. + An entry point MUST NOT contain domain rules — a business decision belongs in a + `model` function the handler calls. +- An entry point MUST assemble the wiring at the edge (composition root): + construct the concrete adapters and pass them into the service. This is the one + place concrete dependencies and the domain meet, and they meet only to be + connected. +- Keep shells thin enough to need no unit tests of their own beyond the integration + test that exercises the real path; the logic worth testing has already been + pushed into the pure model. + +#### 3-10. Let no external library type cross the boundary + +This is the anti-corruption rule proper, and the one reviewers guard hardest. + +- Model and service signatures MUST use **domain types only**. A `Stripe.Charge`, + `sql.Rows`, `AxiosResponse`, `bigquery.Row`, `boto3` client, or ORM entity MUST + NOT appear above the `dependency-implementation` seam. +- The adapter MUST **translate both ways**: incoming SDK/DB shapes map to domain + types before they travel inward; domain types map to SDK calls at the edge. +- Errors cross the boundary as **domain errors** (`Result` values per the [Style + Guide](/style-guide)), not raw SDK exceptions. The `try`/`catch` around the SDK + lives in the adapter and converts the throw into a `Result` right there. +- A leaked type is a **structural defect**. The test: if replacing the vendor would + force a change to a model or service signature, the vendor has already corrupted + the domain. It is always tempting to pass the SDK's rich object one layer inward + "just this once" — that one shortcut is how a vendor ends up owning your domain. + +### Vendor neutrality — keep the freedom to switch + +The anti-corruption layer of §3-10 is the *mechanism*; this cluster is the +*discipline* around it — when to take a dependency on at all, and how to keep the +exit payable. We stand on Cockburn's Ports & Adapters, Evans's ACL, and ADR +practice (Nygard). + +#### 3-11. Add a dependency only on a clearly-met criterion + +- The default answer to "should we pull in this external dependency?" is **no — + implement it, or use what we already have**, following the same ladder the [Style + Guide](/style-guide) mandates (standard library → already-installed dependency → + custom code → last, a new dependency). +- A new external dependency MUST clear a stated criterion before it is added: the + capability is **genuinely hard to own** (a security surface we should not + reinvent, a specialised system with real operational depth, a commodity we have + no business rebuilding) *and* the dependency earns its permanent cost. "It would + save a few lines" is not the criterion. +- "Cost" is **Total Cost of Ownership** — integration, version churn, the CVE class + we inherit, the exit cost the day we leave — not the sticker price. Every package + on the manifest is attack surface, an update burden, and one more master. The + safest dependency is the one we did not add. + +#### 3-12. Log every dependency decision as an ADR + +Every decision to take on (or deliberately reject) an external dependency MUST be +recorded as an ADR. Each dependency ADR MUST state four things: + +- **Reason** — why this dependency, why now, and which §3-11 criterion it meets. +- **Assessment** — the alternatives weighed (including "implement it ourselves"), + the TCO, and the lock-in / exit-cost judgement. +- **Monitoring plan** — what we watch after adopting it: the vendor's health, + pricing changes, deprecations, its CVE feed, and our own usage growth against the + criterion that justified it. +- **Exit strategy** — concretely *how we would leave*: the replacement path, the + data we would export, the format we would export it in, and roughly what the + switch would cost. An exit strategy written before we are trapped is a plan; one + written after is an incident. + +#### 3-13. Preserve the freedom to switch — and prove it is preserved + +- Replaceable external dependencies — **database, email, payments, auth, AI/LLM + providers, third-party APIs** — MUST be reached only through the contained, + one-way adapter of §3-10, expressed in our vocabulary (`sendReceipt`, not + `stripe.charges.create`). One adapter, one implementation, is fine — the port + names the boundary and pins the translation; it is not a speculative abstraction. +- Do NOT wrap a dependency you will never replace and that has no domain vocabulary + to protect (a logging library, a date formatter). Anti-corruption is translation, + not abstraction for its own sake; the layer earns its place only where the vendor + is *replaceable* and its model would otherwise *leak*. +- Data MUST be kept in **portable, open formats** wherever practical, so an exit is + an export, not an excavation — the same stance as the [Infrastructure Planning + Policy](/infra-planning-policy). The exit strategy (§3-12) SHOULD be *tested*, not + just asserted: a second adapter behind the same port is the strongest evidence a + dependency is replaceable, and a fake for tests already counts — if the port + admits a test double, it admits a replacement vendor. +- A dependency the codebase touches in only one place is one whose exit we can + price. A dependency whose calls are scattered across the domain is lock-in that + has already happened, whatever the contract says. Favour portable, open standards + over proprietary anchors: a system we can move, audit, and hand to a client is a + stronger deliverable than one that runs slightly faster but can never leave. + +### Rebuildable boundaries — sacrificial architecture + +We stand on Fowler's **Sacrificial Architecture** and **Strangler Fig**, the +**Building Evolutionary Architectures** work (Ford, Parsons, Kua) and its +**fitness functions**, **YAGNI**, Twelve-Factor **disposability**, and Brooks's +**second-system effect** as the anti-pattern. + +#### 3-14. Draw boundaries along wholly-rebuildable units + +- Each module boundary MUST be drawn so the unit inside is **wholly rebuildable** — + replaceable in one piece, behind a stable interface, without a coordinated + rewrite of its neighbours. The test is concrete: *could you delete this directory + and regrow it from its interface plus its recorded intent, and would anything + outside it need to change?* If yes to the second, the boundary leaks. +- The boundary's **contract MUST be explicit and narrow** — the interface a + neighbour depends on is the thing that must survive a rebuild, so it is stated, + not implied by whatever internals happen to be reachable. This is the same clean + seam §3-2 requires; here the point of the seam is that you can *cut* along it. +- Prefer **smaller, independently disposable units** over one large unit that can + only be rebuilt all-or-nothing — Twelve-Factor disposability applied to source + structure, not just to running processes. + +#### 3-15. Keep the regeneration basis in the repository + +The **regeneration basis** is everything a rebuilder needs to regrow a module +*correctly* without reverse-engineering it: what it must do, why, and which +decisions are load-bearing. + +- The basis MUST live **in the repository, versioned beside the code** (per the + [Repository Documentation Standards](/repository-documentation-standards)): the + spec or acceptance criteria, the domain model, and the ADRs explaining the + non-obvious choices. Intent that lives only in a person's memory or a closed chat + is not a basis; it is a single point of failure. +- Capture **the reasoning, not just the outcome.** "We chose eventual consistency + here *because* the client accepts a 5-minute lag and it lets the module scale to + zero" is regenerable; "uses eventual consistency" is not — a rebuilder cannot tell + whether that was essential or incidental. +- Any behaviour that is *deliberate but non-obvious* (a hardware calibration + constant, a retry ceiling, a quirk that matches a legal requirement) MUST be + written down, because it is exactly the behaviour an innocent rebuild will drop. + This matters most for AI agents: an agent regenerates from what is written and + cannot ask "was this weird retry loop intentional?" — so a repo-resident basis is + what makes "have the agent rebuild that package overnight" a safe sentence to say. + +#### 3-16. Treat discard-and-rebuild as a normal option — especially early + +- Weigh **rebuild against incremental change on the merits of the change in front of + you**, not on a reflex that rewriting is always reckless. When requirements are + still fluid — early in a project, or after a pivot — the first implementation was + built on assumptions that have since moved, and rebuilding from the *current* + understanding is frequently cheaper and cleaner than bending the old shape. +- Use the **Strangler Fig** approach where a big-bang replacement would be risky: + stand the new implementation up beside the old behind the same boundary, route + traffic over gradually, retire the old one once the new carries the load. A clean + boundary (§3-14) is what makes strangling possible at all. +- Let **fitness functions** guard what a rebuild must not break — the tests, + performance budgets, and architectural checks a rebuilt module must still pass. A + rebuild backed by fitness functions is a safe move; one with nothing asserting the + old guarantees is a leap. + +#### 3-17. Record the decision to rebuild + +- When choosing to rebuild a module rather than change it incrementally, the reason + MUST be recorded in the PR description or an ADR: what the module was, why the + incremental path was worse, and what the rebuild must preserve (the contract, the + fitness functions). +- MUST NOT silently rewrite. An unexplained rebuild costs the reviewer the ability + to tell a reasoned sacrifice from churn, and costs the next reader the reasoning + they will need when *they* face the same call. +- Capture, in the same record, **what you learned from the discarded version** — the + assumption that turned out wrong, the edge case the first cut missed. The value of + a sacrificial first version is partly the lessons it bought; write them down so + the rebuild does not re-buy them. + +#### 3-18. Do not gold-plate the first version (YAGNI) + +- Build the first version to **the requirement in front of you**, not a speculative + future. The whole strategy depends on the first version being *cheap enough to + throw away*; one pre-loaded with abstractions for demand that may never arrive is + expensive to build, understand, and — the cruel part — discard, which quietly + removes the sacrificial option you were trying to keep. +- MUST NOT fall into the **second-system effect** — Brooks's warning that the second + system a team designs is the most dangerous, because they over-engineer it with + every feature held back from the first. A sacrificial rebuild replaces a module + with the **simplest thing that meets current requirements**, so it too stays cheap + to sacrifice next time. Rebuild lean, not baroque. + +### Multi-tenancy — decide isolation before the first table + +Isolation is one of the few architectural properties dramatically cheaper built +in than retrofitted: a shared table with no tenant boundary, once it holds two +customers' production data, cannot be split into per-tenant databases without a +migration project. We name the endpoints and the middle with the industry's +**silo / pool / bridge** vocabulary. + +#### 3-19. Decide the isolation model before the first tenant-scoped table + +- Before the first tenant-scoped table exists, a project serving more than one + tenant MUST choose an isolation model and record it in an ADR: + - **Shared schema / pool** — one set of tables, every tenant-scoped row carries a + `tenant_id`. Lowest cost per tenant, highest density, hardest isolation to + guarantee (it rests on a discriminator column). Default for low-sensitivity, + high-tenant-count products. + - **Schema-per-tenant / bridge** — one database, one schema per tenant. Shared + infrastructure, separate namespaces, per-tenant backup/restore granularity. + Reasonable tenant counts, moderate sensitivity. + - **Database-per-tenant / silo** — one database or instance per tenant. Strongest + isolation and blast-radius containment, per-tenant keys and residency, highest + cost and operational overhead. Reach for it when compliance demands physical + separation or a contract requires it. +- The choice MUST be **driven by data classification and regulatory regime first** + (per [Application Security](/application-security)), then bounded by cost and + tenant count — never picked by default. SHOULD prefer the **least separation that + satisfies the classification** — pool before bridge before silo. +- The ADR MUST name the chosen model, the compliance / data-sensitivity driver, the + rejected alternatives, and when to revisit — and MUST be revisited when an + incoming tenant's compliance profile exceeds what the current model guarantees. A + **hybrid** (pooled standard tenants, siloed regulated ones) is legitimate when the + ADR names the requirement that justifies it. + +#### 3-20. Automate tenant provisioning regardless of model + +- A new tenant's isolation boundary — row, schema, or database — MUST be created by + **code, not by hand**: pool provisioning inserts the tenant record and RLS policy; + bridge runs the migration set against a freshly created schema; silo stands up the + per-tenant database and migrates it. +- Manual provisioning does not scale, drifts between tenants, and is the classic + source of "tenant 47 is missing the RLS policy." Keep provisioning and migration + on **one code path** so every tenant is identical by construction. + +#### 3-21. Enforce pooled isolation at the database + +- In the shared-schema model the tenant boundary is a column value, and + application-only filtering is one forgotten `WHERE tenant_id = ?` away from a + cross-tenant leak. Pooled tenant boundaries MUST be enforced **at the database** + with Row-Level Security — set the current tenant in a session variable and let an + RLS policy scope every query — not only in application query code. +- This is defence in depth consistent with [Application + Security](/application-security)'s "assume breach": the boundary holds even when a + query forgets its filter. + +#### 3-22. Design against the noisy neighbour + +- Sharing infrastructure means one tenant's load can starve others. The isolation + model sets exposure: pooled tenants share a connection pool and query capacity and + are most exposed; silo tenants are naturally insulated. +- Whatever the model, the ADR SHOULD state the **performance-isolation stance** — + per-tenant rate limits, connection quotas, or the explicit acceptance that + low-tier tenants share best-effort capacity — so the trade is deliberate rather + than discovered under load. + +## References + +**Deployment shape — monolith first & the cost of distribution** + +- Martin Fowler — MonolithFirst — +- Martin Fowler — Microservice Premium — +- Simon Brown — Modular Monoliths — +- Kamil Grzybek — Modular Monolith: A Primer — +- Shopify (Kirsten Westeinde) — Deconstructing the Monolith — +- David Heinemeier Hansson — The Majestic Monolith — +- L. Peter Deutsch et al. — Fallacies of Distributed Computing — + +**Package internals — boundary architectures** + +- Eric Evans — *Domain-Driven Design* (2003): Bounded Context, Anti-Corruption Layer — +- DDD-crew — Anticorruption Layer pattern (Context Mapping) — +- Alistair Cockburn — Hexagonal Architecture (Ports & Adapters) — +- Robert C. Martin — The Clean Architecture / The Dependency Rule — +- Jeffrey Palermo — The Onion Architecture — +- Gary Bernhardt — Boundaries (functional core, imperative shell) — +- Package by feature, not layer — + +**Vendor neutrality — lock-in, portability, decision records** + +- Michael Nygard — Documenting Architecture Decisions (ADR) — +- Architecture Decision Records — +- Gregor Hohpe — "Don't get locked up into avoiding lock-in" — +- ISO/IEC 25010 — Portability (adaptability, replaceability) — + +**Rebuildable boundaries — sacrificial & evolutionary architecture** + +- Martin Fowler — Sacrificial Architecture — +- Martin Fowler — Strangler Fig Application — +- Neal Ford, Rebecca Parsons, Patrick Kua — Building Evolutionary Architectures (fitness functions) — +- Martin Fowler — Yagni — +- The Twelve-Factor App — Disposability — +- Fred Brooks — *The Mythical Man-Month* (the second-system effect) — + +**Multi-tenancy — tenant isolation** + +- AWS — *SaaS Architecture Fundamentals: Tenant Isolation* (silo / pool / bridge) — +- AWS — *SaaS Architecture Fundamentals: Tenant Onboarding* (automated provisioning) — +- Microsoft — Architecture approaches for multitenancy: Tenancy models — +- Microsoft — Noisy Neighbor antipattern — +- PostgreSQL — Row Security Policies (RLS for pool isolation) — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the AI code review this standard's structural rules are checked under. +- [Coding Style Guide](/style-guide) — the Dependency Rule, Ports & Adapters, `Result` errors, and the KISS/YAGNI dependency ladder this page operationalises. +- [Infrastructure Planning Policy](/infra-planning-policy) — managed/serverless preference, stateless & disposable runtime, lock-in and exit-cost weighing. +- [Application Security](/application-security) — data classification, mandatory security review, and the "assume breach" stance behind tenant isolation. +- [Repository Documentation Standards](/repository-documentation-standards) — where ADRs and the regeneration basis live, versioned beside the code. diff --git a/doc/building-for-ai-users.md b/doc/building-for-ai-users.md new file mode 100644 index 0000000..65de78d --- /dev/null +++ b/doc/building-for-ai-users.md @@ -0,0 +1,251 @@ +# Building for AI Users + +The software we ship is driven by both people and autonomous agents, so we plan +for **AI agents as first-class users** — alongside humans — from the earliest +scoping stage. This standard sets out who we build for; it complements the +[Development Guide](/development-guide) and [Quality Gate](/quality-gate) (which +shape *how* we build and how work is held to a bar), the [API Design +Guide](/api-design) (the machine-consumable contracts agents call), the +[Application Security](/application-security) standard (least-privilege and +auditability for agent-facing surfaces), and the [Design +Guidelines](/design-guidelines) (the human-facing legibility that oversight +depends on). It is the planning-time face of the [AI Usage +Guideline](/ai-usage-guideline). Deviations are allowed, but — as everywhere in +the handbook — they must be deliberate and justified in the project's design +notes. + +This is where OSBR's **human⇄AI cooperation** stance becomes concrete: design +for both users, and keep humans in control. **Be Nice**: never trap a user — +the person at the keyboard, or the humans behind an agent — in a process they +cannot escape. **Be Kind**: make the system legible enough that people can +genuinely understand and intervene, not merely rubber-stamp what an agent did. +**Be Strong**: build systems robust enough to be trusted with autonomy, with a +human path that holds even when the automation misbehaves. + +## How to read this policy + +* **Requirement levels** follow RFC 2119. **MUST** / **MUST NOT** are absolute. + **SHOULD** / **SHOULD NOT** state a strong default overridable only with a + documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry standard or protocol, it + is named inline and cited under [References](#references). We pick standards + that are open and adoptable by a small team, and right-size them — we do not + take on the infrastructure behind their reference deployments. + +[[TOC]] + +## 1. Goal + +Plan every product so that: + +1. **Both users are designed for.** Humans and AI agents are both intended + consumers of what we ship. Interfaces, data, and actions are reachable by + machines without scraping, screen-driving, or reverse-engineering — the + [API-first](https://www.openapis.org/) posture of the [API Design + Guide](/api-design), extended to agents. +2. **Humans stay in control.** No AI-driven process runs where a human cannot + see what it is doing, understand why, stop it cleanly, and take the wheel. + This is **human oversight** as a hard requirement, not a feature bolted on + later. + +A capability that serves an agent audience but hides behind a human-only GUI, or +an autonomous flow whose only off switch is killing the process, fails this goal +regardless of how well it demos. + +## 2. Responsibility + +- **Planners and product owners** MUST treat "an AI agent is a user of this + feature" as an explicit scenario during scoping, and MUST record the + human-in-the-loop path for any autonomous flow as an acceptance criterion — + not a later enhancement. +- **Architects** MUST choose interfaces and autonomy levels deliberately (§3, + §5) and document them in the project's design notes. +- **Engineers** MUST build the observe / interpret / interrupt / take-over path + (§4) into the feature, and MUST NOT ship an autonomous flow whose only stop + control is killing the process. +- **Reviewers** MUST reject autonomous features that are unobservable or + non-interruptible, exactly as the [Quality Gate](/quality-gate) rejects + features that are insecure or untested. + +## 3. Two Users: Humans and Agents + +### 3-1. Agents are users, not an afterthought + +The industry is standardising on **agentic AI** — software that plans and acts +toward goals with limited supervision — and on open protocols for agents to use +tools and talk to one another. Plan for this reality: + +- **Model Context Protocol (MCP)** is the emerging open standard for exposing + tools, data, and actions to AI agents ([modelcontextprotocol.io](https://modelcontextprotocol.io/)). + When a capability should be usable by an agent, prefer exposing it as an **MCP + tool** with typed inputs and outputs over expecting the agent to drive a human + UI. +- **WebMCP** extends this into the browser: a site registers structured tools + that in-browser agents discover and call, instead of scraping the DOM + ([W3C Web Machine Learning CG draft](https://webmachinelearning.github.io/webmcp/)). + For web front-ends whose actions an agent should perform, consider exposing + tools via WebMCP rather than relying on the agent to click through the page. +- **Agent-to-Agent (A2A)** is the open protocol (originally Google, now a Linux + Foundation project) for agents to discover each other's capabilities and + collaborate across vendors and frameworks ([a2a-protocol.org](https://a2a-protocol.org/), + [github.com/a2aproject/A2A](https://github.com/a2aproject/A2A)). When our + system is one participant among several autonomous services, plan its + capabilities as an A2A-style advertised interface, not a private integration. + +You do not have to adopt MCP / WebMCP / A2A on day one. The requirement is to +**decide deliberately** at planning time whether a capability has an agent +audience, and to design the interface so that adopting these standards later is a +small step, not a rewrite. + +### 3-2. Machine-consumable by design + +- Every capability meant for agents MUST be reachable through a + **machine-consumable, documented interface** — a stable API with a published + contract (e.g. [OpenAPI](https://www.openapis.org/), per the [API Design + Guide](/api-design)) or an MCP/A2A surface — never only a human GUI. +- Actions exposed to agents MUST have **typed, validated inputs and explicit + outcomes**, so both a caller and a human observer can tell what was requested + and what happened. +- Agent-facing surfaces MUST be **least-privilege and auditable**, per the + [Application Security](/application-security) standard: an agent gets only the + tools and scopes it needs, and every agent-invoked action is attributable in + logs. + +## 4. Keep Humans in the Loop + +Every AI-driven process MUST provide a human path with four capabilities. Name +them in the requirements and test them like any other acceptance criterion. + +1. **Observe** — a human can see, in near-real time and after the fact, what the + agent is doing and has done. Emit structured logs, decisions, and the inputs + behind them; standardising on [OpenTelemetry](https://opentelemetry.io/) + keeps agent activity as inspectable as any other service. +2. **Interpret** — the human can understand *why* the agent acted: what goal, + what inputs, what tool calls, what alternatives. An action a human cannot + interpret cannot be meaningfully approved or overridden, which is why the + legibility the [Design Guidelines](/design-guidelines) require extends to + agent activity too. +3. **Interrupt** — the human can **pause or stop** the process cleanly, at any + point, without corrupting state. This draws on the AI-safety notions of + **controllability and interruptibility**: an agent should be safely + interruptible — able to be stopped by an operator without the system learning + to prevent, resist, or route around that intervention ([Orseau & Armstrong, + *Safely Interruptible Agents*](https://intelligence.org/files/Interruptibility.pdf)). +4. **Take over** — the human can assume manual control and complete or reverse + the task themselves. Autonomous actions SHOULD therefore be **reversible or + confirmable** (expand/contract, staged commits, undo), consistent with our + deploy-safely-and-reversibly stance. + +**Human-in-the-loop** means a human approves each consequential action before it +takes effect; **human-on-the-loop** means the agent acts autonomously while a +human monitors and can intervene. Both are legitimate; the choice depends on the +autonomy level (§5) and the blast radius of a wrong action. Higher stakes ⇒ +closer to in-the-loop. This mirrors the **human oversight** requirement in +emerging AI governance ([EU AI Act, Article 14](https://artificialintelligenceact.eu/article/14/)) +and the [NIST AI Risk Management Framework](https://www.nist.gov/itl/ai-risk-management-framework). + +- **MUST:** for any action that spends money, changes production data, contacts + a third party, or is otherwise hard to reverse, default to + **human-in-the-loop** (explicit confirmation) unless a deliberate, documented + decision says otherwise. +- **MUST:** every autonomous flow has a working **stop control** reachable by a + human, and stopping never leaves data in a corrupt state. +- **SHOULD:** prefer **human-on-the-loop** with strong observability for + high-volume, low-stakes actions, so humans are not a bottleneck where the risk + does not warrant it. + +## 5. Levels of Autonomy + +State the autonomy level of each AI-driven feature explicitly, the way the +automotive industry names **SAE levels of driving automation (J3016)** — from +driver-assist to full self-driving ([SAE J3016](https://www.sae.org/standards/content/j3016_202104/)). +The analogy is deliberate: the hard questions are the same — *who is responsible +for the task right now, and how fast can control hand back to a human?* + +A practical OSBR ladder: + +- **L0 — Manual:** human does the work; no AI action. +- **L1 — Assist:** AI suggests; a human performs every action. +- **L2 — Supervised (human-in-the-loop):** AI proposes and can act, but each + consequential action needs human confirmation. +- **L3 — Monitored (human-on-the-loop):** AI acts autonomously within bounds; a + human monitors and can interrupt or take over at any time. +- **L4 — Bounded-autonomous:** AI acts without routine human attention *within a + constrained, well-understood domain*; humans handle exceptions and set the + bounds. + +Rules: + +- Each feature MUST declare its level in the design notes, and MUST NOT operate + above the level it was reviewed for. +- The **interrupt** and **take-over** paths (§4) are mandatory at **every** level + from L2 upward — there is no OSBR level at which a human loses the ability to + intervene. +- Raising a feature's autonomy level is a **change that requires review**, not a + config tweak. + +As with SAE's levels, the value is in the shared vocabulary, not in racing to the +highest number. Most OSBR features should sit at L1–L3. L4 is a deliberate, +justified choice for a narrow, well-bounded domain. + +## 6. Planning Checklist + +Answer these while scoping any feature that an agent might use or that acts +autonomously: + +- [ ] **Audience:** Is an AI agent an intended user of this capability? If yes, + what interface serves it (API / MCP / WebMCP / A2A)? +- [ ] **Contract:** Is the agent-facing surface machine-consumable, typed, + documented, and least-privilege? +- [ ] **Autonomy level (§5):** What level is this, and who is responsible for the + task at that level? +- [ ] **Observe:** How does a human see what the agent is doing and has done? + What is logged or traced? +- [ ] **Interpret:** Can a human reconstruct *why* an action was taken? +- [ ] **Interrupt:** What is the stop control, and does stopping leave state + consistent? +- [ ] **Take over:** Can a human finish or reverse the task manually? Are actions + reversible or confirmable? +- [ ] **In-the-loop vs on-the-loop:** For each consequential action, which is it, + and does the blast radius justify the choice? + +If any answer is "we don't know yet," that is a planning gap to close before +build — not a detail to discover in production. + +## References + +**Agentic AI & interoperability** + +- Model Context Protocol (MCP) — +- WebMCP (W3C Web Machine Learning Community Group draft) — +- Agent2Agent (A2A) Protocol — · + +**API-first / machine-consumable design** + +- OpenAPI Specification — + +**Human oversight & AI governance** + +- NIST AI Risk Management Framework — +- EU AI Act, Article 14 (Human Oversight) — + +**Controllability & interruptibility (AI safety)** + +- Orseau & Armstrong, *Safely Interruptible Agents* (UAI 2016) — + +**Levels of autonomy (analogy)** + +- SAE J3016 — Levels of Driving Automation — + +**Observability** + +- OpenTelemetry — + +**Related OSBR standards** + +- [AI Usage Guideline](/ai-usage-guideline) — the human⇄AI cooperation stance this planning policy makes concrete. +- [API Design Guide](/api-design) — the machine-consumable, contract-first surfaces agents call. +- [Application Security](/application-security) — least-privilege, auditability, and access control for agent-facing surfaces. +- [Design Guidelines](/design-guidelines) — the human-facing legibility that observe and interpret depend on. +- [Quality Gate](/quality-gate) — where unobservable or non-interruptible autonomy is rejected. +- [Development Guide](/development-guide) — how these requirements land as acceptance criteria and design notes. diff --git a/doc/capability-over-track-record.md b/doc/capability-over-track-record.md new file mode 100644 index 0000000..8388a81 --- /dev/null +++ b/doc/capability-over-track-record.md @@ -0,0 +1,163 @@ +# Capability over Track Record + +This policy defines how OSBR **proves it can do the work.** When a client or a +user needs to believe we are capable, we do not reach first for a résumé of past +projects, a logo wall, or a list of credentials. We reach for the real thing at +hand — a working proof-of-concept, the concrete design reasoning behind a +decision, and the running service itself. A track record answers *"were they any +good before?"*; a demonstration answers *"are they solving **this** problem, for +**this** client, right now?"* — and only the second question keeps our motivation +aligned with client and user value, because the only way to answer it is to +actually engage with their problem. + +It sits close to two other standards. It complements [Verify Before +Building](/verify-before-building): that policy uses small disposable experiments +to *learn* whether an idea holds; this one uses the real artifact to *show* that +we can deliver it. It also carries a duty into the open — a public write-up is +attack surface, so this policy leans on [Application +Security](/application-security) for the anti-reconnaissance discipline that +keeps a reputation piece from becoming an attacker's map. And it shapes how we +pitch: the demonstrated-capability stance belongs in the [Planning & Shaping +stage](/development-guide) of any proposal, before a track record is ever +offered as a substitute for evidence. + +This is where three OSBR values pull in the same direction. **Be Strong** is +proving by building, not by boasting: anyone can narrate a win, but it takes +strength to put a running artifact in front of people and let it be inspected, +questioned, and broken. **Be Nice** is letting the client judge us on evidence +they can see rather than asking them to defer to our authority — it respects +their time and their judgement. **Be Kind** is guarding the people behind a case +study: never publishing without consent, never handing an attacker +reconnaissance detail about systems a client trusted us with. We protect the +client before we promote ourselves. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as elsewhere in the handbook. + **MUST** / **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a + strong default overridable only with a documented reason. **MAY** marks a free + choice. +* **Named practice.** Where a rule adopts a widely-recognised idea, the idea is + named inline and cited under [References](#references). We adopt ideas a small + team can actually run, not the ceremony larger organisations wrap around them. + +[[TOC]] + +## 1. Goal + +The goal is to **establish credibility through evidence the other side can +inspect, not claims they must take on faith — while never letting that evidence +become a gift to an attacker.** Concretely: + +- **Show, don't tell.** A demo the client can click, poke, and try to break is + worth more than any narrated success. Demo-driven credibility means we let the + work speak. +- **Anchor proposals in something runnable.** A proof-of-concept that touches + the client's actual problem beats a slide claiming "deep experience in X." The + PoC *is* the pitch. +- **Keep motivation honest.** Leaning on the real thing forces us to stay + engaged with the client's and users' value — you cannot fake a running service + the way you can embellish a track record. +- **Do not over-share.** What we publish to build reputation MUST NOT double as + reconnaissance for whoever is probing the client's systems. + +## 2. Responsibility + +- **Everyone proposing or pitching work** owns demonstrating capability with the + real thing at hand — a PoC, concrete design reasoning, or the running service — + *before* falling back on past-project name-dropping. This is part of the + [Planning & Shaping](/development-guide) work of a proposal, not an + afterthought. +- **The engineer building a demo or PoC** owns making it genuinely runnable and + honest: a demonstration that quietly fakes its result is worse than none, and + defeats the entire point. +- **Whoever publishes a case study** owns obtaining client consent first, and + keeping configuration and stack detail coarse enough that it cannot be used to + attack the client. +- **Nobody** owns a track record being impressive. The prior list is not the + evidence; the working thing in front of us is. + +## 3. Practices + +### 3-1. Prove with the real thing at hand + +Capability is shown, not asserted. The strongest evidence is something the other +side can exercise for themselves. + +- You **MUST** demonstrate capability using a working PoC or prototype, concrete + design reasoning, or the running service before resorting to a recital of past + achievements. Show, don't tell. +- Proposals **MUST** be anchored in something runnable wherever possible: a PoC + that engages the client's actual problem, not a generic claim of experience. + If we cannot yet build it, we **MUST** say so plainly rather than substituting + a track record for evidence. +- A demo **MUST** work against reality before it is shown — verified end to end + so it is not quietly faking its result. See [Verify Before + Building](/verify-before-building) for how we prove a thing works against + reality before presenting it, and the [Quality Gate](/quality-gate) for the + bar the artifact itself must clear. +- The live service **SHOULD** be treated as the primary credential. A URL the + client can exercise beats any list of prior engagements. +- Design reasoning **SHOULD** be walkable end to end — why this trade-off, what + we rejected and why — so capability is visible in the thinking, not only in the + outcome. +- Prefer **live demonstration over static portfolio** when both are possible: a + running system that can be exercised is stronger evidence than a screenshot of + one that once ran. + +### 3-2. Publish case studies without arming an attacker + +The urge to show *exactly* how clever the build was is how a reputation piece +turns into an attacker's map. The reader we must design for is not only the +prospective client — it is also the one probing the client's systems tonight. A +public write-up is OSINT surface, and this section applies the +reconnaissance-minimising discipline of [Application +Security](/application-security) to what we say about our own work. + +- You **MUST** obtain explicit client consent before publishing any case study, + screenshot, metric, or architecture detail that identifies the client or their + system. +- Published configuration and stack detail **MUST** stay coarse. Name the *shape* + of a solution ("event-driven ingestion with a managed queue"), never the + operational specifics an attacker needs — exact versions, endpoint paths, + internal hostnames, header or token formats, IAM structure, or infrastructure + topology. +- You **SHOULD** scrub every case study for reconnaissance value the same way you + scrub code for secrets: ask what a hostile reader now knows that they didn't + before. Assume the write-up is read by someone looking for a way in. +- When consent is absent or the detail cannot be made coarse without gutting the + story, **do not publish.** A missing case study costs us a marketing asset; a + careless one costs the client their security margin. + +## References + +Named ideas this policy draws on, chosen because they are widely recognised and +adoptable by a small team. + +**Prove by demonstrating** + +- *Show, don't tell* / demo-driven credibility — evidence a client can inspect + over claims they must trust. +- PoC-driven proposals — anchoring a pitch in a runnable proof of concept against + the client's real problem. +- Portfolio vs. live demonstration — a running, exercisable service as stronger + evidence than a static portfolio entry. + +**Disclose without over-sharing** + +- Security through not over-sharing — public configuration and stack detail as + reconnaissance / OSINT surface for attackers. +- Responsible case-study disclosure — client consent and coarse-grained detail as + preconditions for publishing what we built. + +**Related OSBR standards** + +- [Verify Before Building](/verify-before-building) — reduce uncertainty with + small disposable experiments, and prove the thing works against reality before + presenting it. +- [Quality Gate](/quality-gate) — the bar the demonstrated artifact itself must + clear. +- [Application Security](/application-security) — the reconnaissance-minimising + discipline behind coarse-grained disclosure. +- [Development Guide](/development-guide) — the Planning & Shaping stage where a + proposal earns its credibility with the real thing. diff --git a/doc/ci-cd-pipeline.md b/doc/ci-cd-pipeline.md new file mode 100644 index 0000000..9289ade --- /dev/null +++ b/doc/ci-cd-pipeline.md @@ -0,0 +1,245 @@ +# CI/CD Pipeline + +This is the standard for how a change travels from a developer's machine to +production at OSBR. It expands the CI/CD stance the [Infrastructure Planning +Policy](/infra-planning-policy) states — deploy from the reviewed main line, at +dev/prod parity, measured with DORA — into a working pipeline: three gates, what +each one owns, and where the authoritative pass/fail is decided. It is the +delivery half of the story the [Testing Standards](/testing-standards) tell about +*where tests run* (§3-12); read the two together. Deviations are allowed, but — +as everywhere in the handbook — they must be deliberate and justified in the +project's design notes. + +The pipeline is where OSBR's values stop being aspirations and become machine +enforcement. **Be Nice**: the same script an engineer runs locally is the script +CI runs, so the pipeline is legible — nobody has to reverse-engineer a bespoke CI +config to know what "green" means. **Be Kind**: the release gate is enforced by +the machine, not by any one operator's diligence, so no teammate carries the +release on their personal care and no colleague inherits a break because someone +was tired on a Friday. **Be Strong**: the gate runs against a real +production-simulation, on a fixed architecture, so it finds the environment- and +architecture-dependent failure before a user does — not the tautology of a check +that only passes because it never touched anything real. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the [Coding Style + Guide](/style-guide). **MUST** / **MUST NOT** are absolute. **SHOULD** / + **SHOULD NOT** state a strong default overridable only with a documented + reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice or tool, it is + named inline and cited under [References](#references). We adopt the *criteria* + of large-scale practice and right-size them for an SME — we do not adopt the + headcount or infrastructure behind their reference setups. + +[[TOC]] + +## 1. Goal + +The goal of the pipeline at OSBR is **the same change, the same result — fast +where feedback can be fast, and authoritative where correctness must be +decided.** Concretely, an engineer who picks up a task produces the **same +result** regardless of host OS (PC / Mac) or CPU architecture (x86 / ARM), and no +change reaches production until it has passed authoritatively in a +**production-simulation environment**. Three properties define success: + +- **Deterministic.** The release gate gives one answer across PC / Mac / x86 / + ARM, never two "official" results that disagree because one ran on Apple + Silicon and one on an x86 runner. +- **Machine-enforced.** The gate is enforced by the pipeline, not by an + operator remembering to run it. A change that has not passed cannot merge or + deploy — this is not a convention, it is a mechanism. +- **Production-like where it counts.** The authoritative gate runs against a + simulation of production — same container images, real or emulated managed + services, prod-like config — that a deliberately light local loop does not + reproduce. + +A gate that does not move one of these properties is waste. We optimise for a +trustworthy release, not for a fast pipeline that lets the wrong thing through. + +## 2. Responsibility + +The pipeline runs in **three gates, each a prerequisite of the next.** Every +engineer owns getting their change through all three; the gates are shared +infrastructure and their health is a team-level signal, never an individual +rating — the same way the DORA metrics are read in the [Infrastructure Planning +Policy](/infra-planning-policy). + +- The **author of a change** owns getting it green through Local and CI before it + merges. "Done" includes a green CI run, not just a green laptop. +- The **reviewer** treats the pipeline result as part of the reviewable surface + ([Code Review](/code-review)): a passing CI run is a precondition for review, + not a substitute for it. +- The **team** owns the health of the shared gates: a perpetually-red or flaky CI + on main is a team-level defect that blocks everyone, and fixing it is a duty + owed to the team. +- **AI agents** are first-class contributors here and are held to exactly the + same bar; the human who merges an agent's change owns its passage through the + gates. + +### 2-1. Local — fast, environment-agnostic feedback (not a production simulation) + +On the engineer's own machine, run the **environment-agnostic checks only**: +domain logic, type-checking, unit tests, lint. These fail the same way on any +host, so they belong where feedback is fastest. Local **MUST NOT** be relied on +to stand up production-like infrastructure — it stays light so the inner loop +stays in seconds. Local green means *"my logic and types are correct,"* and +nothing more. It is the same environment-agnostic inner loop the [Testing +Standards](/testing-standards) require of small tests (§3-3, §3-12). + +### 2-2. CI — the production-simulation environment (the authoritative gate) + +CI **is** the production simulation, and it is the single authoritative result. +It MUST run centrally on one **canonical architecture**, from the **same +container images** as production, with: + +- managed services **emulated** (e.g. LocalStack) or provisioned as **ephemeral + sandboxes**; +- **production-like configuration and secrets**, injected via OIDC at runtime, + never committed to the repo; +- **seeded data** for the run. + +Integration and end-to-end tests run **here**, not locally — this is where +environment- and architecture-dependent failures surface: native binaries (x86 +vs ARM), integration with real dependencies, config and secret wiring, +networking, managed-service behaviour, timing and concurrency. A change **MUST +NOT** merge until CI is green, and CI's fixed architecture means x86-vs-ARM +differences never produce two "official" answers. + +### 2-3. CD — deploy, only after CI is green + +On merge to `main` / release, CD deploys. It **MUST** run only after the CI +production-simulation gate is green — never before. CD runs the reproducible +deploy script (`scripts/deploy.sh`), uses **progressive delivery** (canary / +blue-green), and **verifies production responses online after the release** — a +failed post-release verification is a failed release. Credentials are passed only +at runtime. + +## 3. Practices + +### 3-1. Determinism across host and architecture + +The whole point of the pipeline is that the machine, not the machine's owner, +decides correctness. That requires the run to be reproducible byte-for-byte. + +- **Containerize everything.** Local checks, CI, and the build MUST run the same + command in the same container image. The container is the parity mechanism: Mac + or PC, inside the container it is the same Linux userland. +- **Pin versions.** Base images MUST be pinned to a digest or specific tag (never + `latest`); toolchain versions (Node / Go / Python) pinned; lockfiles committed + so dependencies resolve to exact versions. +- **Fix the canonical build/test architecture.** The authoritative CI MUST run on + one chosen architecture (e.g. `linux/amd64`), so native-binary dependencies + (image processing, bundlers, database engines, `node-gyp`-built modules) + produce one official result. Engineers on Apple Silicon (ARM64) get parity + through the same container image — via emulation locally when needed — but the + official answer comes from the fixed-arch CI. +- **Deterministic builds.** Build outputs MUST NOT bake in wall-clock timestamps, + randomness, or machine-specific paths. + +### 3-2. One set of scripts across all three gates + +Consolidate the pipeline into runnable scripts (e.g. `scripts/ci.sh`, +`scripts/deploy.sh`) that Local, CI, and CD all **invoke** — never reimplement +inline. What an engineer runs locally MUST be the same command CI runs. This is +the **Be Nice** rule for the pipeline: the checks are legible because they live +in the repo as plain scripts, not scattered across a CI platform's bespoke +configuration. + +- Keeping one script set is what stops the three gates from drifting apart. +- It keeps the pipeline portable: CI only invokes the scripts, so there is no + CI-platform-specific logic to lock the project in. + +### 3-3. Two classes of error, two loops + +Where a check runs follows from what class of error it catches: + +| Error class | Examples | Caught at | +| ----------- | -------- | --------- | +| Environment/arch-agnostic | domain logic, types, unit, lint | **Local** (fast) | +| Environment/arch-dependent | integration, native binaries (x86/ARM), config/secret wiring, networking, managed-service behaviour, timing/concurrency | **CI production-simulation** (authoritative) | + +Local green is **never** taken as proof that production will work — that is the CI +production-simulation gate's job. Pushing an environment-dependent check down into +the local loop only makes the loop slow and the result unreliable; pushing an +agnostic check up into CI only makes feedback slower. Each check belongs in the +loop that matches its error class. + +### 3-4. Secrets and config are injected, never committed + +- Secrets **MUST NOT** live in the repository. CI and CD obtain + production-like credentials at runtime via **OIDC** (short-lived, federated + identity), so there is no long-lived secret to leak. +- Configuration is supplied to the container as environment / config at run + time, so the **same image** is promoted unchanged from CI through to + production — the parity the [Infrastructure Planning + Policy](/infra-planning-policy) requires, achieved by IaC and containers rather + than a hand-maintained shared box. + +### 3-5. Managed services are emulated or ephemeral in CI + +Integration against real dependencies is where belief meets reality, so CI must +provide something real enough to break against — without depending on a shared, +long-lived environment. + +- Managed and external services **SHOULD** be **emulated** (e.g. LocalStack for + cloud APIs) or stood up as **ephemeral sandboxes** for the duration of the run, + then torn down. This mirrors the throwaway-real-dependency discipline the + [Testing Standards](/testing-standards) require of integration tests (§3-7). +- CI **MUST NOT** depend on a shared, hand-maintained environment: parity comes + from IaC and containers, not from a fragile staging box everyone shares. + +### 3-6. Deploy is progressive and verified online + +A green CI gate says the change is correct in simulation; it does not by itself +prove the change is healthy in production. CD closes that last gap. + +- CD **SHOULD** use **progressive delivery** — canary or blue-green — so a bad + release is exposed to a fraction of traffic before it reaches everyone, and can + be rolled back without a redeploy. +- CD **MUST** verify production responses **online after the release**. A failed + verification is a failed release and MUST trigger rollback — deploying is not + "done" until production is observed healthy. + +### 3-7. The gate is machine-enforced, not operator-trusted + +- A change **MUST NOT** be merged or deployed on the strength of a local run or a + reviewer's recollection that "CI usually passes." The authoritative CI run is + the gate, and the branch-protection / pipeline configuration MUST enforce it — + **Be Kind** means no colleague's release rests on another's personal + vigilance. +- Bypassing the gate (force-merge, skipped CI, deploying a build CI did not + produce) is prohibited except as a deliberate, recorded emergency action with + a follow-up to restore the invariant. + +### 3-8. Acceptance criterion + +> An engineer, on any OS / architecture, runs the same container and the same +> script and gets a byte-identical build and the same agnostic-test result +> locally; the authoritative pass/fail for environment- and +> architecture-dependent behaviour is decided by the CI production-simulation +> environment on the fixed canonical architecture; and CD deploys only after that +> gate is green, verifying production online before the release is called done. + +## References + +**Delivery practice & metrics** + +- DORA — *Accelerate* / State of DevOps research on the four key delivery metrics — +- Jez Humble & David Farley, *Continuous Delivery* — +- Martin Fowler, "Continuous Integration" — + +**Techniques & tooling** + +- LocalStack — local emulation of cloud services for CI — +- Martin Fowler, "Blue Green Deployment" — +- Martin Fowler, "Canary Release" — +- OpenID Connect — federated, short-lived identity for secretless CI/CD — + +**Related OSBR standards** + +- [Infrastructure Planning Policy](/infra-planning-policy) — CI/CD stance, dev/prod parity, DORA metrics. +- [Testing Standards](/testing-standards) — where tests run: local-agnostic vs CI production-simulating (§3-12). +- [Quality Gate](/quality-gate) — the AI code review and reliability lens the pipeline serves. +- [Code Review](/code-review) — a passing pipeline is a precondition for review, not a substitute. +- [Development Guide](/development-guide) — the pull-request flow the gates sit within. diff --git a/doc/code-review.md b/doc/code-review.md new file mode 100644 index 0000000..65dd5b1 --- /dev/null +++ b/doc/code-review.md @@ -0,0 +1,200 @@ +# Code Review + +This is the standard the [Quality Gate](/quality-gate)'s AI code review holds +every change to before it merges. It expands that gate into a working policy: +**what** the review guards, in **what priority**, **who** stays accountable, and +**when** it runs. OSBR's stance is that AI review is the default gate on every +change — consistent and thorough rather than dependent on who happens to be free +to look. Every member's machine runs an AI coding agent carrying OSBR's review +tooling — the `/code-review` plugin and, for security-sensitive surfaces, the +`asvs-audit` plugin (osbrjp/DevTool) — and the review is performed **when a +change is declared `Impl Review`**, pre-merge, alongside the CI +production-simulation gate described in the [CI/CD Pipeline](/ci-cd-pipeline) +standard. Deviations are +allowed, but — as everywhere in the handbook — they must be deliberate and +justified in the project's design notes. + +Review is where OSBR's cooperation between humans and AI becomes a gate rather +than a hope. **Be Kind**: an unreviewed change that regresses safety or leaves +bloat behind is a cost the whole team pays later, so guarding every change is a +duty owed to the team, not a courtesy. **Be Nice**: the review leaves behind +concrete, cited findings a teammate can act on, not vague disapproval. **Be +Strong**: the reviewer's job is to catch the correctness or security defect +before a user does, and it holds that line on every change without tiring. The +machine is tireless and consistent; the human interprets, overrides with a +recorded reason, and stays accountable. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the [Coding Style + Guide](/style-guide). **MUST** / **MUST NOT** are absolute. **SHOULD** / + **SHOULD NOT** state a strong default overridable only with a documented + reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). We adopt the *criteria* + of large-scale practices and right-size them for an SME — we do not adopt the + headcount behind their reference setups. + +[[TOC]] + +## 1. Goal + +The goal of code review at OSBR is a **consistent, thorough guard on every +change — for correctness, security, and readability, in that priority order — +that does not depend on who happens to review.** OSBR believes in cooperation +between humans and AI: code is a shared artifact both must be able to work with, +so the standard is code that is **correct, safe, and legible to a human with or +without AI.** Automated AI review is the mechanism that keeps every change to +that standard. + +A review that does not move one of those three concerns is noise. We optimise for +findings that change the code, not for the appearance of having looked. + +## 2. Responsibility + +- **AI-automated review is the default review gate on every change.** It is not + removed on the argument that policy-grounded generation and each developer's own + QA suffice, and it is not left to a human reviewer's availability. An AI + reviewer inspects each change and MUST pass — or surface findings to be + addressed — before merge. +- **Humans stay in the loop and remain accountable.** The AI review is the + baseline, not the final word: an engineer interprets its findings, MAY + override a finding with a stated reason, and owns the merged change. This is the + same implementer-owns-quality rule the [Quality Gate](/quality-gate) states — + verification is planned at design, not handed to a separate stage. This is + cooperation, not delegation. +- **Human review is welcome but not the enforced default.** A human reviewer MAY + review any change and SHOULD on the changes where judgment matters most (novel + design, security-sensitive surfaces, wide blast radius). What OSBR does *not* do + is make a human reviewer the mandatory gate that every change waits on — the AI + review is that gate. +- **AI agents are first-class authors and are reviewed to exactly the same bar.** + The human who merges an agent's change owns it; a large, green, agent-authored + diff is not self-justifying (§3-5). + +## 3. Practices + +### 3-1. Run the AI review when a change is declared `Impl Review` + +When a change moves to `Impl Review` on the board, the engineer runs the AI +review from the agent on their own machine — the `/code-review` plugin, plus the +`asvs-audit` plugin (osbrjp/DevTool) where the change touches a +security-sensitive surface. It runs **alongside** the CI production-simulation +gate — a change merges only when both are satisfied. + +- The two gates are complementary and MUST NOT be collapsed into one: CI proves + the change *runs* correctly against a production-like environment (see the + [CI/CD Pipeline](/ci-cd-pipeline) and [Testing Standards](/testing-standards)); + the AI review judges whether the change is *built* correctly, safely, and + legibly. A green test suite is not a substitute for review, and a clean review + is not a substitute for tests. +- Declaring `Impl Review` without running the review is declaring it early: no + change leaves `Impl Review` unreviewed, so the guard is present on every + change — by workflow, not by a reviewer remembering to look. + +### 3-2. Guard three concerns, in priority order + +The reviewer — human or AI — guards three concerns, and the order is a +tie-breaker when effort or attention is finite: + +1. **Correctness (first priority)** — the code does what it is meant to, **and no + more.** This is judged against **KISS** (the simplest thing that works), + **DRY** (one source of truth; reuse what already exists rather than + re-implementing it), and **YAGNI** (build only what is needed now — no + speculative abstraction, no "for later" scaffolding). Over-engineering, bloat, + and code that does not serve the stated purpose are **findings, not neutral + background.** An interface with one implementation, a factory for one product, + config for a value that never changes — each is a correctness finding even when + the code runs green. Every part must earn its place. +2. **Security (second priority)** — injection, secrets in code, authorization + gaps, unsafe defaults, and the input/output handling an application-layer + review should catch. This is the OWASP/ASVS-class lens; the authoritative depth + lives in the [Application Security](/application-security) standard and the + [Security Policy](/security-policy), and the reviewer applies it on every + change rather than deferring it to a separate audit. +3. **Readability (third priority)** — an engineer can read and understand the code + **with or without AI.** Legibility to an unaided human is the floor: clear + naming aligned to the project's ubiquitous language, small and obvious + structure. Being easy for an AI to update follows from that, but a human being + able to follow it *without* AI is the bar that must hold. + +### 3-3. Findings are concrete, cited, and severity-gated + +- The reviewer MUST produce **concrete, cited findings** — file and line, with a + proposed fix — not a prose verdict. A finding a reader cannot locate and act on + is not a finding. +- A finding **above the agreed severity** blocks merge until it is resolved or + **explicitly waived with a recorded reason.** The waiver is the human staying in + the loop (§2): an override is a decision on the record, never a silent skip. +- Findings below the threshold are advisory: surfaced for the author's judgment, + not gating. + +### 3-4. The single-sentence standard + +The whole bar is one sentence a reviewer — human or AI — can hold in mind: + +> **Correct and minimal (KISS / DRY / YAGNI), safe, and legible to a human with +> or without AI.** + +A change that adds bloat, regresses safety, or is only followable with AI +assistance is a finding — **even when it is functionally working.** "It passes" is +necessary, not sufficient. This mirrors the [Testing Standards](/testing-standards) +stance that a green result is evidence to interpret, not a goal to farm. + +### 3-5. A large green AI-authored diff is not self-justifying + +OSBR embraces human ⇄ AI cooperation, and agents are productive authors. That +productivity carries the same trap the [Testing Standards](/testing-standards) +name for generated tests: **a large, passing, AI-authored change can look like +health while hiding bloat, an unsafe default, or logic no human has actually +followed.** + +- The merging human MUST review an agent's change against this policy exactly as + they would a human's — the AI review gate applies to AI-authored code too, and a + passing gate is not a reason to skip the human's own read. +- Volume is never the signal. We judge a change by the three concerns of §3-2, not + by how much code it adds or how quickly it was produced — and the ease of + generating code makes that discipline *more* important here, not less. + +### 3-6. The token cost is an accepted cost + +Running an AI review on every change costs tokens. That cost is **accepted, not +minimised away.** The correctness, safety, and legibility it buys — for both the +humans and the AI agents who will maintain the code — is worth more than the +tokens. A gate that runs only sometimes, to save cost, is not the every-change +guard this standard requires. + +## 4. Relationship to human review + +OSBR reconciles two opposed positions rather than picking one. Removing default +review entirely — trusting policy-grounded generation plus each developer's own +QA — leaves changes unguarded when that self-QA lapses. Mandating a human +reviewer on every change makes the guard hostage to reviewer availability and +turns review into a bottleneck. OSBR takes a third path: **keep a review gate, +and make it an AI review run at `Impl Review`**, so the guard is present on every +change (unlike "no review") without waiting on a human's calendar (unlike +"mandatory human reviewer"). Human review stays available and welcome (§2); it is simply not +the enforced default — the AI review is, and the human's accountability sits on +top of it. + +## References + +**Design principles the correctness lens applies** + +- KISS / DRY / YAGNI, "You Aren't Gonna Need It" — +- Andy Hunt & Dave Thomas, *The Pragmatic Programmer* (DRY — one source of truth) — + +**Security lens** + +- OWASP Application Security Verification Standard (ASVS) — +- OWASP Top Ten — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the AI code review this standard implements. +- [Security Policy](/security-policy) — the org-level security stance the review's security lens enforces. +- [Application Security](/application-security) — the OWASP/ASVS-class depth behind §3-2's second concern. +- [Testing Standards](/testing-standards) — the complementary evidence gate; the "green is not self-justifying" discipline. +- [CI/CD Pipeline](/ci-cd-pipeline) — the production-simulation gate the review runs alongside. +- [Development Guide](/development-guide) — the pull-request workflow this review gates. +- [Coding Style Guide](/style-guide) — the ubiquitous language and RFC 2119 levels the readability lens leans on. diff --git a/doc/cost-estimation.md b/doc/cost-estimation.md new file mode 100644 index 0000000..256544e --- /dev/null +++ b/doc/cost-estimation.md @@ -0,0 +1,248 @@ +# Cost Estimation + +This is the standard OSBR holds its cost and effort estimates to: how we +produce them, how we express them, and how we record them. It sits next to the +[Development Guide](/development-guide)'s Planning & Shaping work and the [IT +Investment Evaluation](/it-investment-evaluation) guide — those describe how we +shape work and judge whether it is worth doing; this describes *how we put a +number on doing it*, and how honest that number is allowed to be. Estimates are +part of the reviewable surface the [Quality Gate](/quality-gate) holds work to, +not a private guess that skips review. + +An estimate is a communication to the client that serves a decision — go/no-go, +scope trade-off, sequencing, budget approval — not a promise extracted from us. +A number with no traceable reasoning behind it, or a single figure that hides +how little we know, is not honest work. It fails **Be Nice** (we set the client +up to be surprised), **Be Kind** (we let a teammate inherit a commitment nobody +could keep), and **Be Strong** (we did not do the hard thinking). Humans and AI +agents estimate here as collaborators — and that partnership carries a specific +hazard this policy names head-on (§3-4). + +## How to read this policy + +* **Requirement levels** follow RFC 2119. **MUST** / **MUST NOT** are absolute. + **SHOULD** / **SHOULD NOT** state a strong default overridable only with a + documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts a published estimation practice, the + practice is named inline and cited under [References](#references) — chiefly + Steve McConnell's *Software Estimation: Demystifying the Black Art*. We adopt + the *criteria* of these practices and right-size them for an SME. Deviations + are allowed but must be deliberate and justified in the project's design notes + and the meeting record. + +[[TOC]] + +## 1. Goal + +Produce estimates a client can trust *because* they can see the reasoning, and +that a team can be held to *because* the uncertainty in them was stated up front +rather than discovered later. Concretely, every OSBR estimate must: + +1. **Carry a traceable breakdown** — a reader can see what tasks the number is + composed of, not just the total. +2. **State scope explicitly** — what is included, and just as importantly what is + *excluded*. +3. **Express uncertainty as a range**, never a single figure presented as fact. +4. **Budget for AI-productivity variance** — the same task costs very differently + with and without agent scaffolding, and the estimate must say which world it + assumes. +5. **Be recorded in the meeting record** as an agreement, with its assumptions + attached. + +The point is never false precision. "Seven days" is a worse answer than "five to +twelve days, most likely eight, assuming X" — because the second one is true. + +## 2. Responsibility + +- The **estimator** (the engineer scoping the work) builds the breakdown, states + the assumptions and scope boundaries, gives a range rather than a point, and + flags which AI-scaffolding assumption applies. They own the honesty of the + number. +- The **reviewer** (a second engineer) sanity-checks the breakdown and the range + independently before it reaches the client. Estimates are reviewed like code — + the same AI code review the [Quality Gate](/quality-gate) requires. +- The **project lead** ensures the estimate reaches the client as a range with + its assumptions intact, and that the agreement and its assumptions land in the + meeting record. They own that we do not quietly collapse the range to its low + end under pressure. +- The **whole team** feeds actuals back so future estimates improve. An estimate + nobody checks against reality is a guess we keep repeating. + +## 3. Practices + +### 3-1. Every estimate carries a traceable breakdown + +- Estimates **MUST** be built **bottom-up** where the work is understood: + decompose into tasks small enough to reason about (McConnell's guidance: aim + for pieces of roughly 1–3 days). Decomposition itself reduces error, because + independent over- and under-estimates partially cancel. +- Where the work is *not* yet understood well enough to decompose, use + **analogous estimation** — size it against a comparable past OSBR project — and + **say so**. An analogous estimate is a legitimate early-phase tool; passing one + off as if it were bottom-up is not. +- The breakdown **MUST** be preserved, not discarded once a total is reached. The + client — and the next engineer — is entitled to see the parts. +- A bare total with no visible composition **MUST NOT** be sent to a client. + +### 3-2. Scope inclusions and exclusions are explicit + +- Every estimate **MUST** list what it **includes** and what it **excludes**. + Silent scope is the single largest source of estimate disputes. +- Common exclusions to state explicitly when they apply: data migration, + third-party integration work, content population, non-functional hardening, + infrastructure provisioning, client-side review cycles, and post-launch + support. +- Assumptions **MUST** be written down beside the estimate — this is the + *explicit-assumptions discipline*. Every "we assume the API is documented / the + design is final / the client provides X by date Y" is a load-bearing condition. + When an assumption breaks, the estimate is void and is re-quoted; that is the + deal, and stating the assumption up front is what makes re-quoting fair rather + than a fight. + +### 3-3. Uncertainty is expressed as a range, not a single figure + +- Estimates **MUST** be given as a **range** (low–high), not a single number. A + single number is a claim to knowledge we do not have. +- The width of the range **MUST** reflect where we are on the **Cone of + Uncertainty** (McConnell): early in a project, estimates are legitimately off + by a factor of several in either direction; that spread narrows only as real + decisions are made and unknowns are closed. Early estimates therefore carry + *wide* ranges, and narrowing the cone is earned by doing the discovery work, + not by wishful confidence. +- For individual tasks with real uncertainty, use **three-point / PERT + estimation**: capture optimistic (O), most-likely (M), and pessimistic (P); the + expected value is `(O + 4M + P) / 6` and the spread `(P − O) / 6` gives a usable + sense of the risk on that task. Summing PERT expected values across a breakdown + gives a defensible project figure with a defensible spread. +- For whole-project figures, prefer **reference-class forecasting** over pure + bottom-up when comparable past projects exist: anchor on what *similar OSBR + projects actually cost*, then adjust. This counters the optimism bias and + planning fallacy that make inside-view bottom-up estimates systematically too + low. +- Presenting a range's low end alone, or averaging a range down to one number to + "look competitive," manufactures a surprise for the client later. That is a + violation of **Be Nice**, and we **MUST NOT** do it. + +### 3-4. Budget for AI-productivity variance + +AI coding agents change task cost dramatically — but *unevenly*, and mostly as a +function of whether the ground is prepared. This variance is a first-class term +in our estimates, not a footnote. + +- An estimate **MUST** state which world it assumes: **scaffolded** (agent-ready + — clear specs, existing patterns to follow, good test coverage, tight feedback + loops, established conventions in the repo) or **unscaffolded** (greenfield + ambiguity, no tests to check against, a novel domain, poor or absent + conventions). +- **Scaffolded work SHOULD be estimated lower**, reflecting realistic agent + leverage — but the range **MUST** still be a range, because agent output on a + bad day still needs human correction. +- **Unscaffolded work MUST NOT claim the scaffolded speed-up.** The agent + productivity gain is real where the scaffolding exists and largely absent where + it does not; assuming the best case on unprepared ground is exactly the + optimism bias §3-3 warns against. +- Where scaffolding *could* be built first (writing the specs, the tests, the + conventions) and would move the work into the cheaper world, the estimate + **SHOULD** surface that as an explicit option with its own cost — "N days + as-is, or M days to scaffold + cheaper thereafter." That is a real decision the + client is entitled to make. +- The AI-productivity assumption is itself an assumption under §3-2 and **MUST** + be recorded as one. + +### 3-5. Choose the method to fit what is known + +- **Bottom-up** when the work is decomposable and understood (§3-1) — most + accurate, most effort. +- **Analogous** when it is not yet, early in a project — fast, coarse, honest + about being coarse. +- **Reference-class** when comparable past projects exist — the strongest defence + against optimism bias at the whole-project level (§3-3). +- Use more than one method when the stakes justify it, and **compare**: + convergent estimates raise confidence; divergent estimates have found a hidden + assumption worth chasing before quoting. + +### 3-6. Story points, #NoEstimates, and where they fit + +- For **internal iteration planning**, teams **MAY** use relative **story + points** and velocity to forecast sprint throughput. Points measure relative + size for flow forecasting; they are an internal planning aid. +- Story points **MUST NOT** be handed to a client as a cost. Clients decide in + time and money, not in a relative unit only meaningful inside one team's + velocity. +- The **#NoEstimates** argument — that fine-grained upfront estimation is often + waste, and that slicing work small and shipping continuously forecasts better + than estimating — is a legitimate influence on *how we work* (thin vertical + slices, frequent delivery, empirical forecasting from actuals). But OSBR + clients contract for scope against budget, so a defensible **range-based + estimate with stated assumptions remains required** for client-facing + commitments. We take #NoEstimates' discipline without dropping the client's + right to a bounded number. + +### 3-7. Record the agreement in the meeting record + +- The agreed estimate — its **range**, its **breakdown**, its **scope + inclusions/exclusions**, its **assumptions** (including the AI-scaffolding + assumption), and the **method** used — **MUST** be captured in the meeting + record when it is agreed with the client. +- What is recorded is the **agreement**, not a single negotiated-down number + stripped of its conditions. If the range narrowed or scope changed in the + meeting, the record reflects *why*. +- When an assumption later breaks, the meeting record is what makes re-quoting + straightforward and fair rather than a dispute over memory. This is the **Be + Kind** clause: it protects the teammate who inherits the project and the client + who signed off. +- Estimates **SHOULD** be revisited as the Cone of Uncertainty narrows; a + materially updated estimate is a new agreement and is recorded as one. + +## 4. Anti-patterns + +- **False precision** — "12.5 days." Presenting a single figure as if the + uncertainty were resolved. Give the range. +- **Silent scope** — a number with no stated inclusions/exclusions. Disputes live + here. +- **Ranges quoted at the low end** — sending "5 days" from a "5–12 day" estimate + to win the work. Dishonest by omission. +- **Best-case AI assumption on unprepared ground** — claiming agent speed-ups for + unscaffolded, ambiguous work. +- **Inside-view optimism** — bottom-up summing with no reference-class sanity + check on a project type we have history for. +- **Estimate as commitment** — treating an early, wide-cone estimate as a fixed + promise, then blaming the team when reality lands inside the range we stated. + +## References + +**Core** + +- Steve McConnell, *Software Estimation: Demystifying the Black Art* (Microsoft + Press, 2006) — Cone of Uncertainty, decomposition, estimate-vs-commitment, + method selection. + +**Techniques** + +- Three-point / **PERT** estimation — expected value `(O + 4M + P)/6`, spread + `(P − O)/6` (Program Evaluation and Review Technique). +- **Range estimation** — expressing estimates as intervals that reflect position + on the Cone of Uncertainty. +- **Reference-class forecasting** — the outside-view corrective to optimism bias: + Daniel Kahneman, *Thinking, Fast and Slow* (2011); Bent Flyvbjerg on the + planning fallacy and outside-view forecasting. +- **Bottom-up vs analogous estimation** — decomposition vs comparison-to-past- + project methods (McConnell, above). +- **Explicit-assumptions discipline** — assumptions recorded beside the estimate + as the basis for re-quoting when they break. + +**Agile / debate** + +- **Story points & velocity** — relative sizing for empirical iteration + forecasting. +- **#NoEstimates** — Vasco Duarte and others; the argument for small slices and + forecasting from actuals over fine-grained upfront estimation. + +**Related OSBR standards** + +- [Development Guide](/development-guide) — Planning & Shaping, iteration cadence + that internal forecasting feeds. +- [IT Investment Evaluation](/it-investment-evaluation) — judging whether the + work an estimate prices is worth doing. +- [Quality Gate](/quality-gate) — the AI code review that estimates, like code, + pass through. diff --git a/doc/data-protection.md b/doc/data-protection.md new file mode 100644 index 0000000..cc17902 --- /dev/null +++ b/doc/data-protection.md @@ -0,0 +1,561 @@ +# Data Protection + +This is the standard the [Quality Gate](/quality-gate)'s **Security** lens holds +work to whenever a system handles **personal data** — the data a user entrusts to +us. It sits alongside the [Security Policy](/security-policy) (which keeps that +data safe from attackers) and describes the other half of the duty: the user's own +rights over their data — to know what they agreed to, to see it, to take it, to +leave with it — and the discipline that makes those rights answerable years later +rather than promised in prose. It builds on the [Infrastructure Planning +Policy](/infra-planning-policy) (data residency, backups, durability) and the +[Database Guidelines](/database-guidelines) (schema and history design). + +This is an **engineering** standard: how we design schemas, record consent, keep +history, and send mail so that personal data is handled correctly by construction. +The user-facing **Privacy Policy** — the legal notice of what we collect and why — +is the organisation-level [Privacy Policy](/privacy-policy) standard; this page is +what the system must actually do so that policy is truthful. As everywhere in the +handbook, deviations are allowed but must be deliberate and justified in the +project's design notes. + +Data protection is where OSBR's values become load-bearing. **Be Nice**: the user +always knows what they agreed to and what we hold, and can retrieve both — and +anyone who comes after us, human or AI, can see how a record reached its current +state without having to guess. **Be Kind**: the user owns their data; we only ever +hold it on loan, so leaving must be as easy as joining, we never sneak a material +change past someone by burying it in "continued use," and we never quietly erase a +fact someone may later depend on. **Be Strong**: we build the audit trail before we +need it, so that under scrutiny — a regulator, a dispute, a breach, a corrupted +write — the truth is already recorded, tamper-evident, and reconstructable. Humans +and AI agents design and review these systems here as collaborators, held to one +bar. + +> **Silence is not consent, and the past is a fact, not a field.** If we cannot +> show a clear affirmative act against a specific version, we do not have consent. +> If we overwrite state in place, we are choosing — usually without deciding to — +> that history is worthless. It rarely is. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the [Coding Style + Guide](/style-guide). **MUST** / **MUST NOT** are absolute. **SHOULD** / + **SHOULD NOT** state a strong default overridable only with a documented reason. + **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an external standard or legal regime, it + is named inline and cited under [References](#references). OSBR's home regime is + Malaysia's **PDPA** (Personal Data Protection Act 2010, as amended in 2024); its + Japanese parent studio and international clients also bring Japan's **APPI**, the + EU's **GDPR**, and **CCPA/CPRA** into scope, and these rest on the same core + duties. We adopt the *criteria* — PDPA, APPI, GDPR, CCPA/CPRA, + SQL:2011, the email authentication RFCs — and right-size them for an SME; we do + not adopt the headcount behind a large organisation's compliance function. The + organisation-level notice these controls make truthful is the [Privacy + Policy](/privacy-policy). + +[[TOC]] + +## 1. Goal + +The goal of data protection at OSBR is that **personal data is handled correctly +by design, and every promise we make about it is one the system can actually +keep.** Concretely, for any personal data we hold, we can: + +- **Prove consent** — reconstruct, for that exact user against that exact wording + at that exact moment, what they affirmatively agreed to. +- **Honour the user's rights** — let them see all their data, take a portable copy, + and delete their account and data completely, as first-class product features and + not back-office favours. +- **Reconstruct the past** — answer "what did this record look like then?" because + state transitions were recorded as history, not overwritten into silence. +- **Hold only what we should** — collect the minimum, retain it for a documented + period, and keep it where it is legally allowed to live. +- **Reach the inbox with restraint** — send only mail a recipient expected and can + act on, without damaging the reputation every other message depends on. + +These capabilities are almost impossible to retrofit. They MUST be designed into +the very first user-data model, because a schema that never planned for deletion, +history, or consent versioning cannot grow them later — the history you need has +already been overwritten by the time you need it. + +## 2. Responsibility + +- **Whoever designs the first user-data schema owns the shape of protection.** + Define deletion semantics and cascade behaviour, the retention period and + category of each field, and the history structure for every stateful record — all + *at schema time*, before the first migration ships. "Add history/deletion later" + is a design defect, not a backlog item. +- **Every developer touching user data owns minimization.** Collect only what the + feature uses; do not add a column, log line, or third-party sync that widens what + we hold without a reason recorded in the design notes. Write state transitions as + appends, never as destructive updates. +- **The PR author who introduces an email trigger** owns justifying it, classifying + it, and confirming authentication and (where required) unsubscribe controls are in + place before it can send in production. +- **The reviewer** treats data-protection surface as reviewable: an unexplained + `send_email(...)`, a schema with no answer for "where does the old value go?", or + a consent record with no version is a change that needs an owner and a reason — + refused the same way code without a security review is refused. +- **Legal** owns the wording of each ToS / Privacy Policy version, classifies every + change as material or non-material with a recorded rationale, and sets the + retention floor for records the law requires us to keep. +- **The project's technical owner** ensures export, deletion, retention, and history + are on the roadmap from v1, and audits actual retention against the published + privacy policy at least quarterly. +- **AI agents** are first-class contributors here and held to exactly the same bar; + the human who merges an agent's schema, migration, or consent flow owns it. + +Privacy is everyone's job, not a compliance team's — this is Cavoukian's principle +of protection **embedded into design** rather than bolted on afterwards. + +## 3. Practices + +### 3-1. Design data protection into the first schema + +Export, deletion, retention, minimization, consent versioning, and change history +MUST be decided when the first user-data model is designed, not deferred. For every +table holding personal data, the design notes MUST answer, before the first +migration ships: what category each field is, its retention period, what happens to +it on account deletion (hard delete, anonymise, or retain under a lawful +exception), and where else the data flows. A schema with no answer for "what +happens to this row when the user leaves?" is not finished. + +### 3-2. If a record holds state, history is a requirement — not an enhancement + +For any record that carries status, state, a mutable classification, a price, a +policy version, or any field whose *previous* value could later be asked about, a +change-history structure MUST be designed in from the start. + +- The design MUST make it possible to answer, for any record: **what its state was + at an arbitrary past instant**, and **what sequence of changes** produced the + current state. +- Each recorded change SHOULD capture at minimum the **entity identifier**, the + **new state** (or delta), a **valid-from timestamp**, the **actor / cause**, and — + where the history feeds audit — enough context to explain *why* it changed. +- "We'll add history when we need it" MUST be treated as a design defect: by the + time you need it, the history you need has already been overwritten. If history is + genuinely not required for a record, that decision MUST be recorded in a schema + comment or design note, so it is a choice and not an omission. + +### 3-3. Choose a history structure that fits the record + +There is more than one well-established way to keep history. Engineering MUST choose +deliberately from these (or a documented equivalent) rather than default to +overwrite because it is less code today. All are appropriate; the wrong choice is +*none*. + +| Structure | Keeps | Best when | Anchor | +| --- | --- | --- | --- | +| **Append-only / immutable audit log** | One immutable row per change (who/what/when), separate from the live row | You need a tamper-evident trail alongside a normal mutable table | WORM / audit-log discipline | +| **System-versioned temporal table** | Every row version with `[valid-from, valid-to]` system time, queryable "AS OF" a past instant | The database can own history transparently and you want point-in-time SQL | **SQL:2011** system-versioned tables | +| **Bi-temporal** | Two timelines — *valid time* (when the fact was true) and *transaction time* (when we recorded it) | The real-world effective date differs from when we learned it (back-dated corrections, retroactive consent) | **SQL:2011** bi-temporal | +| **Slowly Changing Dimension (Type 2)** | A new row per change with effective/expiry dates and a "current" flag | Analytical / warehouse dimensions reporting over historical attribute values | Kimball **SCD Type 2** | +| **Event Sourcing** | The full sequence of state-changing events; current state is a *derived projection* | State is inherently a sequence of business events and the event log is the source of truth | Fowler / Greg Young **Event Sourcing** | + +Whichever is chosen, the **previous value MUST remain retrievable** after a change. +A change that leaves no recoverable prior state is an overwrite, whatever it is +called. Corrections SHOULD be new records that supersede, not edits that erase. + +### 3-4. Derive "current", don't destroy "past" + +The current state of a stateful record MUST be **derivable** from its history, not +maintained by destroying the history. + +- "Current" is a **projection / replay** over the events, or the row with the latest + `valid-from` (or `is_current = true`) — computed, not carved out by deletion. +- A denormalised current-state column or table is permitted as an **optimisation**, + but it MUST be reproducible from the history and MUST NOT be the only place the + fact lives. +- Withdrawals, reversals, and cancellations MUST be recorded as **new** entries (a + `cancelled` event, a superseding row), never as deletion of the prior entry. That + something *was* active and then cancelled is itself history worth keeping. + +### 3-5. Make history queryable, not just retained + +Retained history no one can query is a liability, not an asset — it costs storage +and answers nothing. + +- Each history structure MUST carry the **temporal join keys** needed to reconstruct + a point in time: a stable entity id plus the time bounds + (`valid-from`/`valid-to`, effective/expiry dates, or event sequence + timestamp). +- Point-in-time reconstruction ("state AS OF date D") SHOULD be a documented, tested + query — not a one-off archaeology exercise when a dispute lands. +- Where the database offers it natively (SQL:2011 `FOR SYSTEM_TIME AS OF`, or engine + equivalents), prefer the native mechanism over hand-rolled history tables: less + code, and harder to get subtly wrong. + +### 3-6. Data minimization + +The least data we can hold is the safest data — data we never collected can never +leak, never needs exporting, and never needs deleting. + +- Collect only fields the feature **actually uses** (PDPA General Principle; the + APPI's purpose-of-use limitation; GDPR Art. 5(1)(c)). Do not collect "just in + case." +- Prefer not storing personal data at all where a design allows it — derive rather + than store, reference rather than copy. +- Logs, analytics, and error reports are personal data when they carry user + identifiers: minimize and set retention on them too, consistent with the + [Security Policy](/security-policy)'s treatment of logs as protected assets. + +### 3-7. Documented retention, audited against the running system + +Retention MUST be a decision on the record, not an accident of "we never delete +anything" (PDPA **Retention** Principle; the APPI's retention limits; GDPR Art. +5(1)(e), storage limitation). + +- Each category of personal data MUST have a **documented retention period** and a + reason. "Indefinite" is a choice that must be justified, not a default. +- Retention SHOULD be **enforced automatically** — a scheduled job that deletes or + anonymises data past its period — not left to manual cleanup that never happens. +- Retention periods MUST be reflected truthfully in the user-facing privacy policy + and **audited against the running system at least quarterly**. If the database + keeps data longer than the policy promises, one of the two is wrong — fix it. +- History (§3-2) is bounded by **two independent floors**: keep *at least* as long + as law and audit require (consent, financial, personal-data-change records), and + keep *no longer* than minimization allows for personal data. Neither "keep + everything forever" nor "overwrite immediately" is automatically correct; design + the window to satisfy both. + +### 3-8. Self-service data export (portability) + +A user MUST be able to export their own data themselves, without emailing support +and without a developer running a query (PDPA **Access** Principle and the +**data-portability** right added by the 2024 Amendment; the APPI's 開示 access +right; GDPR Art. 20). + +- Export MUST be **portable and machine-readable** — JSON or CSV, documented schema, + UTF-8 — per the Art. 20 standard of "structured, commonly used and + machine-readable." +- Export SHOULD cover **all** data tied to the user — content they created, settings, + retained activity — matching the access right of GDPR Art. 15, not just their + profile. +- Export SHOULD be self-service in the product UI. Large exports MAY be generated + asynchronously and delivered via a time-limited, authenticated link. +- The export path MUST authenticate the requester as the data subject — an + unauthenticated export endpoint is a data-exfiltration endpoint. A portable export + is the difference between a user *choosing* to stay and a user *trapped* into + staying; make the door visible. + +### 3-9. Complete account and data deletion (erasure) + +A user MUST be able to delete their account and have their personal data actually +deleted (PDPA **Access** Principle and the right to withdraw consent; the APPI's +利用停止・消去 cease-use/erasure right; GDPR Art. 17; CCPA/CPRA right to delete). + +- **Deletion semantics and cascade MUST be defined at schema time** (§3-1): for every + table, decide up front whether the row is hard-deleted, anonymised, or retained + under a lawful exception, and wire the foreign-key cascade / cleanup job to match. +- Deletion MUST propagate to **all copies**: primary store, caches, search indexes, + analytics, and any third-party processor the data was shared with. Enumerate these + at design time — a copy you forgot about is a copy you did not delete. +- **Backups** are the documented exception: personal data MAY persist in encrypted + backups until they age out on normal rotation, provided the window is documented + and the data is not restored into live use. +- Where full erasure is impossible because a lawful obligation requires a record + (e.g. financial/transaction records), **anonymise** rather than retain + identifiable data, and record which exception applies. +- Deletion SHOULD be self-service, with a clear confirmation and a stated completion + timeframe. A short, cancellable, disclosed grace period (to guard against account + takeover or misclicks) is acceptable. + +### 3-10. Data subject request (DSR) workflow + +Even with self-service, some access/deletion/portability requests arrive by other +channels. Have a defined path for them. + +- There MUST be a known, documented way to receive and fulfil an access, deletion, + or portability request, with a **named owner**. +- Requests SHOULD be fulfilled within the statutory window (GDPR: **one month**; + CCPA/CPRA: **45 days**), and the requester's identity MUST be verified before data + is handed over or destroyed. +- Wherever possible the DSR workflow SHOULD reuse the *same* export and deletion + machinery as the self-service features — one code path, not a fragile manual side + channel. + +### 3-11. Data residency and sovereignty + +"Data sovereignty" also means honouring *where* the data is legally allowed to live +— the PDPA's **cross-border transfer** rule (under the 2024 Amendment, transfer only +to a place with substantially similar or an adequate level of protection, or on +another permitted ground such as consent), the APPI's cross-border-provision rule, +and the GDPR's Chapter V transfer safeguards. + +- Know and document **which region** personal data is stored and processed in, and + keep it consistent with client and legal requirements — this extends the residency + note in the [Infrastructure Planning Policy](/infra-planning-policy). +- When a client or jurisdiction requires data to stay in a region, that constraint + MUST be reflected in the infrastructure, not just promised in prose. + +### 3-12. Version every legal document with a persistent identifier + +Each ToS and Privacy Policy MUST carry a **persistent, immutable version +identifier**. A published version is frozen — never edited in place; a change +produces a **new version**. + +- Each version MUST record a stable version id (e.g. `privacy-policy@2026-07-15` or + a monotonic `v7`), its **effective date**, the **full text** (or a content hash), + and the **classification** of the change relative to the previous version (§3-14). +- Superseded versions MUST remain retrievable forever: a user who consented to `v5` + MUST be able to retrieve the exact `v5` text they saw. +- The identifier MUST be the join key between the consent event (§3-13) and the + document text. A consent record pointing at "the Privacy Policy" with no version is + **not** a valid record — the PDPA **Notice and Choice** Principle and GDPR Art. + 7(1) put the burden on us to *demonstrate* consent, which is impossible for wording + we can no longer reproduce. + +### 3-13. Record each consent as an immutable event + +Every act of consent MUST be written to an **append-only, immutable audit trail** as +a discrete event — a specific application of the history discipline in §3-2 to §3-5. +The event is a fact about the past; it is never updated or deleted. + +Each consent event MUST capture at minimum: + +- **User** — a stable subject identifier. +- **Policy type** — e.g. `terms-of-service`, `privacy-policy`, `cookie-consent`, or a + specific processing purpose. +- **Policy version** — the persistent identifier from §3-12 (which exact wording was + shown and agreed to). +- **Timestamp** — a trusted, timezone-explicit UTC timestamp of the affirmative act. +- **Consent action** — `granted` or `withdrawn` (withdrawal is a new event, never a + deletion of the grant). +- **Method / context** — how consent was captured (checkbox on signup, re-consent + modal) and, where practical, the collection point, so the record shows a **clear + affirmative action**. + +Rules: + +- Events MUST be **immutable**; corrections are appended, never rewritten. The store + SHOULD be append-only / WORM-style (hash-chained or write-once) so tampering is + detectable. The current consent state of a user is *derived* by replaying their + event stream (§3-4), not stored as an overwritable flag. +- **Withdrawal MUST be as easy as granting** (PDPA right to withdraw consent; GDPR + Art. 7(3)) and MUST itself be recorded as an event. +- The consent request MUST be **clearly distinguishable** from other matters — not + buried inside unrelated ToS acceptance (GDPR Art. 7(2)). This is how Consent + Management Platforms operate under the IAB TCF: consent encoded per-purpose and + per-vendor against a versioned specification, not a single opaque yes. + +### 3-14. Classify changes: material vs non-material + +Every new version MUST be classified by Legal before publication, with the rationale +recorded alongside the version. + +- **Non-material change** — wording clarifications, typo fixes, restructuring, + contact-detail updates: anything that does **not** alter what data is collected, + the purposes, legal basis, recipients, retention, user rights, or cross-border + transfers. Non-material changes MUST be **notified** to users but do **not** require + fresh consent. +- **Material change** — any change to the scope or substance of processing: new + categories of personal data, new purposes, new recipients or sharing, new + international transfers, changed retention, changed legal basis, or any expansion of + what the user is agreeing to. A material change **invalidates prior consent for the + changed scope** and MUST trigger re-consent (§3-15). + +When in doubt, classify as **material**. The cost of over-classifying is one extra +prompt; the cost of under-classifying is processing personal data without a valid +legal basis. **Be Kind** — err toward asking again. + +### 3-15. Force re-consent on material changes — never rely on "continued use" + +On a **material** change, OSBR MUST obtain **fresh, affirmative consent against the +new version** before continuing to process personal data under the new scope. + +- OSBR MUST **block or limit access** to force re-consent — e.g. an interstitial the + user must actively act on. Access to the changed processing is gated until a new + consent event (§3-13) is recorded against the new version. +- OSBR MUST NOT treat **continued use, silence, inactivity, or a pre-ticked box** as + acceptance. Consent requires a **clear affirmative act** (GDPR Recital 32; Art. + 4(11) — freely given, specific, informed, unambiguous). This is the same principle + the ePrivacy Directive applies to cookies: prior, informed, affirmative consent, + not opt-out-by-continuing. +- The re-consent flow MUST show, or clearly link to, **what changed**, so consent is + **informed**. +- Gating SHOULD be **proportionate**: limit access to the affected feature or + processing, not the user's own data or account. A user who declines MUST retain the + ability to access and export their existing data (§3-8) and to withdraw. +- For non-material changes, **notice** is sufficient and MUST still be logged. + +### 3-16. Notice at collection + +At the point personal data is first collected, OSBR MUST present a **notice at +collection** identifying the categories of data and the purposes, and link to the +current Privacy Policy version (the PDPA **Notice and Choice** Principle; the APPI's +purpose-of-use notice; CCPA/CPRA notice-at-collection; the *informed* limb of GDPR +consent). The version shown at collection MUST match the version recorded in +the consent event. + +### 3-17. Justify and review every email trigger + +An email trigger is any code path that sends a message to a person. Each new or +changed trigger MUST be described in the PR that introduces it: what event fires it, +who receives it, why it needs to exist, and how often it can fire per recipient. A +trigger that cannot be justified in a sentence should not ship — reviewers treat an +unexplained `send_email(...)` the way they treat an unexplained network call. + +### 3-18. Classify transactional vs marketing + +Every trigger MUST be classified as **transactional** or **marketing / +notification**, because the classification determines the legal and consent rules +that apply. + +- **Transactional** — a message sent in direct response to an action or relationship + the recipient initiated: password resets, receipts, security alerts, booking + confirmations, account notices. These generally do not require prior opt-in but MUST + still identify the sender honestly and carry no marketing content, or they lose + transactional status. +- **Marketing / notification** — anything promoting a product, feature, + re-engagement, digest, or optional update. These require consent and unsubscribe + controls (§3-20, §3-21). + +The **primary purpose** of a message, not its label, decides classification (the +CAN-SPAM "primary purpose" test): a "receipt" padded with promotions is a marketing +message. When a message mixes purposes, the stricter rule wins. + +### 3-19. Authenticate the domain before the first production send + +The sending domain MUST have **SPF**, **DKIM**, and **DMARC** configured and verified +*before* any production email is sent from it — not after deliverability problems +appear. Unauthenticated mail is filtered, spoofable, and increasingly rejected. + +- **SPF** — publish the sending service's hosts in a DNS TXT record so receivers can + verify the envelope sender is authorised. +- **DKIM** — sign outgoing mail with a domain key so receivers can verify it was not + altered and genuinely came from the domain. +- **DMARC** — publish a policy telling receivers what to do when SPF/DKIM fail and + where to send aggregate reports. Start at `p=none` to observe, then move to + `p=quarantine` / `p=reject` once aligned. +- **BIMI** *(SHOULD, once DMARC is enforced)* — publish a verified brand logo; + requires an enforced DMARC policy and, for most providers, a Verified Mark + Certificate. + +Bulk senders MUST additionally meet the **Google & Yahoo 2024 bulk-sender +requirements**: authenticate with SPF and DKIM, publish a DMARC policy, keep reported +spam rates below the 0.3% threshold, send from aligned domains, and provide one-click +unsubscribe (RFC 8058) honoured within two days. + +### 3-20. Send through a managed service + +Production email MUST go through a managed email service provider (ESP) — never a +self-operated SMTP server or a raw library talking directly to recipient MX hosts. +Managed providers give us authenticated sending, reputation monitoring, and +bounce/complaint handling a hand-rolled sender does not. Bounces and complaints MUST +feed a **suppression list** so we stop mailing addresses that hard-bounce or +complain; ignoring them is the fastest way to lose sender reputation and get all our +mail — including transactional — filtered. + +### 3-21. Unsubscribe and preference management before any marketing send + +No marketing or notification email may be sent in production until unsubscribe and +preference management are in place. Every such message MUST include a clear, working +unsubscribe mechanism that takes effect promptly and requires no login or reply. This +is both an OSBR courtesy and a hard legal requirement in every market we operate in: + +- **CAN-SPAM (US)** — a visible, working opt-out honoured within 10 business days, + honest headers and subject lines, and a physical postal address. +- **特定電子メール法 (Japan)** — an **opt-in** regime: advertising email may be sent only + to recipients who consented in advance, with consent records kept, sender + identification present, and an opt-out path in every message. +- **GDPR (EU/EEA)** — marketing to individuals requires a freely given, specific, + informed, unambiguous opt-in, records proving it, and withdrawal as easy as it was + given (§3-13). + +Where these regimes overlap for a recipient, apply the strictest: default to opt-in, +keep consent records, and make opting out trivial. + +### 3-22. Protect deliverability and sender reputation as a shared asset + +Sender reputation is domain-wide: a noisy marketing trigger degrades delivery of +password resets and receipts too. Send only wanted mail, keep volume and cadence +steady rather than spiky, warm up new sending domains/IPs, monitor bounce and +complaint rates against provider thresholds, and remove addresses that bounce or +complain. Reputation is earned slowly and lost in a single bad send. + +## 4. Design-time checklist + +Before the first user-data migration ships, the design notes SHOULD answer: + +- [ ] What personal data does this model hold, and what category is each field? +- [ ] What is the retention period for each, and why? Does the privacy policy's + stated retention match what the schema actually does? +- [ ] On account deletion, what happens to each table — hard delete, anonymise, or + retain under which exception? Is the cascade wired? +- [ ] Where else does this data flow (cache, index, analytics, third parties), and + how is each cleaned on deletion? +- [ ] How does a user export this data themselves, in a machine-readable format? +- [ ] For every field that holds **state / status / a mutable classification**: is a + history structure identified (§3-3), or is "no history required" recorded as a + deliberate decision? +- [ ] Is the **previous value retrievable** after any change, "current" **derivable** + from history, and are withdrawals/cancellations recorded as **new entries**? +- [ ] Does the history carry the **temporal keys** to answer "state AS OF a past + instant," and does retention satisfy both the **legal floor** and the + **minimisation ceiling**? +- [ ] For consent: is every record joined to a **persistent policy version**, written + **append-only**, and captured as a **clear affirmative act**? + +If personal-data state can be overwritten with no recoverable prior value, or consent +cannot be tied to an exact version, the design is not ready. + +## References + +**Privacy law.** The organisation-level notice these controls uphold is the +[Privacy Policy](/privacy-policy). + +*Malaysia* + +- Personal Data Protection Act 2010 (Act 709) and the seven Personal Data Protection Principles — General, Notice and Choice, Disclosure, Security, Retention, Data Integrity, Access — Personal Data Protection Commissioner / JPDP — +- Personal Data Protection (Amendment) Act 2024 (Act A1727) — mandatory breach notification, Data Protection Officer duty, data portability, cross-border transfer to jurisdictions with substantially similar / adequate protection, and "data user" → "data controller" — + +*Japan* + +- 個人情報保護法 (Act on the Protection of Personal Information, APPI) — Personal Information Protection Commission (PPC) — purpose-of-use specification, cross-border-provision rule, and 開示・訂正・利用停止 rights — +- 特定電子メール法 — 特定電子メールの送信の適正化等に関する法律 (Japan) — opt-in regime for advertising email. + +*EU / US / international* + +- EU General Data Protection Regulation (GDPR), Regulation (EU) 2016/679 — + - Art. 4(11) — definition of consent: freely given, specific, informed, unambiguous. + - Art. 5 — principles incl. minimization (5(1)(c)) & storage limitation (5(1)(e)) — + - Art. 7 — conditions for consent: demonstrate (7(1)), distinguishable (7(2)), withdrawal (7(3)). + - Art. 15 — right of access — + - Art. 17 — right to erasure — + - Art. 20 — right to data portability — + - Recital 32 — consent by a clear affirmative act; silence / pre-ticked boxes / inactivity do not constitute consent. +- California CCPA / CPRA (Cal. Civ. Code §1798.100 et seq.) — notice at collection, right to know, right to delete — +- ePrivacy Directive 2002/58/EC (as amended by 2009/136/EC), Art. 5(3) — prior informed consent for storing/accessing information on a terminal device. +- OECD Privacy Framework — collection limitation, purpose specification, use limitation, security safeguards — +- CAN-SPAM Act — 15 U.S.C. §§ 7701–7713; FTC *CAN-SPAM Act: A Compliance Guide for Business*. + +**Privacy engineering frameworks** + +- Privacy by Design — the 7 Foundational Principles (Ann Cavoukian) — +- ISO/IEC 27701 — Privacy Information Management System (PIMS) — +- ISO/IEC 29100 — Privacy framework — +- IAB Europe Transparency & Consent Framework (TCF) — per-purpose / per-vendor consent encoded against a versioned specification (the TC String). + +**History & temporal patterns** + +- Martin Fowler — *Event Sourcing* — +- Greg Young — Event Sourcing / CQRS — the event log as authoritative source of truth, read models derived from it. +- Martin Fowler — *Temporal Patterns* (Audit Log, Effectivity, bi-temporal model) — +- SQL:2011 (ISO/IEC 9075:2011) — system-versioned temporal tables & bi-temporal data; `FOR SYSTEM_TIME AS OF` point-in-time queries. +- Ralph Kimball — Slowly Changing Dimensions, Type 2 (*The Data Warehouse Toolkit*). +- Append-only / immutable audit log (WORM) — write-once, tamper-evident change records kept alongside the live table. + +**Email authentication & deliverability** + +- SPF — RFC 7208, *Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1*. +- DKIM — RFC 6376, *DomainKeys Identified Mail (DKIM) Signatures*. +- DMARC — RFC 7489, *Domain-based Message Authentication, Reporting, and Conformance*. +- One-click unsubscribe — RFC 8058, *Signaling One-Click Functionality for List Email Headers*. +- BIMI — *Brand Indicators for Message Identification* (AuthIndicators Working Group; requires enforced DMARC). +- Google — *Email sender guidelines* (2024 bulk-sender requirements); Yahoo — *Sender requirements & recommendations* (2024). + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Security lens this standard serves. +- [Security Policy](/security-policy) — protecting data from unauthorised access; protected-asset definitions. +- [Infrastructure Planning Policy](/infra-planning-policy) — data residency, backups, durability, least-privilege access. +- [Database Guidelines](/database-guidelines) — schema and history structure design. +- [Development Guide](/development-guide) — pull-request Specification section where email triggers and schema changes are justified. diff --git a/doc/design-guidelines.md b/doc/design-guidelines.md new file mode 100644 index 0000000..11e1d92 --- /dev/null +++ b/doc/design-guidelines.md @@ -0,0 +1,57 @@ +# Design Guidelines + +At OSBR we design the experience — how a product behaves in a person's hands — while its look and feel are shaped elsewhere. This page holds the standards for that behaviour: how the interface should respond, explain itself, and stay out of the user's way. It is the counterpart to the code [Style Guide](/style-guide), which governs how we write; this one governs how the thing we build feels to use. Where the Style Guide asks whether the code is sound, these guidelines ask whether the experience is reachable, self-evident, complete in every state, and free of traps. Getting this right is **Be Nice** in the interface — thinking wholeheartedly about the person on the other side of the screen. + +**In depth:** [Accessibility](/accessibility) · [Self-Explanatory +UI](/self-explanatory-ui) · [Modeless Design](/modeless-design) · [Interaction +Design](/interaction-design) + +[[TOC]] + +## Accessibility is the floor + +**Accessibility is a starting constraint, not a finishing touch.** We design it in from the first interaction, the same way we ask "does it work" from the first line of code. A control that cannot be reached by keyboard, or an image with no text alternative, is an unfinished feature — exactly as a broken button is — not a defect to be swept up in a later "accessibility pass" that never quite arrives. The reasoning is one idea: an interface that states its meaning and its operations explicitly is reachable by whoever shows up, because assistive technology consumes a product through its declared structure, not its pixels. Designing for the edges makes the centre better. + +**A published baseline keeps "accessible" a verdict, not a feeling.** We hold every product to [WCAG 2.2 AA](https://www.w3.org/TR/WCAG22/) as the minimum — the target is a product people actually find easy, and the floor is what we never ship below. We build from semantic HTML first, because native elements carry role, state, focus, and keyboard behaviour for free, and we reach for [WAI-ARIA](https://www.w3.org/WAI/ARIA/apg/) only to fill a gap no native element can express — never to re-create a native control on a bare `
`. And we verify with more than a linter: automated checks catch perhaps a third of issues and cannot tell us whether a flow makes sense announced aloud or driven by Tab alone. + +- We **MUST** meet **WCAG 2.2 AA** as the floor on every product, and treat a missing label or an unreachable control as a defect, not a nice-to-have. +- We **MUST** verify each UI-bearing feature **keyboard-only and with a screen reader** before it ships — every control reachable and operable, a visible focus indicator, no keyboard traps, and an announced name, role, and reading order that match the visual order. +- We **MUST** build from **semantic HTML first**, using ARIA only to supplement what no native element provides. +- We **SHOULD** review designs for keyboard order, focus, and non-visual meaning **before** implementation begins, so accessibility is scoped into the work rather than retrofitted onto it. + +## The interface explains itself + +**A finished screen is operable without documentation.** If a person needs a manual, a walkthrough video, or a guided tour to complete an ordinary task, the screen is doing less than its job and pushing the shortfall onto the user. Each screen carries the whole conversation: whatever it fails to say, the user must guess, ask, or look up — and every guess is a chance to get it wrong. So the design's job is to say it, on the control, in the user's own words, at the moment and place it is needed. A first-time user, given the screen and nothing else, should be able to tell what it is, what they can do, what is happening, and what to do next. + +**We speak the user's language, and let controls signal their use.** Labels, buttons, and messages use the words the user uses, not our internal jargon — if the domain term is "consignment", the button is not "Submit Record". Interactive elements look interactive; a control that relies on the user hovering or guessing that it is clickable has hidden itself. We write plainly — short sentences, common words, active voice — so a screen needs no glossary. And a reached-for onboarding tour is a signal, not a solution: it usually means a label, a signifier, or an empty state was skipped, and the honest fix is the screen underneath, not an overlay narrating it. A one-time hint that teaches an optional power-feature is fine; a tour required to do the ordinary task is a defect, because users skip tours, forget them, and arrive by deep link having never seen them. + +- We **MUST** write every label, action, and message in the **user's vocabulary**, reusing the product's established terminology. +- We **MUST** give every control a **signifier** that it is interactive and what it will do, and arrange controls so their layout mirrors what they affect. +- We **SHOULD** treat a needed **guided tour or coach-mark as a design finding** — investigate which of the practices above the screen skipped, and fix that instead of narrating over it. + +## Every state is designed + +**A screen designed only for "full of data and working" is a screen designed for the state users spend the least time in.** Every screen that fetches, shows, or accepts data lives in four states — **loading, empty, error, success** — and the three beyond the happy path are exactly where an undesigned screen abandons the user. Loading must show that something is happening, not a frozen screen or an ambiguous spinner that could equally mean "working" or "hung". An empty screen is a first impression, not a fault: it explains what belongs there and offers the single action to create the first item, never a blank void that reads as broken. Success must confirm the outcome, so the user is not left re-checking whether their action took. A screen shipped with only its success-with-data state designed is not finished — the other three get filled by browser defaults, blank space, and raw stack traces, which is to say by no design at all. + +**An error message is the screen speaking at the user's worst moment, so it says both halves: what happened and what to do.** "Something went wrong" states neither; "Card declined — check the number and expiry, or try another card" states both. The message sits next to its cause — a field error at the field, a form-level error where the eye already is — because distance makes the user hunt for what to fix. Better still, we prevent the error first: constrain inputs, disable impossible actions, confirm destructive ones. We never blame the user and never hide the failure; a silent failure is worse than a blunt one. + +**These four states are defined once, before the second screen, and reused everywhere.** A user should learn the product a single time — one loading treatment, one way an error looks and where, one shape for an empty list, one confirmation for a class of action. The first screen sets no precedent alone; the second is where consistency is either won or forked, because every later screen copies whichever way the divergence went. So we settle the states as shared components early, and a second list that invents its own near-identical empty state is a divergence even when it looks similar — "similar" is exactly what the user notices and mistrusts. For structured content with a known layout, a skeleton is the default: it preserves the page's shape and reads as "arriving", where a bare spinner reads as "stuck". + +- We **MUST** design all four states — **loading, empty, error, and success** — for every screen that fetches, shows, or accepts data, not the happy path alone. +- We **MUST** make every error message state **what happened and what to do**, in plain language, **adjacent to its cause**, and prevent the error at the source where we can. +- We **MUST** define the four states as **shared, reusable components before the second screen** consumes them, and reuse them rather than re-create near-duplicates. +- We **SHOULD** use one signal per meaning across the product — one treatment for a class of success, one placement rule per kind of error — and default to a **skeleton** over a spinner for content with a known layout. + +## Interaction is modeless and reachable + +**Modelessness is the default; a mode is the exception that must justify itself.** A mode is any state where the same action produces a different result depending on where the interface currently is — most visibly a modal dialog that seizes the screen and refuses every action but its own. Modes are a principal cause of user error, because the person acts on their intent while the system acts on its hidden state, and the two diverge. So editing, creating, filtering, and previewing happen inline, in a panel, or on their own page — somewhere the user can leave and return to. A modal is warranted only for an **irreversible confirmation** (a delete or permanent send, where a reversible action would be better served by undo than by a stop-and-confirm), a **single indivisible submission** that would be corrupted if left half-done in the background, or a **physically-exclusive interaction** that genuinely needs the full surface. If the interaction is none of these, it is not a modal — "it was easier to drop in a dialog" is the failure mode this guideline exists to catch — and when a modal is warranted, we record which case it falls under, so the exception stays auditable. + +**The place the user is in belongs in the URL, and every mode has more than one way out.** State held only in transient memory is a place with no address: the user cannot bookmark it, share it, reload into it, or step back out with the browser's own Back button — which is a modeless exit we get for free the moment the current place lives in the URL. When a mode must exist, leaving it is trivial and never punishes the user: closing preserves their in-progress input rather than eating the three fields they typed before pressing Escape by reflex. Cancelling is always safe, so it is effortless and available every way; only committing something irreversible earns a deliberate, guarded gesture. And a modal serves two audiences at once — we trap keyboard and screen-reader focus inside it so no one tabs out into inert background controls, while keeping that background visible and readable, because a person answering "delete this invoice?" should still be able to see which invoice. + +**A learned key does the same thing everywhere.** Keyboard behaviour is where inconsistency hides most invisibly and hurts most, because a keyboard or screen-reader user navigates entirely by learned conventions. We adopt the [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/) keyboard contract for each pattern rather than inventing our own bindings, and hold every instance of a pattern to the same keys — every menu to the same arrow-key movement, every dismissible surface to Escape. Focus follows reading order and is always visible. When a screen genuinely must depart from the standard, we record the deviation in our design system's interaction notes — an unrecorded deviation is indistinguishable from a mistake and gets copied as if it were the convention, and a deviation that recurs three times is a signal to update the standard, not to keep forking. + +- We **MUST** treat **modelessness as the default**, use a modal only for an irreversible confirmation, a single indivisible submission, or a physically-exclusive interaction, and record the justification when we do. +- We **MUST** hold view, selection, and "which item is open" state in the **URL** so a place is addressable, shareable, and survives reload, and so Back closes what Back should close. +- We **MUST** give every modal **at least two exits** — Escape and a background click, alongside any Close control — **preserve in-progress input** on close, and **trap focus** inside it while keeping the background readable and the dialog correctly announced (`role`, `aria-modal`, and a label). +- We **MUST** implement the **keys the WAI-ARIA Authoring Practices specify** for each pattern, use them identically across every instance, and keep focus visible and in reading order. +- We **SHOULD** reuse our design system's existing component, state, and tokens over a one-off variant, and **record every deliberate deviation** — with what differs and why — in the shared interaction notes. diff --git a/doc/development-guide.md b/doc/development-guide.md index 4eef552..ad33732 100644 --- a/doc/development-guide.md +++ b/doc/development-guide.md @@ -14,7 +14,7 @@ The following checklist is all mandatory by our security policy: * Sleep mode activation within 5 minutes, mandatory reauthentication after sleep mode. * Install antivirus software on Windows devices. * Prohibit keeping files permanently on the desktop. -* Prohibit displaying text in the browser’s bookmark bar. +* Prohibit displaying project or client names in the browser’s bookmark bar. * Configure proxy settings for verification and production environment testing. * Ask administrator for the proxy settings. @@ -64,13 +64,29 @@ principles plus per-language rules (TypeScript, Go, Python, HTML & CSS, Terraform), each tagged 🌎 industry-standard or 🏠 house-rule. Read it before your first pull request. -### 1-9. Database Guidelines +### 1-9. Design Guidelines + +Follow the [Design Guidelines](/design-guidelines) for how the experience +behaves: accessibility as the floor, screens that explain themselves, every +state designed, and modeless, reachable interaction. They are the counterpart to +the Style Guide — that governs how we write code; these govern how the thing we +build feels to use. + +### 1-10. Database Guidelines Follow the [Database Guidelines](/database-guidelines) when choosing a data store (SQL vs NoSQL) or designing a relational schema, including OSBR's SQL house style. The [Infrastructure Planning Policy](/infra-planning-policy) sets the higher-level infrastructure defaults these build on. +### 1-11. The Quality Gate + +Follow the [Quality Gate](/quality-gate): the three checks every change clears before it merges — that it is **reliable**, **secure**, and **sustainable**. One engineer, with their AI, holds all three as they build, and the gate holds at `Impl Review` on the board. + +### 1-12. AI Usage Guideline + +Follow the [AI Usage Guideline](/ai-usage-guideline) for how we work with AI: one engineer owning the whole of a piece of work with AI beside them, and the standards — data boundaries, provider resilience, day/night rhythm, policies-as-plugins — that keep AI-assisted work safe, resilient, and honest. + ## 2. Workflow Overview ### 2-1. Scrum-Like Agile Development @@ -89,11 +105,11 @@ Following practices characterize our agile style: * Work is broken down into short iterations, typically 1 week. * To ensure continuous delivery of value and frequent opportunities for feedback. -* CI/CD piplines have to be set up and automated first. +* CI/CD pipelines have to be set up and automated first. #### Brief Issues, Contextual Pull Requests -* **Issues** should be short-descripted to help the team: +* **Issues** should be briefly described to help the team: * Create small, manageable tasks that fit within a 1-week sprint. * Encourage the creation of more issues to track all identifiable tasks at any given moment. * Motivate project leaders to take ownership of issue creation instead of delegating it to team members. @@ -166,7 +182,7 @@ The following GitHub Actions are pre-configured in each repository. #### Status -This field belongs our standard project board. +This field belongs to our standard project board. | No | Status | Description | |----|---------------|---------------------------------------------------------------------------| @@ -174,9 +190,9 @@ This field belongs our standard project board. | 2 | Todo | Ready to be worked on specification. | | 3 | Spec Review | On a specification review before being 'In Progress'. Can skip if enough confident. | | 4 | In Progress | Currently being worked on implementation. | -| 5 | Impl Review | On a implementation review before being merged. | -| 6 | Shipping | Merged to 'main', issue closed, and ready to be shipped. | -| 7 | Done | Shipped and verified on the production environment. | +| 5 | Impl Review | On implementation review before being merged: by declaring this status the engineer takes on running the AI review from their own agent, then a human interprets its findings and owns the merge (see [Code Review](/code-review)). | +| 6 | Shipping | Merged to 'main' and ready to be shipped. | +| 7 | Done | Shipped and verified on the production environment; the issue is closed. | The following is a flowchart of the project status. @@ -250,6 +266,16 @@ Difficulty estimates the complexity of a task, which may arise from unclear spec Sprint is a period of time during which specific work has to be completed and made ready for review. It is usually 1 week long. +#### Repositories Are Not a Support Channel + +The issue tracker is the **engineering backlog** — reproducible defects, planned features, and technical debt the team owns and burns down on engineering time. It is not a helpdesk. Support requests — how-to questions, account problems, billing, "is this broken for me?" — run on a different clock and a different audience, and mixing the two buries real defects under noise while answering users on a cadence never designed for them. + +So before any product goes live it has a **dedicated, published support channel**, and the resolution path for a support request stays entirely inside it. When a request lands as an issue anyway, we **thank** the person, **redirect** them to the support channel with its URL, and **close** the issue — never keep it open under a standing `support` label. The only bridge from support into the tracker runs one way: when a support conversation surfaces a genuine defect, its owner opens a fresh, reproduced, engineer-written issue, and the user stays updated in the support channel. + +- We **MUST** give every product a nominated, published support channel before it goes live, and resolve support requests there, never through the issue tracker. +- We **MUST** handle a support request that arrives as an issue by thank → redirect → close, rather than parking it under a support label. +- We **SHOULD** re-file genuine engineering work found via support as a fresh, deduplicated, engineer-written issue — not a forwarded user thread. + ### 2-3. Weekly Planning All developers participate in the weekly planning meeting to discuss the progress of the project and plan the next week's work. @@ -292,6 +318,30 @@ Following is the typical agenda for the weekly planning meeting. * Encourage feedback on the meeting's structure or areas for improvement. * End with a positive note to motivate the team for the upcoming sprint! +### 2-4. Planning & Shaping + +Before a piece of work reaches the board as something we can build, it has to be shaped — turned from a customer's initiative into a committed, understood slice with a number against it and the riskiest part already tested. This is the pre-work phase, and it maps to the early board stages: `Todo`, where a request lands, and `Spec Review`, where we agree it is worth doing and know enough to do it. A week-long sprint gives us little room to discover halfway through that we were solving the wrong problem, so we spend the front of the effort making sure we are not. + +**We research the real need before we take a request at face value.** A stated request is a symptom and a hypothesis — evidence of a deeper business need, not the need itself. So we study the customer's world first: how their industry makes money, what people actually do all day versus the tidy process on the org chart, and the workarounds and shadow spreadsheets they have built to survive the current one. We trace each stated request down to the job beneath it — the progress the customer is trying to make, independent of any solution — and we validate that job against what we found. Serving the customer well sometimes means telling them, early and with evidence, that the thing they asked for is not the thing their business needs. That honesty is service, not cleverness at their expense. + +**We prove the risky part with something that actually runs.** Where an idea carries real uncertainty — whether it will pay off, whether it is what the customer needs, whether people will use it, whether we can even build it — we do not find out by building the whole thing and hoping. We find out cheaply first, with the smallest experiment that answers the question: a spike for a technical unknown, a thin walking skeleton for an end-to-end one, a proof-of-concept for a doubt about value. The experiment is time-boxed and carries a written question and a written "what result changes our plan." Its value is the lesson, not the code — a spike that fails in three days has saved a three-month bet, and that is a success. Experiment code lives in a clearly marked disposable space (a `lab/` or `spike/` area, a `spike:` comment) and is never quietly promoted into production; if the idea is proven, we budget the real build. + +**We size it honestly, and we judge it by the customer's return.** An estimate is a communication that serves a decision, not a promise squeezed out of us, so every estimate carries a visible breakdown, states plainly what it includes and excludes, and is expressed as a range rather than a single figure that hides how little we yet know. Because agents change task cost sharply but unevenly, an estimate says which world it assumes — scaffolded ground with clear specs and tests, or unprepared ground where that speed-up largely evaporates. Above the cost sits the prior question: for this customer's business, does the return justify the spend, and how sure are we? We rank proposals by their return to the customer, not by how large an order they would be for us; we state the expected effect and how certain we are of it; and we phase large spend into increments the customer can verify before funding the next, attacking the riskiest, highest-value part first. The agreed estimate and the accepted case — range, breakdown, assumptions, increment plan — land in the meeting record, so that when an assumption breaks, re-quoting is fair rather than a fight over memory. + +**We win and start work by demonstrated capability, not by reciting the past.** When a customer needs to believe we can do the work, we reach for the real thing at hand — a working proof-of-concept against their actual problem, the concrete reasoning behind a design decision, the running service they can click and try to break — before we reach for a list of past projects. A track record answers "were they any good before?"; a demonstration answers "are they solving *this* problem, right now?", and only the second keeps our motivation honestly tied to the customer's value. When we publish case studies to build reputation, we get consent first and keep the detail coarse — the shape of a solution, never the versions, endpoints, and topology an attacker probing the customer's systems would need. + +**We share one language across customer, code, and team.** The terms we harvest from the customer's world become the project's ubiquitous language — one authoritative word per concept, agreed with the domain experts and mirrored everywhere the concept appears. We search the existing terms before coining a new one, treat a second synonym for an existing idea as a defect rather than a style choice, and flag contradictions (two words for one thing, one word for two) as findings that usually mark a real boundary. When a concept is renamed, the rename is atomic — code, schema, tests, logs, and docs in the same change — so the old word leaves no landmine for the next reader, human or AI. + +- We **MUST** research the customer's industry, real workflow, and pain before treating a request as a specification, and trace every request down to the job it is trying to satisfy. +- We **MUST**, where real uncertainty exists, reduce it with the smallest time-boxed experiment that answers a written question — and keep that experiment disposable and out of production until the real build is budgeted. +- We **MUST** give every estimate a visible breakdown, explicit scope in/out, a range not a single figure, and a stated assumption about scaffolded versus unprepared ground. +- We **MUST** rank proposals by return to the customer rather than order size to us, state the expected effect and its certainty, and phase large spend into increments the customer can verify before funding the next. +- We **MUST** record the agreed estimate and accepted case, with their assumptions, in the meeting record. +- We **SHOULD** demonstrate capability with a runnable proof-of-concept, design reasoning, or the live service before citing past work — and publish case studies only with consent and with detail kept coarse. +- We **SHOULD** capture the field's own terms verbatim as the project's ubiquitous language, one word per concept, and hold that language consistent across the customer, the code, and the team. + +Each part of shaping has a full standard behind it: [Market Research](/market-research), [Requirements Modeling](/requirements-modeling), [Verify Before Building](/verify-before-building), [Cost Estimation](/cost-estimation), [IT Investment Evaluation](/it-investment-evaluation), [Legal Compliance](/legal-compliance), [Domain Terminology](/domain-terminology), and [Capability over Track Record](/capability-over-track-record). + ## 3. Tutorial diff --git a/doc/domain-terminology.md b/doc/domain-terminology.md new file mode 100644 index 0000000..1130574 --- /dev/null +++ b/doc/domain-terminology.md @@ -0,0 +1,202 @@ +# Domain Terminology + +Every OSBR project maintains a **ubiquitous-language dictionary**: one +authoritative word per domain concept, agreed with the domain experts and +mirrored everywhere the concept appears — code, schema, docs, tests, logs, and +error messages. This page is the per-project *discipline* for building and +holding that dictionary. + +It is **distinct from the org-wide [Technical Glossary](/technical-glossary)**. +The Technical Glossary defines cross-project *technical* vocabulary (DI, IaC, +Clean Architecture) so everyone at OSBR reads code the same way. This discipline +is *per-project* and *domain*-facing: the nouns and verbs of *this* product's +business — `Booking`, `Invoice`, `Ledger`, `settle`, `void` — the words a domain +expert would recognise. One is a shared reference book; the other is a living +project artifact you grow and defend as the model evolves. + +The words themselves are not invented at the keyboard. They are the language we +first hear in [Market Research](/market-research) and pin down while +[Requirements Modeling](/requirements-modeling) — the domain expert's own terms, +carried into the code unchanged. This page is where that discipline meets the +[Development Guide](/development-guide)'s **Planning & Shaping**: naming is +shaped with the model, not bolted on after. + +[[TOC]] + +## 1. Goal + +A codebase where **the same concept has exactly one name, and that name means +exactly one thing** — end to end. A new engineer, a domain expert, or an AI agent +reading any layer (a Postgres column, a Go struct, a log line, an error string) +encounters the *same* word for the *same* idea, and can therefore reason about +the system without a translation table in their head. + +This directly serves the OSBR human⇄AI principle: **one word per concept keeps +the codebase self-explanatory to humans and AI alike.** An AI agent editing our +code has no hallway to ask "is a `client` the same as a `customer`?" — it infers +meaning from names. Synonyms and drift are, for an agent, silent corruption of +the model; for a human, a 3am bug. + +This is **Ubiquitous Language** in the Domain-Driven Design sense (Eric Evans): +the language of the model, spoken by developers and domain experts, embedded +rigorously in the software. The dictionary is where we write that language down. + +## 2. Responsibility + +- **Every engineer** searches the dictionary before coining a term, and appends + to it when the domain reveals a genuinely new concept. +- **The person who renames a concept** owns updating *every* affected surface in + the same change (§3-5). +- **Reviewers** reject PRs that introduce a synonym, a modifier-laden alias, or + an identifier that diverges from the agreed word — naming is part of code + review, not a follow-up. +- **The whole team** treats the dictionary as the source of truth for the model's + vocabulary; disagreements about a word are resolved *with the domain expert*, + then written down, not re-litigated per PR. + +## 3. Practices + +### 3-1. One word per concept — maintain the dictionary + +- Keep a project **dictionary** (a `TERMS.md`, a docs page, or a section in the + domain model) listing each concept, its single agreed word, and a one-line + definition. This is **glossary-driven development** in Gojko Adzic's sense — the + glossary of business terms is a first-class deliverable that the specification, + the code, and the conversation all draw from. +- You MUST record a concept in the dictionary before it spreads across more than + one module. A word that only lives in three engineers' heads has three + definitions. +- The definition SHOULD be phrased so a domain expert would nod at it — not a + technical restatement. + +### 3-2. Search before you coin + +- Before introducing a new noun or verb, you MUST **search the existing terms** + (the dictionary first, then the codebase — `grep`, symbol search) for an + established word for that concept. +- If a word already exists for the idea, use it. If a *near* word exists but the + concept is genuinely different, the resolution is a conversation with the + domain expert to name the distinction — not a quiet second synonym. +- This is the cheap half of the old adage that the two hard things in computer + science are cache invalidation and naming things. Searching first is how we + stop the naming problem from compounding. + +### 3-3. No synonyms, no modifier-laden names + +- **One concept, one word.** `customer` / `client` / `account` for the same + entity is a defect, not a style choice. Pick one; the dictionary records which. +- Avoid **modifier-laden names** that encode uncertainty or overlap: `realUser`, + `actualTotal`, `finalFinalInvoice`, `newBookingData`. A qualifier that exists + only to disambiguate from a near-synonym is a signal the underlying concept is + unclear — fix the concept, not the label. +- A name that needs a compound of three nouns usually means the *ubiquitous + language itself* is too compound. A long, modifier-heavy identifier means the + domain vocabulary needs simplifying upstream: agree a simple, ideally + single-word term with the domain expert, and short identifiers follow for free. + +### 3-4. Identifiers mirror the ubiquitous language + +- Struct/type names, function names, variables, DB tables and columns, API + fields, config keys, event names, and log keys MUST use the dictionary word for + the concept they carry. +- **Naming is design, not decoration.** A well-named function reveals its + contract; a mis-named one hides a mismatch between the code and the model. Treat + a name you keep wanting to qualify as a design smell pointing at a missing or + wrong concept — the fix is usually to split or rename the concept, and the + better name falls out. +- Where a mechanical convention applies (singular entity, plural table; + `snake_case` in SQL; the per-language style guides), the *convention* shapes the + casing but the *word* still comes from the dictionary. + +### 3-5. Renaming is an atomic, whole-system change + +When a concept is renamed — because the domain expert corrected us, or the model +sharpened — you MUST update **every affected surface in the same change**: + +- production code and type definitions, +- database schema / migrations, +- tests and fixtures, +- **logs, metrics, and event names**, +- **error messages** and user-facing strings, +- docs, comments, and the dictionary entry itself. + +A rename that lands in the code but not the logs, or in the schema but not the +error text, re-creates the two-names-one-concept problem it was meant to kill — +and leaves a landmine for whoever (human or agent) next greps for the old word. +If a full atomic rename is genuinely too large for one PR, apply an +expand/migrate/contract discipline: introduce the new word, migrate every surface +across explicit steps, then remove the old — never leave both live indefinitely. + +### 3-6. Contradictions are findings, not footnotes + +A dictionary earns its keep by surfacing the two shapes of naming trouble, and +each marks a real boundary in the domain: + +- **Two words for one thing** — a synonym. Usually harmless-looking drift; resolve + it to the single agreed word (§3-3). +- **One word for two things** — a homonym. `settle` meaning both "mark an invoice + paid" and "close a dispute" is the more dangerous case: the shared word hides + that these are *different concepts*. Flag it, take it to the domain expert, and + split it into two words that each mean one thing. + +When you hit either, you MUST raise it as a finding — a note in the dictionary or +an issue — not paper over it. A contradiction between two names is the model +telling you where a boundary actually runs; naming it right sharpens the domain, +not just the code. + +## 4. Pinning subtle terms by example + +When a term is subtle, pin it down with a concrete example, not a longer abstract +definition — Gojko Adzic's **specification by example**. `A Booking is void once +the guest no-shows past the cutoff` teaches the word better than a paragraph, and +doubles as a test name. Examples in the dictionary keep humans and AI reading the +same meaning, and tie the vocabulary straight back to the scenarios captured +during [Requirements Modeling](/requirements-modeling). + +## 5. Quick Checklist + +Before merging, the author and reviewer confirm: + +- [ ] Any new concept has a dictionary entry (one word, one definition). +- [ ] No new synonym for an existing concept was introduced. +- [ ] No modifier-laden alias (`real*`, `actual*`, `*Data`, `new*`) papering over + a fuzzy concept. +- [ ] No word quietly carrying two meanings (§3-6) — homonyms raised as findings. +- [ ] Identifiers across code, schema, API, and events use the agreed word. +- [ ] Any rename updated code **and** tests **and** logs **and** error messages + **and** docs — in this change. + +## 6. OSBR Values in Practice + +- **Be Nice** — a clear, single, honest name is a gift to the next reader. Naming + things well is one of the kindest, least glamorous things we do for teammates. +- **Be Kind** — take the time to learn the domain expert's real word rather than + inventing a developer-convenient one. Meeting people in *their* language, and + writing it down so it lasts, is respect made durable. +- **Be Strong** — hold the line in review: reject the second synonym now, because + the drift you wave through today is the ambiguous model everyone fights for the + project's life. Do the whole atomic rename, not the convenient half. + +One word per concept is how we keep the codebase **self-explanatory to humans and +to the AI agents that read and write it beside us** — no glossary lookup, no +guessing, no drift. + +## References + +- Eric Evans — *Domain-Driven Design: Tackling Complexity in the Heart of + Software* (Ubiquitous Language) — +- Gojko Adzic — *Specification by Example* (glossary-driven / living + documentation) — +- Martin Fowler — Ubiquitous Language — +- Martin Fowler — "TwoHardThings" (naming things, cache invalidation) — + +**Related OSBR standards** + +- [Technical Glossary](/technical-glossary) — cross-project technical vocabulary, + the complementary reference to this per-project domain dictionary. +- [Requirements Modeling](/requirements-modeling) — where the domain's concepts + and their names are first captured. +- [Market Research](/market-research) — where we first hear the domain expert's + language. +- [Development Guide](/development-guide) — Planning & Shaping, and the + pull-request review where naming is held to the dictionary. diff --git a/doc/ethical-design-policy.md b/doc/ethical-design-policy.md new file mode 100644 index 0000000..d71d481 --- /dev/null +++ b/doc/ethical-design-policy.md @@ -0,0 +1,39 @@ +# Ethical Design Policy + +The people using what we build are the people we are here to serve. That single fact settles how we design: an interface earns a choice honestly, or it does not earn it at all. This policy sets out how we keep our sign-up, consent, subscription, cancellation, and pricing flows free of deception — not as a legal chore, but because manipulating the people who trust us is a short-term win and a long-term betrayal. Designing honestly is **Be Nice** made concrete: thinking wholeheartedly about the people on the other side of the screen. + +[[TOC]] + +## Why we design honestly + +**We act in the user's genuine interest, or we have failed — even when the numbers look good.** A "dark pattern" (the industry also calls these "deceptive patterns") is any interface deliberately shaped to push someone toward a choice that serves us over them: subscribing, sharing more data, spending more, or staying enrolled, when they would decide otherwise had the choice been laid out plainly. These patterns can lift a metric this quarter. They spend down the trust that took years to build, and trust does not come back at the price it left. + +**An honest interface is one a person can understand, choose freely, and reverse as easily as they made it.** That is the whole standard, and everything below is how we hold work to it. It is also a legal line in the jurisdictions we serve — deceptive consent and subscription flows are directly regulated — but we would hold it even if no regulator did. + +- We **MUST** design every flow to serve the user's genuine interest, and treat a choice extracted by pressure, confusion, or guilt as a defect, not a result. +- We **MUST NOT** trade the trust of the people we serve for a short-term gain in a conversion or retention metric. + +## What we do not build + +**We review every interface against the known catalogue of deceptive patterns before it ships.** The catalogue is settled and public, so "is this manipulative?" is a question we can answer by inspection rather than instinct. Any match is a defect to fix, not a decision to defend. The patterns we watch for include confirmshaming (guilt-tripping the option to decline), pre-checked consent, sneaking items into a basket, forced continuity (a free trial that slides into a charge with no clear, timely warning), hard-to-cancel flows, misdirection (visual emphasis or trick wording that steers toward the choice that suits us), nagging (re-asking for a permission already declined), and hidden costs revealed only after commitment. + +**We never manufacture pressure.** Urgency and scarcity cues — countdown timers, "only two left", "others are viewing" — are shown only when they are literally, verifiably true. If it is not real, it is not on the screen. + +- We **MUST** walk every consent, sign-up, subscription, cancellation, and pricing flow against the deceptive-pattern catalogue before it merges, and fix every match. +- We **MUST NOT** use confirmshaming: the option to decline is stated as neutrally as the option to accept. +- We **MUST NOT** sneak items, warranties, or donations into a flow that the user did not choose, and we **MUST** disclose every cost — including what recurs and when — before the user commits. +- We **MUST** show urgency or scarcity indicators only when they are literally true and verifiable, and otherwise **MUST NOT** show them. +- We **SHOULD** meet the stricter of any two jurisdictions' rules everywhere, rather than fork an interface's honesty by region. + +## Consent and symmetry + +**Leaving is as easy as arriving.** Whatever it took to get in, it takes no more to get out — if sign-up is one click, so is cancellation; if a toggle turned data sharing on, the same toggle, equally prominent, turns it off. The way out gets equal visual weight, equal accessibility, and equal reach: the same number of steps, no retention maze, no phone call, no hidden menu. + +**Consent is a free choice or it is nothing.** We ask for it in plain language — what we collect, why, and who receives it, in words a non-specialist reads once and understands — never buried in legalese or a linked policy. Consent controls default to off; silence and inaction are never a yes. A person can decline and still use the core product, because consent bundled with unrelated function is not freely given, and recording a coerced or pre-ticked click does not turn it into consent. + +- We **MUST** make the way out of any state reachable in the same number of steps, with equal prominence and equal accessibility, as the way in. +- We **MUST** default every consent control to off, and **MUST NOT** record consent from a pre-checked or defaulted control. +- We **MUST** write consent copy in plain language that states what is collected, why, and to whom, and tie the recorded consent to the exact wording the user saw. +- We **MUST** let a user decline non-essential consent and still use the core product. +- We **SHOULD** send a clear, timely reminder before any free trial converts to a paid charge, and offer cancellation in a single step. +- We **SHOULD NOT** re-request a permission a user has already declined without a fresh, user-initiated reason. diff --git a/doc/incident-management.md b/doc/incident-management.md new file mode 100644 index 0000000..9bcadad --- /dev/null +++ b/doc/incident-management.md @@ -0,0 +1,535 @@ +# Incident Management + +This is the standard the [Quality Gate](/quality-gate)'s **Reliability** lens +holds work to for the moment something goes wrong in production. It covers the +whole arc of an incident: how anyone raises one, how we respond and meet our +legal duties, and how AI helps us investigate without widening the blast radius. +It builds on the [Infrastructure Planning Policy](/infra-planning-policy) (which +gives us backups, RPO/RTO targets, and reversible deploys), the [Security +Policy](/security-policy) (what we protect and the risks we assume), and the +pull-request discipline of the [Development Guide](/development-guide). Deviations +are allowed, but — as everywhere in the handbook — they must be deliberate and +justified in the project's design notes. + +An incident is where OSBR's values are tested hardest. **Be Nice**: we keep +stakeholders informed, on time, in plain language, and we never hand a tired +on-call human a change they did not ask for. **Be Kind**: the person who reports +an incident — or caused one — is doing us a favour by surfacing it, so the report +is thanked and the post-mortem is blameless; we fix systems, not people. **Be +Strong**: we face the incident directly, contain it, tell the client the truth +even when it hurts, and know when to call for help. Humans and AI agents work an +incident here as collaborators — and that partnership carries a specific hazard, +live production, that this policy names head-on (§3). + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the [Coding Style + Guide](/style-guide). **MUST** / **MUST NOT** are absolute. **SHOULD** / + **SHOULD NOT** state a strong default overridable only with a documented + reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice — NIST SP 800-61, + SANS PICERL, the incident-command patterns of Google SRE and PagerDuty — the + practice is named inline and cited under [References](#references). We adopt the + *criteria* of these practices and right-size them for an SME; we do not adopt + the headcount or infrastructure behind their reference setups. +* **This is guidance, not legal advice.** The reporting deadlines and thresholds + in §2-3 are OSBR's default reading of the law. For any serious personal-data + incident, confirm the current obligation with the client's legal counsel. When + in doubt, notify. + +[[TOC]] + +## 1. Reporting an Incident + +The goal of reporting is to make it **fast, safe, and normal** for *any* +developer or collaborator to raise a suspected incident, so the Security Officer +can act while the situation is still small. The behaviour we want is simple: +**report early, report often.** A rumour of a problem, reported in two minutes, +beats a confirmed breach found three days later. This section exists to remove +every reason someone might hesitate — uncertainty, embarrassment, fear of blame, +or worry about "wasting" the officer's time. + +### 1-1. The reporter does not decide whether it is an incident + +This is the single most important rule of reporting: + +- The reporter **MUST NOT** self-triage before reporting. Deciding *whether* + something is "really" an incident is the **Security Officer's** call, not the + reporter's. The reporter's only job is to surface what they noticed. +- Asking individuals to judge severity before speaking up is how real incidents + get silently sat on. When in doubt, report — the cost of a false alarm is + minutes; the cost of a missed incident is measured in trust and data. + +### 1-2. Where to report — speed beats formality + +- Reporters **MUST** raise suspected incidents in the dedicated + **`#incident-response`** channel, addressed to the Security Officer. +- Reporters **MUST NOT** wait to gather a "complete" picture, open a formal + ticket first, or route the report through a team lead before speaking up. +- If the dedicated channel is unavailable, reporters **SHOULD** contact the + Security Officer directly (or `info@osbrjp.com` if the officer is unreachable) + and note it in the channel once reachable. +- Reporters **MUST NOT** attempt to quietly fix a suspected incident alone. A + delayed report turns a small incident into a large one. + +### 1-3. What to report — the three points + +Every report **MUST** include these three points. Keep it short; a few sentences +each is enough. You are **not** expected to know the full answer to any of them — +"I don't know yet" is a valid and useful answer. + +1. **Summary** — What did you see? A plain description of the symptom (e.g. "prod + API is returning other users' data on `/orders`", "I think I pushed an AWS key + to a public repo"). +2. **Status & certainty** — Is it ongoing or over? How sure are you? Say so + honestly — "still happening", "not sure if it's real", "90% sure". **Low + certainty is not a reason to stay silent.** +3. **Scope of impact** — Who or what looks affected, as far as you can tell? Which + system, which data, which customers — even a rough guess ("looks like staging + only, but I can't confirm prod is safe"). + +### 1-4. Report regardless of scale or certainty + +- Reporters **MUST** report even when unsure whether the event qualifies as an + incident, and regardless of how small it looks. +- Reporters **SHOULD** report *near-misses* too — the thing that almost went + wrong. Near-misses are free lessons. +- A report made in good faith that turns out to be nothing is a **success**, not + a mistake. We would rather investigate ten false alarms than miss one real + event. + +### 1-5. No blame for reporting in good faith + +OSBR operates a **blameless / just culture** (Dekker), grounded in **psychological +safety** (Edmondson): people only surface problems early when they are confident +that doing so will not be held against them. Punishing reporters destroys exactly +the early-warning signal we depend on. This is **Be Kind** made concrete. + +- There is **no personal penalty** for reporting a suspected incident in good + faith — **including one you caused yourself**. +- The Security Officer and team leads **MUST** respond to reports with thanks and + focus on the system and the fix, never on punishing the reporter. +- Every response review and post-incident write-up **MUST** stay blameless (§2-5): + they ask *what in the system let this happen* and *how do we make it harder next + time*, never *who to blame*. + +## 2. Responding to an Incident + +We do not invent our own incident model. We follow the practices the field has +already settled on — **NIST SP 800-61**, **SANS PICERL** (Prepare, Identify, +Contain, Eradicate, Recover, Lessons-learned), and the incident-command patterns +published by **Google SRE** and **PagerDuty** — right-sized for a small team. The +goal is to detect an incident quickly, **contain the damage**, meet every +mandatory reporting obligation on time, restore verified service, and make sure +the same thing cannot happen the same way twice. + +### 2-1. One owner — the Security Officer as Incident Commander + +Every incident has exactly **one owner**: the on-duty **Security Officer**, who +acts as **Incident Commander (IC)** in the sense used by Google SRE and PagerDuty. +The IC is the single decision-maker; they need not do every task, but own that +every task happens. + +The Security Officer **MUST**: + +- Declare the incident and take command; hand over explicitly if they step away — + no incident is ever ownerless. +- Acknowledge every report promptly so the reporter knows it landed, and make the + **incident-vs-not determination**. +- Determine the incident **type** and **severity** (§2-2). +- Assess **mandatory-reporting obligations** and start those clocks (§2-3). +- Select and drive the applicable **response flows** (§2-4). +- Own the closing artifacts: **blameless post-mortem**, **recurrence-prevention + plan with a verification date**, and **stakeholder report** (§2-5). + +The reporter **SHOULD** stay reachable to answer follow-up questions but is +**not** responsible for running the response unless asked. + +**Roles scale down, they do not disappear.** On a large incident the IC delegates +two roles from the same playbooks: an **Operations/Tech lead** who actually +changes the system, and a **Communications lead** who handles client and internal +updates. On a small incident one person may wear all three hats — but the *roles* +still exist, so nothing is dropped. + +### 2-2. Determine type and severity + +Before acting, the Security Officer classifies the incident. Classification +decides severity, which decides how fast we move and who we wake up. + +**Type** — classify against the risk categories in the [Security Policy, Appendix +2](/security-policy) (accidents / human error, external attacks, insider threats), +and note whether **personal data** is in scope, because that triggers the legal +duties in §2-3. Typical types: personal-data breach; unauthorised access / +account compromise; malware / ransomware; availability incident (ties into the +SLO / RTO / RPO targets in the [Infrastructure Planning +Policy](/infra-planning-policy)); accidental disclosure or data loss. + +**Severity** — the Security Officer **MUST** assign a severity at declaration and +**MUST** revise it as facts change. Severity is about **impact and reach**, not +blame, and is set by the officer, never by the reporter. + +| Sev | Meaning | Response | +| --- | --- | --- | +| **SEV1** | Confirmed personal-data breach, active attacker, or major outage. Reporting clocks likely running. | IC engages immediately; client notified; consider external assistance (§2-4). | +| **SEV2** | Contained or limited-blast-radius incident; potential (unconfirmed) data exposure. | IC owns; assess reporting duties (§2-3) without delay. | +| **SEV3** | Minor / suspected incident, no evidence of data exposure. | Handle in-hours; record and monitor. | +| **SEV4 / near-miss** | Caught before impact. | Record as a free lesson (§1-4); no urgent response. | + +When unsure between two severities, the officer **MUST** pick the higher one and +de-escalate later. Under the PDPA, APPI, and GDPR the reporting clock can start on +a *suspected* breach, not only a confirmed one (§2-3). Downgrading is cheap; a +missed legal deadline is not. **Do not under-classify to avoid paperwork.** + +### 2-3. Mandatory-reporting obligations + +As part of analysis, the Security Officer **MUST** determine whether a legal +notification duty applies **and start the clock the moment a breach is reasonably +suspected** — not when it is fully understood. If a duty applies, the **Disclose** +flow (§2-4) becomes mandatory, not optional. + +**Malaysia — PDPA (Personal Data Protection Act 2010, Act 709).** OSBR's home +law. Under the **Personal Data Protection (Amendment) Act 2024 (Act A1727)**, a +data controller must **notify the Personal Data Protection Commissioner of a +personal-data breach as soon as practicable**, and **notify affected data subjects +where the breach is likely to cause significant harm**. The regulator is the +**Personal Data Protection Commissioner / Personal Data Protection Department +(JPDP)**. + +**Japan — APPI (revised Act on the Protection of Personal Information).** A +business handling personal information must report a reportable breach to the +**Personal Information Protection Commission (PPC, 個人情報保護委員会)** and notify +affected individuals. A breach is **reportable** when it involves any of: +**sensitive personal information** (要配慮個人情報); a risk of **property damage** (e.g. +leaked payment data); a breach committed for an **improper purpose** (cyberattack +/ unauthorised access); or **more than 1,000 data subjects.** Reporting is +two-stage — a **preliminary report (速報)** promptly, within about **3–5 days** of +becoming aware, and a **final report (確報)** within **30 days** of awareness +(extended to **60 days** where the breach was for an improper purpose). If some +required items are not yet known by the deadline, file with what is known and +complete it as the facts are established. + +**EU / EEA — GDPR (where applicable).** GDPR applies where a project processes the +personal data of individuals in the EU/EEA (confirm scope with the client; mind +data residency per the [Infrastructure Planning +Policy](/infra-planning-policy)). **Article 33** — notify the competent +supervisory authority **without undue delay and, where feasible, within 72 +hours** of becoming aware, unless the breach is unlikely to result in a risk to +individuals; a missed 72-hour mark must be explained. **Article 34** — where the +breach is likely to result in a **high risk** to individuals' rights and freedoms, +communicate it to the **affected data subjects without undue delay**. + +**More than one may apply.** A single incident can trigger the PDPA, APPI, *and* +GDPR at once. Track each clock separately — they have different recipients and +different deadlines — inside the incident record (§2-4). + +### 2-4. The five response flows + +Once type, severity, and reporting duties are set, the Security Officer executes +among these five flows. They are not strictly sequential: **Record** runs +throughout, **Disclose** runs on the legal clock, and **Request external +assistance** can start at any point. The lifecycle maps OSBR's flows onto SANS +PICERL and the NIST SP 800-61 phases; a real incident loops back as new facts +arrive. + +| OSBR flow | PICERL phase | NIST SP 800-61 phase | +| --- | --- | --- | +| — (before the incident) | Prepare | Preparation | +| **Record** | Identify | Detection & Analysis | +| **Prevent (contain)** | Contain | Containment | +| **Remediate** | Eradicate + Recover | Eradication & Recovery | +| **Disclose** | (across all phases) | Post-Incident notification duties | +| **Request external assistance** | (any phase, as needed) | — | +| Close-out (§2-5) | Lessons-learned | Post-Incident Activity | + +**Record** · *Identify.* Open the incident record the moment the incident is +declared and keep it current — it is the backbone of every later flow and of any +regulator submission. The Security Officer **MUST** capture: a **single, +timestamped, append-only timeline** of what was observed, decided, and done, by +whom; **type, severity, and scope** (which systems, whose data, how many records); +any **reporting clocks** started (§2-3), with their deadlines; and **evidence +preserved before it is destroyed** — logs, metrics, and communication history are +protected assets ([Security Policy, Appendix 1](/security-policy)). Preserve +first; **do not tamper** while containing. The record **SHOULD** live in the +project's agreed incident location, access-limited to those who need it, with +personal data masked per the Security Policy. + +**Prevent (Contain)** · *Contain.* Stop the bleeding before cleaning up — +**containment comes before eradication.** The Security Officer **MUST** contain +proportionally to severity: revoke or rotate compromised credentials and keys +(**immediately revoke any committed credential**, per the [Security +Policy](/security-policy)); isolate affected hosts, disable compromised accounts, +or block malicious traffic at the WAF / edge; and where appropriate throttle or +take a service offline rather than let a breach continue — a short outage can beat +an ongoing data leak. Containment actions **MUST** be written to the record as +they happen. + +**Remediate** · *Eradicate + Recover.* Once contained, remove the root cause and +restore verified service. The Security Officer **MUST**: **eradicate** — remove +the attacker's foothold, malware, or the defect and close the vulnerability that +allowed the incident, fixing the **root cause**, not the symptom; **recover** — +restore service from a known-good state and, where data was lost, restore from +**tested** backups against the datastore's **RPO / RTO** (per the [Infrastructure +Planning Policy](/infra-planning-policy)); and **verify** — confirm the system is +clean and healthy through monitoring before declaring recovery, watching for +recurrence. Root-cause fixes that cannot ship during the incident become items in +the recurrence-prevention plan (§2-5), each with an owner and a verification date. + +**Disclose** · *cross-phase, on the legal clock.* Tell the people who need to know +— **honestly, promptly, in plain language.** This flow is **mandatory** whenever +§2-3 applies, and good practice even when it does not. The Security Officer (or +Communications lead) **MUST**: file the **regulator** notifications on their +deadlines (PDPA notification to the Personal Data Protection Commissioner as soon +as practicable; APPI 速報 / 確報 to the PPC; GDPR Art. 33 to the supervisory +authority); notify **affected individuals** where required (PDPA where significant +harm is likely; APPI; GDPR Art. 34 high-risk); +keep the **client** informed from the start — never let a client learn of their +own incident from a regulator or the news; and give internal stakeholders honest, +timely status updates (**Be Nice**). Disclosure **SHOULD** state what happened, +what data was involved, what we have done, and what affected parties should do — +no spin, no minimising. Every external communication is logged in the record. + +**Request external assistance** · *any phase.* Knowing when to call for help is +**Be Strong**, not weakness. The Security Officer **SHOULD** bring in outside help +when the incident exceeds the team's capacity or authority: **legal counsel** for +reporting obligations and liability (the default for any SEV1/SEV2 personal-data +breach); the **cloud provider's** security team, or a specialist **DFIR** +(digital-forensics & incident-response) firm for serious intrusions; **law +enforcement**, where the client and counsel agree it is warranted; and the +relevant **CSIRT / JPCERT-CC**-style coordination body where appropriate. External +parties are given least-privilege access and recorded in the incident record. +Involving them never removes the Security Officer's ownership of the incident. + +### 2-5. Close-out — every incident ends the same way + +An incident is not closed when service is restored — it is closed when we have +**learned from it** (the PICERL Lessons-learned phase; NIST Post-Incident +Activity). The Security Officer **MUST** produce all three artifacts below. + +- **Blameless post-mortem** — written in the sense established by Etsy and the + Google SRE practice: the analysis assumes everyone acted reasonably with the + information they had, and asks *how the system let this happen*, never *who to + blame*. This is **Be Kind** made concrete — the reason people report honestly + instead of hiding. It **MUST** cover timeline, impact, root cause(s), what went + well, what went badly, and where we got lucky, and **MUST NOT** name individuals + as causes. +- **Recurrence-prevention plan (with a verification date)** — the concrete actions + that stop this class of incident from recurring. Each action **MUST** have an + **owner** and a **verification date** — a scheduled point at which someone + confirms the fix is in place and effective. A prevention plan with no + verification date is a wish, not a plan. +- **Stakeholder report** — a written report to affected stakeholders (client + first, plus internal leadership and, where relevant, regulators and affected + individuals): what happened, the impact, what we did, and what we are changing + so it does not recur. Plain language, no blame, on time. + +## 3. AI-Assisted Production Investigation + +The same structured logs, metrics, and traces that let an on-call engineer find a +fault are what let an AI agent investigate one. This section is the +access-control and data-handling contract that makes it safe to actually do that +in **production**. The promise is asymmetric on purpose: we want AI to make +investigation *faster* — trace an error to its span, correlate a spike to a +deploy, read the error budget, draft the root-cause narrative — without making the +*blast radius* any larger than a human investigator's already is. So AI gets +exactly the observer's reach and no more. + +Two boundaries are **inviolable**: every path AI uses to reach production is +**read-only**, and **PII is masked before it enters AI context**. They are not +tunable per project, per incident, or per urgency. A faster investigation is never +a reason to widen either one; if a boundary is in the way, the answer is a better +read-only view or a better mask, never an exception. **Be Nice** — AI does the +tedious correlation across a hundred thousand log lines so a tired on-call human +does not. **Be Kind** — the people *in* the telemetry are protected, because their +personal data never reaches the model at all. **Be Strong** — an investigator, +human or AI, that can see the whole system reaches root cause faster. + +We lean on published standards rather than inventing our own: the **principle of +least privilege** and **read-only / just-in-time access** (AWS IAM best practices; +NIST SP 800-207 Zero Trust), **PII de-identification / masking** (NIST SP +800-122), **data minimisation** (GDPR Art. 5(1)(c)), **human oversight of AI** +(NIST AI RMF), and the LLM-specific failure mode of **sensitive-information +disclosure** (OWASP Top 10 for LLM Applications). + +### 3-1. Same outputs a human reaches — no private backchannel + +AI investigates through the **same observation outputs** a human on-call engineer +uses — the central log store, the metrics and trace backend — defined by OSBR's +observability discipline. + +- **MUST** give AI access to the *same* telemetry surface humans use, not a + bespoke firehose, raw database, or node-level access a human investigator would + never touch. Parity of *observation*, not a wider door. +- **MUST NOT** grant AI a path to production data that bypasses the observability + pipeline's masking and access controls — reading raw production tables, tailing + an unmasked log file on a host, or snapshotting memory. If a human investigator + should not reach it, neither does AI. +- **SHOULD** expose telemetry through query tools/APIs (e.g. an MCP server, a + read-only observability API) scoped to exactly the observation outputs, so the + reach is defined by the tool surface, not by a broad cloud credential. + +### 3-2. Read-only is the first inviolable boundary + +Every path AI uses to reach production observation data is **read-only**, enforced +by the *permissions of the identity*, so that even a confused, prompt-injected, or +buggy agent **cannot** mutate production through its investigation path. + +- **MUST** run all AI production investigation under a **dedicated, read-only, + least-privilege identity** whose permissions include **no** write, delete, + update, restart, scale, deploy, rollback, or configuration-change action on any + production resource. Least privilege is the floor; read-only is the ceiling. +- **MUST** enforce read-only at the **permission layer** (IAM policy / role / + scoped API token), not merely by instructing the model or filtering prompts. A + boundary a prompt can talk its way past is not a boundary. +- **MUST** keep the investigation identity **separate** from any identity that can + mutate production (§3-4). One credential never holds both "read the logs" and + "restart the service." +- **MUST NOT** grant the investigation identity standing high-privilege access "to + save a round trip." +- **SHOULD** scope the identity by time and blast radius where the platform allows + — short-lived / just-in-time credentials, read replicas over primaries, + environment- and service-scoped tokens. + +### 3-3. PII masked before context is the second inviolable boundary + +Personal data is **masked or removed before it enters AI context** — before the +bytes reach the model, not after. This inherits from OSBR's rule that PII must be +masked or omitted **at the source** before it is written to any log, span +attribute, or metric label, and from the [Security Policy](/security-policy)'s +treatment of telemetry as a **Protected Asset**. AI adds a second reason the +masking must already be done: data placed in a model's context is data +minimisation you can no longer take back. + +- **MUST** ensure PII is masked, redacted, tokenised, or pseudonymised **at the + source** so the observation outputs AI reads **do not contain** raw names, + emails, tokens, full card/account numbers, precise location, or request/response + bodies carrying personal data. The mask is upstream of AI, not a filter bolted + on at query time that a new field can slip past (NIST SP 800-122). +- **MUST NOT** place unmasked personal data into an AI prompt, tool result, or + retained context — including when a human pastes a raw log excerpt into an agent. + The masking obligation follows the data, not the pipeline. +- **MUST** apply **data minimisation** to what enters context — only the + observation data the investigation needs, only for as long as it needs it (GDPR + Art. 5(1)(c)). +- **SHOULD** use **stable pseudonymous identifiers** (a hashed user id) over raw + ones, so AI can correlate "the same user's requests" without ever holding who + that user is. +- **SHOULD** treat any secret, credential, or token found in telemetry as a leaked + credential — revoke it and fix the mask that let it through — rather than + reasoning about it in context. + +### 3-4. Mutating actions live on a separate, human-approved path + +An investigation may conclude that production must change — restart an instance, +roll back a deploy, scale a pool, flip a flag, correct a record. That action does +**not** happen on the investigation path; it happens on a **separate path gated by +explicit human approval**, the way high-privilege and break-glass access is gated +elsewhere in modern practice (Google Cloud Privileged Access Manager; AWS IAM). + +- **MUST** route every mutating production action through a **distinct, + human-approved path** — a separate identity/role, a change gate, a break-glass + procedure — never through the AI investigation identity (§3-2). +- **MUST** require a **human approval step** before any change is applied. AI may + *prepare* the action (draft the rollback command, the scaling change, the + runbook step); a human **approves and applies** it. AI approving its own + proposed change is prohibited. +- **MUST** make break-glass / emergency-change use **auditable and alerting** — + who invoked it, when, why, on what. Emergency speed does not buy silence. +- **SHOULD** prefer pre-decided, automated self-healing for the conditions a + machine already handles — a retriable timeout, an unhealthy instance de-routed, + a pre-agreed rollback trigger. Those need no live human approval *because they + were approved when written down*. The approval gate here is for the **novel** + change an investigation invents on the spot. + +### 3-5. Human-in-the-loop — AI diagnoses, a human decides + +The output of AI-assisted investigation is a **proposal**, not an executed +decision. Keeping a human in the loop for the *action* is what lets us take the +*speed* of AI investigation without inheriting the risk of an autonomous agent +changing production (NIST AI RMF). + +- **MUST** treat AI conclusions as **advisory**. A root-cause narrative, a + suspected bad deploy, a recommended rollback — a human reviews it and owns the + decision to act, exactly as they would a colleague's suggestion. +- **MUST** keep the human **able to understand and override** the proposal: AI + cites the specific traces/logs/metrics it reasoned from (the correlation a shared + `trace_id` makes possible), so the human can check the evidence, not just the + conclusion. +- **SHOULD** let AI do the **toil** end-to-end within the read-only boundary — fan + out across logs, correlate spans, compute the error-budget burn, draft the + incident timeline and the post-mortem — so the human spends their judgement on + the decision, not the data-gathering. +- **SHOULD** capture the human's decision (approved / rejected / modified) so the + investigation record shows both the AI proposal and the human call. + +### 3-6. Attribution and audit + +Both boundaries are only real if their use is visible. + +- **MUST** make every AI query against production observation data **attributable + to the AI investigation identity** and logged, so "what did the agent read" is + answerable after the fact — telemetry access is itself a Protected-Asset access + ([Security Policy](/security-policy)). +- **MUST** log every human-approved mutating action (§3-4) with who approved it, + what AI proposed, and why — the change record ties the AI proposal to the human + decision to the applied action. +- **SHOULD** alert on anomalies in the investigation path itself — an investigation + identity attempting a write it should not have, an unusual volume of telemetry + pulled into context — as a high-signal event. + +**Anti-patterns this section exists to prevent:** handing the investigation agent +a broad admin/deploy credential "so it can fix things too"; enforcing read-only +only in the prompt while the underlying token can write; masking PII *after* it +reaches the model, or "planning to add masking later"; pasting a raw, unmasked +production log dump into an agent because it is faster; letting AI restart, roll +back, or scale production on its own conclusion with no human approving; a +break-glass change applied with no record of who, what, or why; and AI reaching +production data through a backchannel (raw DB, host shell) that bypasses the +observability pipeline's masking. + +## References + +**Incident-handling frameworks** + +- NIST SP 800-61 — Computer Security Incident Handling Guide — +- SANS — Incident Handler's Handbook (the PICERL model: Preparation, Identification, Containment, Eradication, Recovery, Lessons-learned) — + +**Incident command & operations** + +- Google SRE — Managing Incidents (Incident Command System) — +- PagerDuty Incident Response documentation — + +**Blameless culture & post-mortems** + +- Sidney Dekker, *Just Culture: Balancing Safety and Accountability* — separating honest error from the system that allowed it — +- Amy Edmondson, *The Fearless Organization* — psychological safety as the precondition for speaking up — +- Etsy — Blameless PostMortems and a Just Culture (John Allspaw) — +- Google SRE — Postmortem Culture: Learning from Failure — + +**Legal / mandatory reporting** + +- Malaysia — Personal Data Protection Act 2010 (Act 709), the home law — Personal Data Protection Department (JPDP) — +- Malaysia — Personal Data Protection (Amendment) Act 2024 (Act A1727), mandatory breach notification to the Personal Data Protection Commissioner (as soon as practicable; affected data subjects where significant harm is likely) — +- Japan — Personal Information Protection Commission (PPC), breach-reporting duty under the revised APPI — +- GDPR Article 33 — Notification of a personal data breach to the supervisory authority (within 72 hours) — +- GDPR Article 34 — Communication of a personal data breach to the data subject — +- GDPR Article 5(1)(c) — Data minimisation — + +**Least-privilege, read-only & human-approved access** + +- AWS IAM — Security best practices (least privilege, read-only, just-in-time, short-lived credentials) — +- NIST SP 800-207 — Zero Trust Architecture — +- Google Cloud — Privileged Access Manager (just-in-time elevation, emergency/break-glass access) — + +**PII masking & AI oversight** + +- NIST SP 800-122 — Guide to Protecting the Confidentiality of PII (de-identification/masking) — +- NIST AI Risk Management Framework (human oversight; govern/map/measure/manage) — +- OWASP Top 10 for LLM Applications (Sensitive Information Disclosure; Excessive Agency) — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Reliability lens this standard serves. +- [Security Policy](/security-policy) — protected assets, credential hygiene, and the risk categories incidents fall into. +- [Infrastructure Planning Policy](/infra-planning-policy) — backups/DR, RPO/RTO, observability, and reversible deploys that make detection and recovery possible. +- [Development Guide](/development-guide) — the pull-request discipline that carries root-cause fixes and prevention items. +- [Code of Conduct](/code-of-conduct) — the behavioural baseline that makes blameless reporting safe. diff --git a/doc/index.md b/doc/index.md index d769eda..c9d69c9 100644 --- a/doc/index.md +++ b/doc/index.md @@ -23,3 +23,9 @@ features: details: "A welcoming overview of OSBR’s culture and values, providing a glimpse into what it’s like to be part of our team." --- + +
+ +AI agents: a machine-readable version of this handbook is available at [/llms.txt](/llms.txt) (index) and [/llms-full.txt](/llms-full.txt) (full text). + +
diff --git a/doc/interaction-design.md b/doc/interaction-design.md new file mode 100644 index 0000000..c7f20c5 --- /dev/null +++ b/doc/interaction-design.md @@ -0,0 +1,300 @@ +# Interaction Design + +This is the standard OSBR holds interaction design to, and the consistency +companion to the [Design Guidelines](/design-guidelines): **the interactive +behaviour of a component — how it loads, fails, empties, succeeds, and responds +to the keyboard — is defined once, before the second screen is built, and reused +everywhere.** A user should learn the product a single time. When they have +learned what a loading state looks like, what an error says and where, what an +empty list offers, and which key dismisses a thing, that knowledge MUST carry to +every other screen unchanged. Any screen that behaves differently forces the +user to re-learn, and re-learning is a tax the design levied instead of paying +itself. + +We do not invent our own interaction conventions per screen. We stand on named, +published practice — the Nielsen Norman Group's *consistency and standards* +heuristic, the major design systems (Google Material, IBM Carbon, Shopify +Polaris, Atlassian) that codify component states, and the W3C WAI-ARIA Authoring +Practices for keyboard interaction — and apply them as a single shared standard +rather than a per-screen decision. This page is the consistency companion to [UI +That Requires No Manual](/self-explanatory-ui) (that page makes *one* screen +explain itself; this page makes *every* screen explain itself *the same way*), +to [Modeless Design](/modeless-design) (a consistent exit gesture only helps if +it is the same gesture everywhere), and to [Accessibility](/accessibility) +(keyboard and focus consistency is where these standards meet). + +Interaction design is where OSBR's values become something the user can feel. +**Be Nice**: define the standard early and write down every deliberate +exception, so the next builder inherits the convention instead of guessing at +it. **Be Kind**: the payoff is literal — the user learns the product *once, not +once per screen*; it is unkind to make someone who mastered one screen feel lost +on the next because the same button now sits elsewhere, the same error now +speaks differently, or the same key now does nothing. **Be Strong**: refuse to +let the second screen freelance its own loading spinner because a shared one was +not ready yet. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as elsewhere in the [Design + Guidelines](/design-guidelines). **MUST** / **MUST NOT** are absolute. + **SHOULD** / **SHOULD NOT** state a strong default overridable only with a + documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). We adopt the *criteria* + of these design systems and right-size them for an SME — we do not adopt the + scale or org behind their reference setups. + +[[TOC]] + +## 1. Goal + +The goal is a product the user learns **once, not once per screen.** Every +interactive component behaves predictably: the same state looks the same, the +same action produces the same result, and the same key does the same thing, no +matter which screen it appears on. The user builds one mental model early and +spends it everywhere. + +The root idea is the Nielsen Norman Group's fourth usability heuristic, +**consistency and standards**: "users should not have to wonder whether +different words, situations, or actions mean the same thing." A product that +answers that question differently on each screen makes the user carry a separate +rule for every page. A product that answers it once lets a habit formed on +screen one transfer, untouched, to screen fifty. This is why the standard must +be set **before the second screen** — the first screen alone establishes no +convention; the second is where consistency is either won or permanently lost, +because every later screen copies whichever way the divergence went. + +## 2. Responsibility + +Whoever builds the interactive layer owns its consistency with the rest of the +product — not just its correctness in isolation. Concretely, that person or pair +MUST: + +- **Define the four interactive states** — loading, error, empty, success — as a + shared, reusable standard **before the second screen** consumes them, not + re-improvise them per screen (§3-1, §3-2). +- **Standardise keyboard interaction** so the same keys do the same things on + every equivalent component, following the WAI-ARIA Authoring Practices for + each pattern (§3-3). +- **Reuse the design system's components and tokens** rather than + re-implementing a one-off variant of something that already exists (§3-4). +- **Record every deliberate deviation** in the design system's interaction notes + — what differs, on which component, and why — so the exception is visible and + auditable, never silent divergence (§3-4). + +This is not a hand-off to a separate "design system" team who reconcile the +screens at the end. The person building the second screen is the person who +either upholds the convention or records the break, because those are the same +decision made at the same moment. + +## 3. Practices + +### 3-1. Define the four interactive states once, before the second screen + +Every component that fetches, shows, or accepts data lives in four interactive +states, not one. The major design systems all treat these as first-class, named +things to be designed deliberately — not incidental moments. IBM Carbon +documents [empty states](https://carbondesignsystem.com/patterns/empty-states-pattern/) +and [loading/skeleton states](https://carbondesignsystem.com/patterns/loading-pattern/) +as reusable patterns; Shopify Polaris ships a [Skeleton content set](https://polaris.shopify.com/components/feedback-indicators/skeleton-thumbnail) +and [empty-state guidance](https://polaris.shopify.com/patterns/empty-states); +Atlassian's Design System codifies [empty state](https://atlassian.design/patterns/empty-state) +and messaging patterns; Google Material specifies [loading indicators](https://m3.material.io/components/loading-indicator/guidelines) +and [progress indicators](https://m3.material.io/components/progress-indicators/guidelines). +The point OSBR takes from all four: **the states are components, defined once, +not decisions re-made on each screen.** + +| State | What it must do, identically everywhere | Grounded in | +| ----- | --------------------------------------- | ----------- | +| **Loading** | Same indicator, same placement, same threshold for showing it — a shared skeleton or progress component, not a per-screen spinner | Material [loading](https://m3.material.io/components/loading-indicator/guidelines), Carbon [loading](https://carbondesignsystem.com/patterns/loading-pattern/), Polaris [skeleton](https://polaris.shopify.com/components/feedback-indicators/skeleton-thumbnail) | +| **Error** | Same tone, same structure (what happened + what to do), same placement relative to cause | Carbon / Atlassian messaging patterns; see [self-explanatory-ui §3-3](/self-explanatory-ui) | +| **Empty** | Same layout, same "here's the first action" shape, on every empty collection | Carbon [empty states](https://carbondesignsystem.com/patterns/empty-states-pattern/), Polaris [empty states](https://polaris.shopify.com/patterns/empty-states), Atlassian [empty state](https://atlassian.design/patterns/empty-state) | +| **Success** | Same confirmation mechanism (toast, inline, banner) for the same class of action, everywhere | Material / Polaris feedback guidance | + +- These four states MUST be defined as **shared components or documented + patterns before the second screen is built.** The first screen sets no + precedent alone; the second is the moment consistency is decided, so the + standard must exist by then. +- A component's states MUST be **reused, not re-created.** A second list that + invents its own empty state instead of using the shared one is a divergence + even if it looks similar — "similar" is exactly what the user notices and + mistrusts. +- **Skeleton screens are the default loading treatment** for content that has a + known layout (Material, Carbon, Polaris all provide them): they preserve the + page's shape and read as "arriving", where a bare spinner reads as "stuck". Use + the shared skeleton, not a bespoke one. +- Each state's *content* still follows [UI That Requires No + Manual](/self-explanatory-ui) — this page governs that the state exists, is + shared, and is identical across screens; that page governs that its copy is in + the user's words. + +One screen can look however it looks — there is nothing yet to be consistent +*with*. The second screen is where the product either reuses the first screen's +behaviour or forks it. If the shared states are not defined by the time the +second screen is built, the fork happens by default, and every later screen +inherits the inconsistency. Define first, build second. + +### 3-2. Same state, same signal — no per-screen dialects + +Consistency and standards (NN/g heuristic 4) is violated not only by different +*words* but by the same situation *signalled differently*. A user who learns +that a green toast means "saved" should never meet a green inline banner meaning +the same thing two screens later, or a silent success a screen after that. The +signal for a state is part of the product's vocabulary, and a vocabulary with +synonyms is a vocabulary the user cannot trust. + +- The **same class of outcome MUST use the same signal** across the product: all + transient successes as toasts, or all as inline confirmations — pick one per + class and hold it. Do not mix dialects for the same meaning. +- Loading MUST use a **consistent threshold and treatment**: if a delay under ~1 + second shows nothing on one screen, it shows nothing on all; skeletons for + structured content, indeterminate progress for unknown-length waits, + determinate progress when the total is known (Material [progress + indicators](https://m3.material.io/components/progress-indicators/guidelines)) + — applied the same way everywhere. +- Error placement MUST be **consistent with its cause**: field errors at the + field, form errors at the form, system errors in the shared system-message + surface — the same rule on every screen, never a banner here and an inline + message there for the same kind of error. +- Empty states for the same *kind* of collection SHOULD share one layout and one + "first action" shape, so an empty list always reads the same way and always + offers the way forward in the same place. + +Every time the same meaning wears a different costume, the user has to stop and +check whether it really is the same meaning. That hesitation is the exact cost +heuristic 4 exists to remove. One signal per meaning, product-wide, is what lets +recognition replace re-checking. + +### 3-3. Standardise keyboard interaction to the ARIA Authoring Practices + +Keyboard behaviour is the most invisible place inconsistency hides and the most +punishing place to get it wrong — a keyboard or screen-reader user navigates +entirely by learned key conventions, so a component that binds keys its own way +strands them. The W3C **WAI-ARIA Authoring Practices Guide (APG)** publishes the +[keyboard interaction](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/) +contract for every common pattern — menu, dialog, tabs, combobox, listbox, +disclosure, and the rest. OSBR adopts the APG's keyboard model as the standard +rather than deciding key bindings per component; this is the interaction side of +[Accessibility](/accessibility). + +- Each interactive pattern MUST implement the **keys the APG specifies for that + pattern**, unchanged: `Tab`/`Shift+Tab` to move between widgets; arrow keys to + move *within* a composite widget (menu, listbox, tab list, radio group); + `Enter`/`Space` to activate; `Escape` to dismiss or cancel; `Home`/`End` where + the pattern defines them. See the APG's + [patterns](https://www.w3.org/WAI/ARIA/apg/patterns/) index for the exact + contract per component. +- The **same pattern MUST use the same keys everywhere.** Every menu in the + product responds to arrow keys the same way; every dismissible surface + responds to `Escape` (as [Modeless Design §3-3](/modeless-design) also + requires). A user's keyboard habit, once formed, MUST transfer to every + instance of that pattern. +- **Focus order MUST follow reading order**, and focus MUST be visible: a + keyboard user has to see where they are. This is a baseline, not a per-screen + judgement (WCAG 2.4.7 *Focus Visible*, 2.4.3 *Focus Order*). +- Roving `tabindex` or `aria-activedescendant` MUST be used for composite widgets + so that `Tab` lands on the widget and arrow keys move within it, per the APG — + never a flat tab stop on every child, which makes one component behave unlike + every other of its kind. +- Custom controls MUST carry the **role, state, and properties** the APG names + for the pattern they imitate (`role`, `aria-expanded`, `aria-selected`, + `aria-modal`, etc.), so assistive technology announces them consistently. + +The APG exists precisely so that every product's menu, dialog, and tab list +behave the same way — a user's keyboard knowledge is portable *between* products +because of it. Diverging inside a single product is worse than diverging between +products: it breaks the one place the user was entitled to assume consistency. +Adopt the published contract; do not author a private one. + +### 3-4. Reuse the system, and record every deviation in the interaction notes + +Consistency is not sustained by willpower on each screen — it is sustained by a +shared system that makes the consistent thing the easy thing, plus an honest +record of where reality had to depart from it. Every mature design system pairs +reusable components with documented usage; OSBR requires the same, including the +departures. + +- Builders MUST **reuse the design system's existing component, state, and + token** rather than re-implement a near-duplicate. A one-off variant of a + component the system already provides is a divergence even when it is prettier + — it splits the user's mental model and doubles the maintenance surface. +- When a screen genuinely **must deviate** — a novel interaction the system has + no pattern for, a constraint the standard did not anticipate — the deviation + MUST be **recorded in the design system's interaction notes**: which component, + what differs from the standard, and why the standard did not serve. An + unrecorded deviation is indistinguishable from a mistake and will be copied as + if it were the convention. +- A recurring deviation is a **signal to update the standard**, not to keep + forking. If the same exception appears three times, the standard is wrong or + incomplete — promote the exception into the shared component so it stops being + an exception. +- The interaction notes are the **single source of truth** for "how does this + behave": a new builder reads them to inherit the conventions instead of + reverse-engineering them from whichever screen they happened to open first. + +Deviating is sometimes right — no standard anticipates everything. What is never +right is deviating *silently*, because the next person cannot tell your +considered exception from an accident, and they will propagate whichever they +find. Writing the deviation down is what keeps the standard a standard: the +exceptions stay countable, reviewable, and reversible. + +## 4. What "Learn Once" Requires, Per Component + +Before an interactive component is called finished, it MUST satisfy all of the +following. This is the checklist the practices above add up to, and the surface +the [Quality Gate](/quality-gate) holds interactive work to: + +| Requirement | The question it answers | +| ----------- | ----------------------- | +| **States defined early** | Are loading, error, empty, and success defined as shared components *before the second screen*? (§3-1) | +| **States reused** | Does this component use the shared states, not a re-created near-duplicate? (§3-1) | +| **Skeleton loading** | Does structured content load via the shared skeleton, not a bespoke spinner? (§3-1) | +| **One signal per meaning** | Does the same class of outcome use the same signal everywhere in the product? (§3-2) | +| **Consistent error placement** | Is each kind of error shown in the same place relative to its cause on every screen? (§3-2) | +| **APG keyboard contract** | Does the component implement exactly the keys the WAI-ARIA APG specifies for its pattern? (§3-3) | +| **Same keys everywhere** | Does every instance of this pattern respond to the same keys? (§3-3) | +| **Visible, ordered focus** | Is focus visible and in reading order for keyboard users? (§3-3) | +| **System reuse** | Does it reuse the design system's component and tokens rather than a one-off variant? (§3-4) | +| **Deviation recorded** | If it departs from the standard, is the departure written in the interaction notes with its reason? (§3-4) | + +A component that works in isolation but fails any row above is not finished — it +behaves correctly on its own screen while quietly teaching the user that this +product must be re-learned page by page. + +## References + +Named, published practice this policy is grounded in — each a documented source +an SME can adopt directly. + +**Consistency as a principle** + +- Nielsen Norman Group — 10 Usability Heuristics for User Interface Design (heuristic 4, *Consistency and standards*) — +- Nielsen Norman Group — Maintain Consistency and Adhere to Standards (Usability Heuristic 4) — + +**Design systems: component states** + +- Google — Material Design 3: Loading indicator — +- Google — Material Design 3: Progress indicators — +- IBM — Carbon Design System: Empty states pattern — +- IBM — Carbon Design System: Loading pattern (skeleton states) — +- Shopify — Polaris: Empty states pattern — +- Shopify — Polaris: Skeleton content — +- Atlassian — Design System: Empty state pattern — + +**Keyboard interaction** + +- W3C WAI-ARIA Authoring Practices Guide — Developing a Keyboard Interface — +- W3C WAI-ARIA Authoring Practices Guide — Patterns (per-component keyboard contracts) — +- W3C — WCAG 2.1: 2.4.3 Focus Order & 2.4.7 Focus Visible — + +**Skeleton / loading patterns** + +- Nielsen Norman Group — Skeleton Screens & Progress Indicators (system status, perceived performance) — + +**Related OSBR standards** + +- [Design Guidelines](/design-guidelines) — the parent design policy this standard serves; RFC 2119 requirement levels. +- [UI That Requires No Manual](/self-explanatory-ui) — makes a single screen explain itself; this page makes every screen explain itself *the same way*, so the four states and their copy stay identical across the product. +- [Modeless Design](/modeless-design) — a consistent exit gesture (`Escape`, background click) only reduces load if it is the same gesture on every modal; this page is why "the same everywhere" is the rule. +- [Accessibility](/accessibility) — the keyboard, focus, and ARIA baseline §3-3 standardises across every instance of a pattern. +- [Quality Gate](/quality-gate) — the gate that holds interactive work to the §4 "learn once" checklist. diff --git a/doc/it-investment-evaluation.md b/doc/it-investment-evaluation.md new file mode 100644 index 0000000..b69da23 --- /dev/null +++ b/doc/it-investment-evaluation.md @@ -0,0 +1,260 @@ +# IT Investment Evaluation + +This policy defines how OSBR decides whether a piece of work is **worth doing** +before we do it, and how we express that judgement to the client. It sits at the +front of [Planning & Shaping](/development-guide): every proposal is assessed by +its **return to the client's business** — not by how large an order it would be +for us — and that assessment is made *up front*, showing the expected effect and +how certain we are of it. Large investments are **phased into small, verifiable +increments** so the client keeps deciding on evidence, rather than a single big +return being promised once and then hidden. + +It builds on two neighbouring standards. [Cost Estimation](/cost-estimation) +puts an honest, ranged number on *what it costs to do the work*; +[Market Research](/market-research) spends a little to *learn whether the thing +is worth building at all*. This policy sits above both and asks the prior +question — **for this client's business, does the return justify the spend, and +how sure are we?** We ground it in named investment-appraisal and value-based +delivery practice rather than house intuition, and right-size it for an SME and +its clients. Deviations are allowed, but — as everywhere in the handbook — they +must be deliberate and justified in the project's design notes. + +This is where OSBR's values meet the client's money. **Be Nice**: we put the +client's return ahead of our order size, and recommend the work that serves +their business best even when it bills us less. **Be Kind**: we record the +accepted case and its assumptions so the teammate who inherits the project, and +the client who signed off, are both protected when a plan has to change. +**Be Strong**: we do the honest thinking up front — stating the effect, its +certainty, and what it depends on — instead of dressing work up with an +impressive-looking number. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as elsewhere in the handbook. + **MUST** / **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a + strong default overridable only with a documented reason. **MAY** marks a + free choice. +* **Named practice.** Where a rule adopts an industry practice (WSJF, TCO, NPV), + the practice is named inline and cited under [References](#6-references). We + adopt the *criteria* of these practices and right-size them for an SME — a + two-week job does not carry the appraisal apparatus of a six-figure platform + decision. + +[[TOC]] + +## 1. Goal + +The goal is to ensure that money the client spends with OSBR goes to the work +that returns the most to *their* business, and that the client can **see the +expected return and its certainty before committing** — not discover it +afterwards. + +Concretely, every proposal OSBR puts to a client must: + +1. **State the expected effect** — what business outcome the investment is + expected to produce (revenue, cost saved, risk reduced, time returned), not + just the feature delivered. A feature is a cost; the outcome it enables is + the return. +2. **State the certainty** — how confident we are in that effect, and what the + effect depends on. A likely-small win beats a maybe-huge one we cannot stand + behind. +3. **Be ranked by return to the client, not by size to us.** The proposal that + bills the most hours is not automatically the one we recommend. Usually it is + not. +4. **Phase large spend into verifiable increments** — each increment delivering + value the client can check before funding the next, rather than one lump + commitment against one distant promised return. + +The point is never to make work look impressive. An honest *"this saves roughly +two staff-days a month, we're fairly confident, payback in about a year"* is +worth more than a precise-looking ROI figure nobody can defend. + +## 2. Responsibility + +- The **proposer** (the engineer or lead scoping the work) frames the work as an + investment: they name the expected business effect, its certainty, and what it + depends on. They own that the case is honest and built around *the client's* + return, not our order size. +- The **reviewer** independently sanity-checks the business case before it + reaches the client — is the claimed effect real, is the certainty stated + fairly, is a cheaper increment being skipped? Business cases are reviewed like + estimates and like code; this is the AI code review the + [Quality Gate](/quality-gate) requires, applied to the *why* of the work. +- The **project lead** ensures proposals reach the client ranked by client + return, that large spend is offered as increments, and that the accepted case + and its assumptions land in the meeting record. They own that we do not + up-sell scope the client's return does not justify. +- The **whole team** feeds realised outcomes back — did the effect actually + land? An investment case nobody checks against reality is a sales pitch we + keep repeating. + +A vendor paid by the hour has a standing incentive to grow scope; **Be Nice** +means we reject that incentive on purpose. Recommending work whose return we +could not defend fails Be Nice, and quietly padding scope fails **Be Strong** — +we didn't do the honest thinking. + +## 3. Practices + +### 3-1. Evaluate every proposal as an investment, up front + +- Before work is committed, a proposal **MUST** be expressed as an investment + case: the **expected business effect**, the **cost to achieve it** (see + [Cost Estimation](/cost-estimation)), and the **certainty** of the effect. +- The effect **MUST** be stated in the client's terms — money made, money saved, + risk lowered, time returned — not merely "we will build feature X." +- Certainty **MUST** be stated honestly and, like an estimate, as a **range or + confidence**, never a single confident figure. A wide, honest range is worth + more than false precision. +- Where the effect is genuinely unknown, the right first move is often **not** a + full estimate but a cheap experiment — hand off to + [Market Research](/market-research) to buy down the uncertainty before quoting + a large build. + +### 3-2. Rank by return to the client, not size to us + +- Proposals **MUST** be ordered by their return to the *client's* business, not + by revenue to OSBR. When a smaller engagement delivers most of the value, we + say so and recommend it. +- Where value, cost, and urgency can be compared across candidate pieces of + work, teams **SHOULD** use **Weighted Shortest Job First (WSJF)**: rank by + *Cost of Delay ÷ job size*, so small high-value work is done first and large + low-value work is deferred or dropped. WSJF is a deliberate structural counter + to the "big project first" bias. +- **Cost of Delay** — what it costs the client *per unit time* to not have this + yet — **SHOULD** be made explicit when sequencing work. Urgent, cheap, + high-value work goes first; it is often not the largest order. +- We **MUST NOT** recommend a larger or longer engagement than the client's + return justifies. The vendor incentive to up-sell scope is real, and it is + named here precisely so that we refuse it. + +### 3-3. Use real appraisal numbers, right-sized + +Investment appraisal has standard, defensible tools. Use them at a depth that +fits the decision — not to impress, but so the client can compare this spend +against alternatives on the same footing. + +- Compare cost against benefit over the asset's life, not just build cost. Where + it matters, account for **Total Cost of Ownership (TCO)** — build *plus* run, + maintenance, licences, and eventual replacement — so a cheap-to-build, + expensive-to-run option is not sold as cheap. +- For investments with a return over time, teams **SHOULD** offer a simple + **ROI**, a **payback period** (how long until the client is made whole), and, + where the horizon is long enough to matter, **Net Present Value (NPV)** — + future returns discounted to today, because money later is worth less than + money now. +- These figures **MUST** carry their assumptions (discount rate, expected effect + size, time horizon) exactly as estimates carry theirs. An NPV with a hidden + discount rate is false precision. Right-size the rigour: a two-week job does + not need a discounted cash-flow model; a six-figure platform decision does. + +### 3-4. Phase large investments into small verifiable increments + +Large upfront commitments concentrate risk and let a distant promised return +hide behind a big cheque. We break them up. + +- Large investments **MUST** be phased into increments, each delivering value + the client can **verify before funding the next**. This is **incremental + funding**: release money in stages against demonstrated results, not one lump + against one far-off promise. +- Each increment **SHOULD** be framed as a **real option**: the client pays a + small amount now to keep the *right, but not the obligation,* to continue — and + can stop, pivot, or expand as evidence arrives. Phasing is what converts an + all-or-nothing bet into a series of cheap, revocable decisions. +- The expected return of each increment **MUST** be visible, not rolled into an + opaque total. Hiding the expected return behind a single large number is + exactly what this policy exists to prevent — it denies the client the + information they need to stop early. +- Sequence the increments to **attack the riskiest, highest-value part first** + (this aligns with WSJF in §3-2 and the riskiest-assumption discipline in + [Market Research](/market-research)), so that if the investment is going to + fail, it fails cheaply and early. + +### 3-5. Value-based delivery, not scope delivery + +- Success **MUST** be measured by value delivered to the client's business, not + by scope shipped or hours billed. A delivered feature nobody uses is a cost + with no return, and we treat it as such. +- Proposals **SHOULD** name how the expected effect will be *observed* after + delivery — the signal that will tell us the return actually landed. An + investment case with no way to check it later is unfalsifiable. +- When realised outcomes come back weaker than the case predicted, that is a + **learning to feed forward** into the next proposal (§2, whole-team), not + something to bury. Honest post-hoc review is how our investment cases stay + trustworthy. + +### 3-6. Record the accepted case in the meeting record + +- The accepted investment case — its **expected effect**, its **certainty**, its + **cost basis**, its **increment plan**, and the **assumptions** each rests on — + **MUST** be captured in the meeting record when agreed with the client, + alongside the estimate under [Cost Estimation](/cost-estimation). +- What is recorded is the **agreement and its reasoning**, not just a headline + number. When an assumption breaks or an increment underperforms, the record is + what makes re-planning fair rather than a dispute over memory. This is the + **Be Kind** clause — it protects the teammate who inherits the project and the + client who signed off. + +## 4. Anti-patterns + +- **Order-size ranking** — recommending the biggest engagement because it bills + the most, not because it returns the most to the client. +- **Scope up-sell** — padding a proposal with work the client's return does not + justify, because we are paid by the hour. +- **Hidden return** — one large commitment against one distant promised payoff, + with the per-increment expected return never broken out, so the client cannot + stop early. +- **False-precision ROI** — a confident single ROI or NPV figure with its + discount rate, effect size, and horizon assumptions hidden. +- **Build-cost-only** — quoting build cost as the investment while ignoring run, + maintenance, and licence cost (TCO). +- **Scope delivered ≠ value delivered** — declaring success because features + shipped, with no check on whether the business effect landed. + +## 5. Related standards + +- [Cost Estimation](/cost-estimation) — putting an honest, ranged number on + *what the work costs*; this policy weighs that cost against the client's + return. +- [Market Research](/market-research) — spending a little to *learn whether the + thing is worth building* before a large investment case is built on guesses. +- [Development Guide](/development-guide) — the Planning & Shaping stage this + policy fronts, and the meeting record the accepted case lands in. +- [Quality Gate](/quality-gate) — the AI code review discipline applied here + to the business case, not just the code. + +## 6. References + +Named investment-appraisal and value-based delivery practice this policy is +grounded in. + +**Investment appraisal** + +- **ROI / Total Cost of Ownership (TCO)** — comparing whole-of-life cost against + benefit, not build cost alone. +- **Net Present Value (NPV) & payback period** — discounting future returns to + present value and measuring time-to-recovery; standard capital-budgeting + appraisal. +- **Business-case discipline** — expressing proposed work as expected benefit + vs. cost with stated assumptions (the case-driven appraisal tradition; the + HM Treasury *Green Book* five-case model is a useful reference point, + right-sized for an SME). + +**Value-based sequencing** + +- **Weighted Shortest Job First (WSJF)** — rank by Cost of Delay ÷ job size — + +- **Cost of Delay** — Donald Reinertsen, *The Principles of Product Development + Flow* — quantifying the cost per unit time of not having a capability yet. + +**Incremental funding / options** + +- **Incremental Funding Method (IFM)** — funding software in value-delivering + increments; Denne & Cleland-Huang, *Software by Numbers*. +- **Real options thinking** — treating each increment as a paid option to + continue, pivot, or stop as evidence arrives. + +**Guarding against the vendor incentive** + +- **Value-based delivery over scope delivery** — measuring success by client + outcome, not hours billed — the deliberate counter to the paid-by-the-hour + incentive to up-sell scope. diff --git a/doc/legal-compliance.md b/doc/legal-compliance.md new file mode 100644 index 0000000..6033976 --- /dev/null +++ b/doc/legal-compliance.md @@ -0,0 +1,330 @@ +# Legal Compliance + +This is the standard for enumerating the legal and regulatory constraints on a +project **before design begins**, and recording them where they will actually be +honoured — the project's risk register. It sits at the planning end of the +lifecycle, alongside the [Development Guide](/development-guide)'s *Planning & +Shaping* stage: that stage says *understand the client's business before you take +requirements*; this standard says *enumerate the legal duties that business +carries before you draw the architecture*. Legal constraints are design premises, +not late surprises. Deviations are allowed, but — as everywhere in the handbook — +they must be deliberate and justified in the project's design notes. + +Almost every expensive compliance failure is a failure of *timing*, not of +intent: the duty was real and knowable, but nobody looked it up until the +architecture was already built around ignoring it. This standard is where OSBR's +values become preventative. **Be Nice**: surfacing a constraint early is a gift +to your future teammate — the design that already assumes consent, notation, and +retention limits is the one nobody has to tear apart later. **Be Kind**: these +rules exist to protect the people whose personal data and money pass through what +we build, so honouring them is a duty owed to those people, not a box-ticking +chore. **Be Strong**: do the unglamorous legal homework up front and tell a +client plainly when the law forbids what they asked for, instead of discovering +it in an audit or a complaint. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as elsewhere in the handbook. + **MUST** / **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a strong + default overridable only with a documented reason. **MAY** marks a free choice. +* **Named practice.** This is the *compliance-by-design* and *privacy-by-design* + posture — building legal obligations into the design from the first line rather + than inspecting for them at the end. Privacy-by-design is codified as **data + protection by design and by default** in + [GDPR Article 25](https://gdpr-info.eu/art-25-gdpr/) and traces to + [Ann Cavoukian's seven foundational principles](https://iapp.org/resources/article/privacy-by-design-the-7-foundational-principles/). + We adopt the *discipline* of these frameworks and right-size it for an SME + engagement — we do not import the headcount or ceremony of their reference + setups. +* **Escalation.** Where a question is genuinely a legal judgement call, escalate + to qualified counsel. This policy makes sure the question gets *asked* in time — + not that engineers answer it alone. + +[[TOC]] + +## 1. Goal + +A cookie banner bolted on after launch, a mandatory online-seller disclosure page +— Malaysia's under the Consumer Protection (Electronic Trade Transactions) +Regulations 2024, or Japan's 特定商取引法に基づく表記 — scrambled together the week +before a store opens, a data flow that turns out to need a telecom-business +notification after the pipes are already laid — each is cheap to design in and +painful to retrofit. The goal here is to move that work to the one +moment it is cheap: + +**Before design begins, enumerate every legal and regulatory requirement that +applies to this project, treat each as a fixed design premise, and record it in +the project's risk register so it is owned, tracked, and re-evaluated like any +other risk.** + +The register itself is the **legal & regulatory register** required by +[ISO/IEC 27001:2022 Annex A control 5.31](https://www.iso.org/standard/27001) +("Legal, statutory, regulatory and contractual requirements shall be identified, +documented and kept up to date") — the same single [risk +register](/supply-chain-risk) the project already keeps, not a parallel document +that quietly falls out of date. + +## 2. Responsibility + +- The **project lead** owns the legal & regulatory enumeration: that it happens + *before* design, that it is recorded in the [risk register](/supply-chain-risk), + and that each requirement has a named owner and a re-evaluation trigger. +- The **requirement owner** (named per entry) is accountable for one legal + requirement: that the design satisfies it, that evidence of compliance exists, + and that its re-evaluation date is honoured. +- **Every developer and collaborator** raises a newly-triggered legal duty the + moment a design change creates one — a new data class, a new payment flow, a + new external transmission, a new jurisdiction. Compliance is not a lawyer's + phase bolted onto the end; it is a shared reflex during planning. If you can see + a legal constraint, you own naming it. +- The **client** confirms the business facts that decide applicability (who the + users are, where they are, what is sold, what data is collected) and co-owns the + duties that are legally theirs as the operator or data controller. +- Genuine legal judgement calls — does *this* service count as a 電気通信事業? is + *this* processing high-risk enough to need a DPIA? — MUST be escalated to + qualified counsel. This policy ensures the question is asked in time, not that + engineers answer it alone. + +## 3. Practices + +Named, established practice, right-sized for an SME engagement. Import the +discipline of the reference material, not its headcount or ceremony. + +### 3-1. Enumerate applicable law from the business facts, before design + +You cannot design around constraints you have not listed. Before design begins, +the project MUST produce a **legal & regulatory enumeration** derived from the +concrete facts of the engagement (established during the [Development +Guide](/development-guide)'s *Planning & Shaping* stage): + +- **Who are the users, and where are they?** Jurisdiction of the users — not of + the company — is what pulls in most obligations. Users in Malaysia pull in the + [PDPA (Personal Data Protection Act 2010)](https://www.pdp.gov.my/); users in + Japan pull in the [APPI (個人情報保護法)](https://www.ppc.go.jp/en/legal/); + EU/EEA users pull in [GDPR](https://gdpr-info.eu/); users elsewhere pull in + their own regimes (for example the CCPA/CPRA for California consumers). +- **What data is collected, for what purpose, and how long is it kept?** This + decides consent, notification, purpose-limitation, and log-retention duties. +- **Is anything sold, and to consumers?** Consumer online sales pull in an + online-seller disclosure duty. In Malaysia the **Consumer Protection + (Electronic Trade Transactions) Regulations 2024** require the seller to + disclose its name/company, business (company) registration number, contact + (email, phone, address), and the full price, with the **Electronic Commerce + Act 2006** governing the validity of the electronic contract; in Japan the + [特定商取引法 (Act on Specified Commercial Transactions)](https://www.no-trouble.caa.go.jp/) + and its mandatory 特定商取引法に基づく表記 (seller notation). +- **Does the service transmit user information to third parties, or operate as a + communications service?** This pulls in Japan's telecom rules (§3-3). +- **Are card payments handled?** This pulls in [PCI DSS](https://www.pcisecuritystandards.org/). + +The output is a list of *named* obligations tied to *named* business facts — not +"we should probably be compliant", but "Malaysian users → PDPA applies → notice +and choice, security, and retention limits are design premises; EU users → GDPR +adds lawful basis, privacy notice, and data-subject rights". + +### 3-2. The starter checklist — enumerate, then justify each in or out + +Every project MUST walk this list and, for each item, record either **how it will +be satisfied** or **why it does not apply**. An explicit "not applicable, +because…" is a required answer, not a silent omission. + +| Requirement | Trigger | Named source | +| --- | --- | --- | +| **Terms of Service** | Any service users sign up for or transact through | Contract law; consumer-protection statutes | +| **Privacy policy / privacy notice** | Any collection of personal data | [PDPA](https://www.pdp.gov.my/) Notice and Choice Principle; [APPI](https://www.ppc.go.jp/en/legal/) purpose-of-use notification; [GDPR Arts. 13–14](https://gdpr-info.eu/art-13-gdpr/) | +| **Data-subject / individual rights** | Processing personal data of Malaysian, Japanese, or EU/EEA users | [PDPA](https://www.pdp.gov.my/) Access Principle (access, correction, withdrawal, portability); [APPI](https://www.ppc.go.jp/en/legal/) 開示・訂正・利用停止; [GDPR Arts. 6, 15–22](https://gdpr-info.eu/art-6-gdpr/) | +| **Consumer privacy rights (US)** | Personal data of California (or similar-state) consumers | CCPA / CPRA | +| **Online-seller disclosure (Malaysia)** | Consumer online sales from Malaysia | Consumer Protection (Electronic Trade Transactions) Regulations 2024; Electronic Commerce Act 2006 | +| **特定商取引法 notation (表記)** | Consumer online sales from Japan | [特定商取引法](https://www.no-trouble.caa.go.jp/) | +| **Cookie / tracker consent + disclosure** | Cookies or trackers beyond the strictly necessary | [ePrivacy Directive 2002/58/EC](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32002L0058); Japan external-transmission rules (§3-3) | +| **Telecom-business notification** | Operating a telecommunications service in Japan | [電気通信事業法 (Telecommunications Business Act)](https://www.soumu.go.jp/main_sosiki/joho_tsusin/eng/) | +| **PCI DSS controls** | Storing, processing, or transmitting cardholder data | [PCI DSS](https://www.pcisecuritystandards.org/) | +| **Log-retention duties (and limits)** | Any logging of user activity | Retention obligations vs. GDPR storage limitation (§3-5) | +| **DPIA** | High-risk processing (§3-4) | [GDPR Art. 35](https://gdpr-info.eu/art-35-gdpr/) | + +The checklist is a floor, not a ceiling. It is a prompt to think, not the full +universe of law — sector rules (finance, health, education) and other +jurisdictions are enumerated the same way. + +### 3-3. Get the Malaysia- and Japan-specific and cookie duties right + +These are the ones most often missed by teams anchored on GDPR alone. + +- **Consumer Protection (Electronic Trade Transactions) Regulations 2024 + (Malaysia).** Consumer online sales from Malaysia require the seller to + publish its name/company, business (company) registration number, contact + (email, phone, address), and the full price; the **Electronic Commerce Act + 2006** governs the validity of the resulting electronic contract and records. + Like Japan's notation below, this is a design premise — a page that must exist + and be reachable, decided before the checkout flow is drawn. +- **特定商取引法 (Specified Commercial Transactions Act).** Consumer online sales + require a published **特定商取引法に基づく表記**: seller identity, address, + contact, price, delivery, and return/cancellation terms. It is a design premise + for any store — a page that must exist and must be reachable, decided before the + checkout flow is drawn. +- **電気通信事業法 (Telecommunications Business Act).** A service that mediates + others' communications (messaging, some SaaS, some platforms) may be a + **電気通信事業** requiring **notification (届出) to the MIC**. Whether a given + service qualifies is a legal judgement — the duty here is to *raise the question* + before architecture assumes the answer is "no". +- **External-transmission rules (外部送信規律).** The 2023 amendment to the + Telecommunications Business Act requires services to **disclose to users when + their information is transmitted to third parties** (analytics, ad tags, embedded + widgets) — Japan's functional counterpart to the ePrivacy cookie-consent regime. + If the design embeds third-party tags, the disclosure mechanism is a design + premise. +- **Cookie / ePrivacy consent (EU).** Under the [ePrivacy + Directive](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32002L0058), + non-essential cookies and trackers require **prior consent**. That means consent + MUST be capturable *before* the tracker fires — an architectural constraint on + how analytics and tags load, not a banner you sprinkle on at the end. + +### 3-4. Decide DPIA up front, not after launch + +A **Data Protection Impact Assessment** is required by [GDPR Article +35](https://gdpr-info.eu/art-35-gdpr/) when processing is **likely to result in a +high risk** to individuals — large-scale profiling, systematic monitoring, or +large-scale processing of special-category data are the canonical triggers (see +the [EDPB / WP29 DPIA guidelines](https://ec.europa.eu/newsroom/article29/items/611236)). + +- The project MUST decide **whether a DPIA is required during planning**, because + a DPIA that concludes "this is too risky as designed" is only useful *before* + the design is built. +- A DPIA is the privacy-by-design mechanism made concrete: it identifies the risk + to data subjects, the measures that reduce it, and the residual risk that + remains — the same shape as a [risk-register](/supply-chain-risk) entry, and it + SHOULD live there. +- Where APPI applies, treat the equivalent risk-of-leakage assessment and the + Personal Information Protection Commission's guidance as the parallel + obligation. + +### 3-5. Treat log retention as a two-sided duty + +Logs attract *two* opposing legal pressures, and the design MUST satisfy both: + +- **A duty to retain** — some records must be kept for a defined minimum + (accounting/tax records, sector-specific audit trails, and communications + records where a telecom obligation applies). Losing them too early is the + violation. +- **A duty not to over-retain** — Malaysia's [PDPA](https://www.pdp.gov.my/) + **Retention Principle**, APPI's purpose-limitation principle, and [GDPR Article + 5(1)(e)](https://gdpr-info.eu/art-5-gdpr/) **storage limitation** require that + personal data is **not kept longer than necessary**. Keeping everything forever + is the violation. + +The resolution is a **defined, per-data-class retention period** chosen at design +time and recorded in the register — long enough to meet retention duties, short +enough to meet minimisation duties. "We keep all logs indefinitely" is a design +decision that fails both tests and MUST be caught before, not after, the logging +pipeline is built. The concrete controls that implement a retention schedule live +in [Data Protection](/data-protection); this standard is where the *duty* to have +one is fixed. + +### 3-6. Record every requirement in the risk register + +Enumeration is worthless if it lives in a one-off document nobody revisits. Each +identified legal requirement MUST become an entry — or a linked set of entries — +in the project's single [risk register](/supply-chain-risk), carrying the same +fields every entry carries: + +| Field | For a legal requirement | +| --- | --- | +| **Description** | The obligation and what triggers it: *Malaysian users → PDPA → the seven Personal Data Protection Principles are design premises; EU users → GDPR Art. 25 → data protection by design and by default is required.* | +| **Likelihood / Impact** | The risk of non-compliance — how likely to be caught, how bad the consequence (fine, injunction, reputational, client harm). | +| **Countermeasures** | The design and process controls that satisfy the obligation (consent flow, notation page, DPIA, retention schedule). | +| **Residual risk (named & accepted)** | Any remaining exposure, explicitly accepted by an authorised named person — including "we sought counsel and this is the agreed interpretation". | +| **Owner** | The single named requirement owner. | +| **Re-evaluation date / trigger** | When it is revisited; and the design changes that force an early revisit. | + +This makes legal compliance **one kind of risk among others**, handled by the +machinery the project already runs, rather than a separate track. It is the +[ISO/IEC 27001:2022 control 5.31](https://www.iso.org/standard/27001) legal +register and control 5.34 (privacy and protection of PII), made operational. + +## 4. Rules summary (MUST / SHOULD) + +- A project **MUST** produce a legal & regulatory enumeration from the business + facts **before design begins** (§3-1). +- Every item on the §3-2 checklist **MUST** be answered explicitly — how it is + satisfied, or a reasoned "not applicable" (§3-2). +- Consent and disclosure mechanisms (cookie/ePrivacy, external-transmission) + **MUST** be treated as architectural premises capturable *before* trackers fire, + not retrofitted (§3-3). +- The project **MUST** decide during planning whether a **DPIA** is required, and + run it before the design it assesses is built (§3-4). +- Every data class that is logged **MUST** have a defined retention period that + satisfies both retention duties and storage-limitation/minimisation duties + (§3-5). +- Every identified legal requirement **MUST** be recorded as an owned, + re-evaluated entry in the project's single [risk register](/supply-chain-risk) + (§3-6). +- Genuinely legal judgement calls (telecom-business applicability, DPIA necessity, + lawful-basis choice) **MUST** be escalated to qualified counsel (§2). +- Projects **SHOULD** revisit the enumeration whenever a design change alters the + business facts that decided applicability (§5). + +## 5. Re-evaluation triggers + +Applicable law is not fixed for the life of a project — the *facts* that decide it +change. A project **MUST** re-evaluate the affected legal entries, and add new +ones, whenever any of the following happens, without waiting for the scheduled +date: + +- **A new user jurisdiction** — the service opens to users in Malaysia, Japan, the + EU, a US state, or elsewhere it did not serve before, pulling in that + jurisdiction's regime. +- **A new data class or purpose** — the project starts collecting or using a new + category of personal data, or uses existing data for a new purpose (re-checks + consent, notice, DPIA). +- **A new payment or commerce flow** — card handling (PCI DSS) or consumer sales + (Malaysia's Consumer Protection (Electronic Trade Transactions) Regulations + 2024; Japan's 特定商取引法) enters scope. +- **A new external transmission or third-party tag** — analytics, ads, or embeds + that transmit user information (external-transmission disclosure, cookie + consent). +- **A change in the service's nature** — it begins mediating communications + (possible telecom notification) or changes retention behaviour. +- **A change in the law itself** — a regulation is amended (as the + Telecommunications Business Act was in 2023); the register entry's source has + moved. + +## References + +The authoritative regimes this policy is grounded in. + +**Compliance- and privacy-by-design (the frame)** + +- Privacy by Design — the 7 Foundational Principles (Ann Cavoukian) — +- GDPR Article 25 — Data protection by design and by default — +- ISO/IEC 27001:2022 — Annex A controls 5.31 (legal, statutory, regulatory & contractual requirements) and 5.34 (privacy and protection of PII) — + +**Malaysia** + +- Personal Data Protection Act 2010 (Act 709) and the seven Personal Data Protection Principles — Personal Data Protection Department (JPDP) — +- Personal Data Protection (Amendment) Act 2024 (Act A1727) — data-controller terminology, mandatory breach notification, Data Protection Officer, data portability, cross-border transfer — +- Consumer Protection (Electronic Trade Transactions) Regulations 2024 — online-seller disclosure (identity, business registration number, contact, full price); in force 25 December 2024, revoking the 2012 version +- Electronic Commerce Act 2006 (Act 658) — validity of electronic contracts and records + +**Japan** + +- APPI (個人情報保護法) — Personal Information Protection Commission — +- 特定商取引法 (Act on Specified Commercial Transactions) — +- 電気通信事業法 (Telecommunications Business Act), incl. the 2023 external-transmission rules (外部送信規律) — Ministry of Internal Affairs and Communications — + +**EU / international data protection** + +- GDPR (Regulation (EU) 2016/679) — full text — +- GDPR Article 35 — Data Protection Impact Assessment — +- EDPB / WP29 — DPIA guidelines (WP248) — +- ePrivacy Directive 2002/58/EC (cookie consent) — + +**Payments** + +- PCI DSS — Payment Card Industry Data Security Standard — + +**Related OSBR standards** + +- [Development Guide](/development-guide) — the *Planning & Shaping* stage where the business facts that decide legal applicability are established. +- [Supply-Chain Risk](/supply-chain-risk) — the single risk register these requirements are recorded in, and the likelihood × impact machinery that ranks non-compliance against every other risk. +- [Data Protection](/data-protection) — the concrete controls (access control, logging, retention) that many of these obligations are satisfied by. +- [Quality Gate](/quality-gate) — the AI code review that checks these premises were honoured in the delivered design, not just listed. diff --git a/doc/market-research.md b/doc/market-research.md new file mode 100644 index 0000000..124c1ac --- /dev/null +++ b/doc/market-research.md @@ -0,0 +1,205 @@ +# Market Research + +This policy defines what OSBR does **before** it takes a single requirement from +a client. We proactively study the client's industry, its market structure, and +the actual work people do inside it — so that when we finally sit down to gather +requirements, we already speak the field's language and understand the business +the software has to serve. It sits at the front of the [Development +Guide](/development-guide)'s Planning & Shaping work: this policy is about +understanding the problem, so that everything downstream is about building the +right solution. + +We do not treat the client's stated request as the specification. A stated +request is a **symptom and a hypothesis** — evidence about a deeper business +need, not the need itself. Serving the client well is **Be Nice** and **Be +Kind**: we serve the real need, even when it differs from the literal ask. Doing +that takes **Be Strong** — the discipline to research before building, and the +honesty to tell a client that their stated solution is not the one their +business needs. + +## How to read this policy + +* **Requirement levels** follow RFC 2119. **MUST** / **MUST NOT** are absolute. + **SHOULD** / **SHOULD NOT** state a strong default overridable only with a + documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). We adopt the *criteria* + of each practice and right-size it for an SME engagement — we do not import the + headcount or ceremony of the reference material. + +[[TOC]] + +## 1. Goal + +The goal of market research at OSBR is to **enter every engagement already fluent +in the client's world**, so that requirements-gathering refines a shared +understanding instead of starting from zero. Concretely, before requirements are +taken we aim to know: + +- **The industry** — how this sector makes money, who the players are, and what + forces shape it. We use Porter's Five Forces and the value chain as the lens + for market structure: suppliers, buyers, substitutes, new entrants, rivalry, + and where value is actually added along the chain. Software that ignores this + optimises the wrong link. +- **The real workflows and pain points** — what people actually do all day, in + their real context, versus the idealised process on the org chart. This is + grounded in contextual inquiry and the broader UX-research practice. +- **The job to be done** — the progress the client and their users are trying to + make, independent of any particular solution. *People don't want a + quarter-inch drill; they want a quarter-inch hole.* +- **The field's own language** — the exact terms, units, artefacts, and + edge-cases practitioners use, captured verbatim as the raw material for the + project's [domain terminology](/domain-terminology). + +The output of this phase is not a signed spec. It is a **shared, evidence-based +understanding of the client's business** that de-risks everything downstream — +because most expensive project failures are failures of understanding the +problem, not of building the solution. + +## 2. Responsibility + +Whoever leads an engagement owns this research. It is not optional homework, and +it is not the client's job to spoon-feed us. + +- The engagement lead **MUST** complete a market-and-workflow survey before the + first formal requirements session, and record it where the whole team — human + and AI — can read it. +- We **MUST NOT** take a stated request at face value as the specification. Every + stated request is logged together with the **underlying job** it is trying to + satisfy, and the two are tracked separately. +- We **MUST** bring the field's own terminology back into the project as + candidate terms for its [domain terminology](/domain-terminology) — not + paraphrased into our own words, which quietly discards meaning. +- We **MUST** be honest and kind when research contradicts the client's stated + solution: surface the gap early, with evidence, framed as service to their + business — not as being clever at their expense. This is **Be Nice** and **Be + Kind** expressed as professional courage (**Be Strong**), not as quiet + compliance. +- This work is a **human ⇄ AI collaboration**. AI agents fan out the desk + research, structure findings, and draft the domain glossary at speed; humans + own the field contact, judgement, and the relationship with the client. + Neither half is optional. + +## 3. Practices + +### 3-1. Desk research first — industry and market structure + +Before talking to anyone, build a map of the sector. + +- **MUST** analyse market structure with **Porter's Five Forces and the value + chain**: who the client's suppliers, buyers, substitutes, and rivals are, and + where in the value chain the client actually captures value. +- **SHOULD** identify the incumbents, their products, and the standard tools + practitioners already use — the software we build competes with, or must + interoperate with, these. +- **SHOULD** collect the industry's regulations, standards bodies, and compliance + constraints as first-class inputs, not late surprises. + +### 3-2. Study the real work, in context + +Idealised process diagrams lie; watch the actual work. + +- **SHOULD** run lightweight **contextual inquiry** — observe real users doing + real tasks in their real environment, and let them teach you (the + master/apprentice stance) rather than interviewing them in a meeting room. +- **MUST** capture the pain points, workarounds, and **shadow processes** — the + private spreadsheets and side-channels people have built to survive the current + process. These are the sharpest signal of unmet need. +- **SHOULD** choose research methods deliberately from the established + UX-research menu (interviews, field studies, diary studies, surveys) — + attitudinal versus behavioural, qualitative versus quantitative — matching the + method to the question. + +### 3-3. Frame needs as jobs, not features + +- **MUST** express each finding as a **Job-To-Be-Done**: *"When [situation], I + want to [motivation], so I can [expected outcome]."* This separates the durable + need from the disposable solution the client happened to ask for. +- **SHOULD** trace every stated request down to the job beneath it, and validate + that job with evidence from §3-1 and §3-2 before it becomes a requirement. A + request is a hypothesis; the job is what we are actually serving. + +### 3-4. Harvest the field's language + +- **MUST** record domain terms **verbatim**, with the practitioner's own + definition, units, and edge-cases — the seed of the project's [domain + terminology](/domain-terminology). One term, one meaning, used identically by + code, docs, and client. +- **SHOULD** run collaborative discovery — **Event Storming** or example mapping + — with the client's domain experts to surface the events, commands, and + boundaries in their own words. This doubles as the first draft of the domain + model. +- **MUST** treat conflicts and synonyms in the terminology as findings, not + noise: two words for one thing (or one word for two) usually marks a boundary + between sub-domains. + +### 3-5. Run a structured discovery, not an ad-hoc chat + +Use a named inception format so nothing is skipped and the client is a +participant, not a spectator. + +- **SHOULD** adopt one of the established discovery frameworks and right-size it: + - **Lean Inception** — a week-shaped sequence for aligning on the product's + vision, personas, and a lean MVP. + - **Design Sprint** — a time-boxed map → sketch → decide → prototype → test + loop when the problem space is genuinely uncertain. + - **Working Backwards (PR-FAQ)** — draft the future press release and customer + FAQ *first*, forcing clarity on the customer benefit before any building. +- **MUST** produce, from whichever format is used, an artefact that states the + customer, the job, and the expected outcome in the client's own language — + reviewable by the client and by AI agents downstream. + +### 3-6. Feed research into requirements, then keep it honest + +- **MUST** hand the survey, the JTBD list, and the draft domain glossary into the + requirements phase as its starting inputs — requirements refine this + understanding, they do not replace it. This is the understanding the [Quality + Gate](/quality-gate) later holds the delivered work against: a solution can + only be judged fit if the problem it serves was understood first. +- **SHOULD** revisit the research when reality contradicts it; a survey that is + never updated becomes a comfortable fiction. As in + [Verify Before Building](/verify-before-building), verify against ground truth — + the field, the data, the client's actual numbers — not against our earlier + assumptions. + +The lazy version of this phase is to write down the client's request and start +coding. It feels efficient and it is the most common way projects fail. +Researching first, and telling a client the honest result even when it is not +what they asked for, is **Be Strong** in service of **Be Nice** and **Be Kind**: +we protect the client's business, not just our own comfort. + +## References + +Named practice this policy draws on, chosen because each is publicly documented +and adoptable by a small team. + +**Market structure** + +- Michael Porter, "How Competitive Forces Shape Strategy" (Five Forces, value chain), HBR — + +**Customer need & jobs** + +- Clayton Christensen et al., "Know Your Customers' Jobs to Be Done", HBR — + +**User & market research** + +- Nielsen Norman Group, "Which UX Research Methods?" — +- Beyer & Holtzblatt, *Contextual Design* (contextual inquiry) — + +**Domain discovery & language** + +- Eric Evans, *Domain-Driven Design Reference* (ubiquitous language) — +- Alberto Brandolini, Event Storming — + +**Discovery & inception formats** + +- ThoughtWorks / Martin Fowler, "Lean Inception" — +- Google Ventures, Design Sprint — +- Amazon, "Working Backwards" (PR-FAQ) — + +**Related OSBR standards** + +- [Development Guide](/development-guide) — the Planning & Shaping work this research feeds. +- [Domain Terminology](/domain-terminology) — where the field's harvested language becomes the project's ubiquitous language. +- [Quality Gate](/quality-gate) — the gate that judges whether the delivered solution serves the understood problem. +- [Verify Before Building](/verify-before-building) — verifying against ground truth rather than earlier assumptions. diff --git a/doc/meeting-recording.md b/doc/meeting-recording.md new file mode 100644 index 0000000..e7d4070 --- /dev/null +++ b/doc/meeting-recording.md @@ -0,0 +1,198 @@ +# Meeting Recording + +This standard governs how OSBR records and transcribes meetings: **only with +prior consent from everyone present**, and only for the purpose everyone agreed +to. A decision made in a meeting and remembered only in people's heads gets +misremembered, disputed, or silently reversed; "I think we agreed to…" is not +good enough six months on. Done right, the recording becomes a searchable +**decision record** — usually produced with the help of AI transcription (see +the [AI Usage Guideline](/ai-usage-guideline)) — that the client and the team +both trust. It is bound by the consent and retention discipline of the [Data +Protection Policy](/data-protection), and the records it produces link back into +the [Development Guide](/development-guide)'s tickets and pull requests. +Requirement levels follow RFC 2119: **MUST** / **MUST NOT** are absolute, +**SHOULD** states a strong default overridable only with a documented reason, +**MAY** marks a free choice. + +Recording is where our values meet a hard boundary. **Be Nice**: nobody has to +reconstruct a meeting from memory, and the client can always retrieve the exact +record of what was decided and why. **Be Kind**: we never record someone who did +not agree to it — consent comes first, every time, with no exception dressed up +as convenience. **Be Strong**: we build the shared record deliberately, before a +dispute needs it, so that under scrutiny the truth is already written down and +attributable. + +[[TOC]] + +## 1. Goal + +The goal is to **preserve the decision-making process, not just the +conclusion**, as a single shared source of truth. The transcript is the record +of *what was said*; the decisions and action items extracted from it are the +record of *what was agreed*. When the transcript, a ticket, and someone's memory +disagree, the decision record is the one that governs. + +This exists to serve, not to surveil. A recording captures a conversation people +chose to have on the record, and the data is used **only within the scope the +client agreed to**. + +## 2. Responsibility + +- The **meeting owner (OSBR side)** MUST obtain and confirm prior consent from + every participant before recording starts, state the purpose and scope, and + stop recording if anyone declines. +- **Project / Engineering** runs the transcription pipeline, stores transcripts + and summaries in the access-controlled knowledge base, and enforces retention + and deletion — including confirming the tooling's own data handling matches the + agreed scope. +- **Data protection** honours access, correction, and deletion requests over + recordings and transcripts, retains only for the agreed period, and confirms + the consent model and any vendor's data-processing terms are acceptable for the + applicable jurisdiction (per the [Data Protection Policy](/data-protection)). +- **Every participant** — client or OSBR — MAY decline to be recorded or ask that + a portion be off the record. That request is honoured without penalty. + +## 3. Practices + +### 3-1. Prior consent is a precondition, not a formality + +Recording MUST NOT begin until **every participant has given prior, informed +consent**. This is both a legal requirement and a trust requirement. + +- The meeting owner MUST announce, **before recording starts**, that the meeting + will be recorded and transcribed, **why** (to preserve decisions as a shared + record), and **who** will have access. Recording begins only after agreement. +- Consent MUST be **prior** and **affirmative** — an announced "I'm starting the + recording now, any objections?" with a real pause, not a recording that + silently began before anyone was asked. Silence after a genuine, clearly-heard + request MAY count as consent only where everyone could realistically object; + when in doubt, ask each person explicitly. +- The consent itself SHOULD be captured as a record — the opening seconds of the + recording where consent is given, or a written agreement. +- Any participant MUST be able to **decline** or ask that a segment be **off the + record**, and that MUST be honoured immediately, with no penalty. + +**Why "prior" and "all-party" matter legally.** Many jurisdictions require +all-party (two-party) consent to record a conversation, and the strictest +applicable law governs a multi-jurisdiction call. As a Malaysian company, +OSBR starts with Malaysia's **Personal Data Protection Act 2010 (PDPA)**: a +recording of an identifiable person is personal data, so the PDPA's **Notice and +Choice** principle applies — give notice and obtain consent **before** recording, +and use it only for the stated purpose. Japan's **APPI** likewise treats a +recording of an identifiable person as personal information whose acquisition and +use must stay within a stated purpose (利用目的); the EU **GDPR** requires consent +that is specific, informed, and unambiguous. The safe, universal rule that +satisfies all of these: **get everyone's consent, in advance, on the record.** + +### 3-2. Use only within the client-agreed scope + +The recording and everything derived from it MUST be used **only for the purpose +the client agreed to** — preserving the decision record for that engagement. + +- The transcript, summary, and audio MUST NOT be repurposed — not for training + models outside the agreed scope, not for marketing, not for sharing with other + clients or teams — without fresh, specific consent. This is **purpose + limitation** under Malaysia's PDPA, Japan's APPI, and the GDPR. +- Access MUST be limited to the people who need it for the engagement, under the + access controls of the [Data Protection Policy](/data-protection). +- Where the transcription tool is a third party, its **data-processing terms** + MUST be verified before client data flows through it — where the audio and text + are stored, whether the vendor trains its models on content, and how deletion is + honoured (see the [AI Usage Guideline](/ai-usage-guideline)). A tool that trains + on client audio by default is not acceptable for scoped client data without + explicit client agreement. +- Retention MUST be bounded: transcripts and recordings are kept only for the + agreed period and then deleted. Deletion MUST propagate to the vendor, not just + OSBR's copy. + +### 3-3. Capture decisions as data, not just prose + +A wall of transcript text is searchable but not yet *useful*. Each meeting record +SHOULD distil the raw transcript into **structured decisions and action items** — +a decision as a first-class record with attributes, not a paragraph someone has +to re-read. + +- Each summary SHOULD extract, at minimum: the **decisions made**, the **options + considered and rejected** (the reasoning, not only the outcome), **action items + with owners**, and **open questions** deferred to later. +- Each decision SHOULD be attributable and dated, so the record answers not just + *what* was decided but *when*, *by whom*, and *why the alternatives were set + aside*. +- The structured summary is what makes the record a genuine single source of + truth: the transcript is the evidence; the decision log is the answer. + +### 3-4. Make the record searchable and single-source + +The transcripts and summaries MUST be stored in a **searchable knowledge base** +so the record is retrievable, not merely archived. + +- The record MUST be **full-text searchable** across meetings, so "what did we + decide about authentication?" returns the relevant moment across every meeting, + not a folder of dated audio files nobody opens. +- Each record SHOULD be **cross-linked** to the artefacts it drove — the tickets, + pull requests, and design docs of the [Development + Guide](/development-guide) — so a decision and its implementation are traceable + in both directions. +- There MUST be **one** canonical store for meeting decisions per engagement. + Scattering the same decision across email, chat, and three people's notes + recreates exactly the ambiguity this policy exists to remove. + +### 3-5. Shared with the client, not held over them + +The record is **shared** — it belongs to the relationship, not to OSBR alone. + +- The client SHOULD receive the summary (and, on request, the transcript) of + meetings they took part in. It is a mutual reference, not OSBR's private + evidence file. +- Corrections MUST be possible: if a participant says the transcript misheard or + misattributed something, the correction is recorded (appended, not silently + overwritten), so the record stays honest and the original remains auditable. + +## 4. Minimum record — checklist + +A meeting recording is legitimate only if **all** of the following hold: + +- [ ] **Prior consent** obtained from **every** participant, before recording began +- [ ] Purpose and access **stated** to participants up front +- [ ] Consent itself **captured** (recorded statement or written agreement) +- [ ] Vendor **data-processing terms** verified acceptable for client data (storage, training, deletion) +- [ ] Transcript + **structured summary** (decisions, rejected options, action items, owners) produced +- [ ] Stored in the **access-controlled, searchable** knowledge base as the single source of truth +- [ ] Used **only within the client-agreed scope**; retention bounded and deletion propagated + +If prior consent cannot be shown, there is no recording — full stop. + +## References + +**Consent law** + +- **All-party (two-party) consent** — many jurisdictions require every party to a + conversation to consent before it may be recorded; the strictest applicable law + governs a multi-jurisdiction call. +- **PDPA (Personal Data Protection Act 2010)** — Malaysia's data-protection law + (amended 2024; regulator: Personal Data Protection Commissioner, + ). A recording of an identifiable person is personal + data, so the **Notice and Choice** principle requires notice and consent before + recording, the **Retention** principle limits keeping it beyond need, and use + stays bound to the stated purpose — a consent-based regime. +- **APPI (個人情報の保護に関する法律)** — Japan's Act on the Protection of Personal + Information: a recording of an identifiable person is personal information, and + its acquisition and use must stay within a stated purpose (利用目的). +- **GDPR — Regulation (EU) 2016/679** — Art. 4(11) & Recital 32 (consent must be + specific, informed, unambiguous, by clear affirmative act); Art. 5(1)(b) + (purpose limitation); Art. 6 (lawful basis). + +**Practice** + +- **Decision log / "decisions as data"** — recording each decision as a discrete, + queryable record with its context, alternatives, and consequences (a lightweight + cousin of the Architecture Decision Record). +- **Searchable knowledge base / single source of truth** — one canonical, + full-text-searchable store governs each engagement's decisions, so scattered + notes never compete with the record. + +**Related OSBR standards** + +- [Data Protection Policy](/data-protection) — consent, access control, retention, and deletion. +- [AI Usage Guideline](/ai-usage-guideline) — using AI transcription tooling on client data. +- [Development Guide](/development-guide) — the tickets and pull requests a decision record links to. diff --git a/doc/modeless-design.md b/doc/modeless-design.md new file mode 100644 index 0000000..2241548 --- /dev/null +++ b/doc/modeless-design.md @@ -0,0 +1,138 @@ +# Modeless Design + +This policy defines OSBR's default posture for interaction: **modelessness is the default; a mode is the exception that must justify itself.** A *mode* is any state where the same user action produces a different result depending on where the interface currently is — the classic example being a modal dialog that seizes the screen and refuses every action but its own. Whenever a design reaches for a mode, the burden is on the design to prove the mode is warranted, and to record why. It sits under the [Design Guidelines](/design-guidelines) and sharpens one thread of [Interaction Design](/interaction-design) down to the altitude of a single interaction. Deviations are allowed, but — as everywhere in the handbook — they must be deliberate and recorded in the project's design notes. + +We do not invent our own interaction theory. We stand on named, published practice — Jef Raskin's *The Humane Interface* on modelessness, Larry Tesler's lifelong campaign against modes, the Nielsen Norman Group's modal-dialog guidance and the *user control and freedom* heuristic, Apple's Human Interface Guidelines and Google's Material dialog guidance, and the W3C WAI-ARIA Authoring Practices dialog pattern — and apply it at the altitude of a single interaction. The accessibility obligations a mode must still satisfy when it does exist live in [Accessibility](/accessibility); this page is where interaction design decides, one interaction at a time, whether a mode should exist at all. + +## How to read this policy + +* **Requirement levels** follow RFC 2119. **MUST** / **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a strong default overridable only with a documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is named inline and cited under [References](#6-references). We adopt the *criteria* of these sources and right-size them for an SME — not the tooling or scale behind their reference setups. + +[[TOC]] + +## 1. Goal + +The goal is an interface where **the user is always in a place they chose to be, and can always leave it.** A mode — most visibly a modal dialog — takes control away from the user: it decides what they may do next and blocks everything else. The default is to not do that. When the interface must impose a mode, it does so briefly, for a reason the user can see, and with the door left unlocked. + +Raskin's argument is the root of this policy: **modes are a principal cause of user errors**, because a person acts on their intent while the system acts on its hidden state, and the two diverge (Jef Raskin, *The Humane Interface*, 2000). Larry Tesler spent his career on the same point — his "NOMODES" licence plate and the maxim *"Don't mode me in"* name the whole discipline. A modeless interface lets the user's habits transfer everywhere because the same gesture always means the same thing. + +::: tip This is what "Be Kind" looks like in interaction +OSBR's values are **Be Nice**, **Be Kind**, and **Be Strong**. **Be Nice** is, when a mode is genuinely justified, making it painless — obvious exits, preserved input, a readable background — so even the necessary mode respects the person inside it. In interaction design, **Be Kind** is literal: **never trap the user in a mode.** A modal the user cannot escape, that eats their half-typed input when it closes, or that hides the context they were reading, is unkindness encoded in software. **Be Strong** is refusing the lazy modal that exists only because it was easier to build than an inline flow. +::: + +## 2. Responsibility + +Whoever designs an interaction owns the decision to introduce a mode. Concretely, that person or pair MUST: + +- Treat **modelessness as the default** and reach for a modal only when the interaction fits one of the justified cases (§3-1). +- **Record the trade-off** in the PR or an ADR whenever a modal is introduced — the justification is part of the change, not tribal memory (§3-1). +- Hold view and selection **state in the URL**, not in ephemeral modal state, so a place is addressable, shareable, and survivable across reload (§3-2). +- Give every modal **more than one exit** and **preserve in-progress input** on close (§3-3). +- **Trap focus** inside a modal for assistive-technology correctness while keeping the **background visible and readable** (§3-4). + +This is not a hand-off to a separate role who audits modals at the end. The person who introduces the mode is the person who justifies it, because the two are the same decision. + +## 3. Practices + +### 3-1. Modeless by Default; a Modal Only for Justified Cases + +The NN/g guidance on [modal & nonmodal dialogs](https://www.nngroup.com/articles/modal-nonmodal-dialog/) is blunt: modals interrupt and demand action, so they should be reserved for the few situations that genuinely need to stop the world. Everything else — editing, creating, filtering, previewing — SHOULD happen inline, in a panel, on its own page, or in a nonmodal surface the user can ignore and return to. + +A modal is justified only when the interaction is one of these: + +- **Irreversible confirmation** — a destructive or non-undoable action (delete, permanent send, irrecoverable overwrite) where a deliberate stop-and-confirm prevents a costly mistake. NN/g's *error prevention* heuristic backs the interruption here; a reversible action does **not** qualify — prefer an undo to a confirm. +- **A single, indivisible submission unit** — a short, self-contained task that must be completed or abandoned as one atomic unit (e.g. a focused credential or payment step) and that would be corrupted by leaving it half-done in the background. +- **Physically-exclusive interaction** — a task that genuinely needs the full surface or an exclusive input channel (a media crop, a full-screen capture, an OS-level permission prompt) where a background interaction would be meaningless or conflicting. + +- If the interaction is none of the above, it MUST NOT be a modal. "It was easier to drop in a dialog" is not a justification — it is the failure mode this policy exists to catch. +- Whenever a modal **is** introduced, the design MUST **record the trade-off in the PR description or an ADR**: which of the three cases it falls under, and why a modeless surface would not serve. This makes the exception auditable and reversible later. + +::: info The default carries no burden; the mode does +A modeless design needs no defence — it is the baseline. A modal is a claim that *this* interaction is special enough to take control away from the user. If that claim cannot be written down in a sentence tied to one of the three cases, the claim is false and the modal should not ship. +::: + +### 3-2. Hold State in the URL, Not in the Mode + +A mode that lives only in transient in-memory state is a place with no address: the user cannot bookmark it, share it, reload into it, or navigate back out of it with the browser's own controls. Raskin's modelessness has a direct web-era corollary — **the URL is the application's modeless state.** NN/g's *user control and freedom* heuristic (heuristic 3 of the [10 Usability Heuristics](https://www.nngroup.com/articles/ten-usability-heuristics/)) wants a clearly marked exit at all times; the browser's Back button and address bar are that exit, and they only work if state is in the URL. + +- View, selection, filter, and "which item is open" state SHOULD be encoded in the URL (path or query), so every meaningful place is **addressable, shareable, and reload-survivable.** +- Opening a detail or edit surface SHOULD change the URL so **Back closes it** and forward/reload restores it. The browser's native navigation then becomes a free, always-present modeless exit. +- State that is genuinely ephemeral (a hover, an unsaved draft mid-keystroke) MAY stay in memory — but anything a user could reasonably want to return to, link to, or refresh into belongs in the URL. + +::: tip The Back button is a modeless exit you get for free +When the current place is in the URL, the browser's Back button already does what §3-3 asks every modal to do — it lets the user leave. Building on the URL means you inherit a universal exit instead of reimplementing one badly. +::: + +### 3-3. Multiple Exits, and Never Eat the User's Input + +If a mode must exist, it must be trivial to leave, and leaving must not punish the user. The WAI-ARIA Authoring Practices [dialog (modal) pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) requires that **Escape closes the dialog** — this is a baseline accessibility expectation, not a nicety. NN/g's *user control and freedom* heuristic frames the rest: users need a clearly marked way out that does not force them through an unwanted flow. + +- Every modal MUST offer **at least two ways to close it**: the **Escape key** (required by the ARIA pattern) **and** a **click on the background/overlay**, in addition to any explicit Close or Cancel control. Multiple exits mean the user never has to hunt for the one door that works. +- Closing a modal MUST **preserve in-progress input** rather than silently discarding it. A user who typed three fields and pressed Escape by reflex should get those fields back, not a blank form — re-open restores, or an "unsaved changes" guard intervenes before loss. +- The one exception to easy dismissal is the **irreversible-confirmation** case (§3-1): a delete-confirm MAY intentionally require an explicit choice rather than a background-click dismissal — but Escape (i.e. "cancel, do nothing") MUST still work, because cancelling a destructive action is always safe. +- Never make the user complete a modal to escape it. A dialog with only a single "OK" that commits an action is a trap; there must be a safe way out that changes nothing. + +::: info Dismissal is safe by default; only destruction is guarded +The asymmetry is deliberate. *Cancelling* is always harmless, so it should be effortless and available every way — Escape, background click, Close. *Committing* something irreversible is the only thing that earns a deliberate, guarded gesture. Guard the destruction, never the retreat. +::: + +### 3-4. Trap Focus, but Keep the Background Readable + +A modal has two audiences at once — a keyboard or screen-reader user who must not be able to tab out into dead background controls, and a sighted user who often needs the context behind the dialog to answer it. Both the WAI-ARIA APG [dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) and the platform dialog guidance (Apple's [Human Interface Guidelines — Modality](https://developer.apple.com/design/human-interface-guidelines/modality), Google's [Material dialogs](https://m3.material.io/components/dialogs/guidelines)) speak to this: contain interaction, but do not obliterate context. The full accessibility bar these rules serve is set out in [Accessibility](/accessibility). + +- Keyboard focus MUST be **trapped within the modal** while it is open — Tab and Shift+Tab cycle only through the dialog's focusable elements, focus moves into the dialog on open, and returns to the triggering element on close (WAI-ARIA APG). Background controls MUST be inert to the keyboard and to assistive technology. +- The background SHOULD remain **visually present and readable** — dimmed or scrimmed for focus, not blanked out. A user answering "delete this invoice?" should still be able to see *which* invoice. Do not hide the context the mode is asking about. +- Modals SHOULD stay **small and focused** on the single decision or task (Material, Apple HIG). A modal that grows into a full secondary application is a sign the interaction wanted a page or a nonmodal panel — reconsider against §3-1. +- The dialog MUST be correctly announced: `role="dialog"` with `aria-modal="true"` and a programmatic label (`aria-labelledby`/`aria-label`), per the ARIA pattern, so its purpose and boundary are clear to assistive technology. + +::: tip Contain the focus, not the understanding +Focus trapping is a mechanism for correctness — it keeps keyboard and screen-reader users from wandering into an inert background. It is not a licence to hide that background from the eye. Trap the *interaction*; keep the *information* visible. +::: + +## 4. What "Modeless by Default" Requires, Per Interaction + +Before an interaction that introduces a mode is called finished, it MUST satisfy all of the following. This is the checklist the practices above add up to — the bar the [Quality Gate](/quality-gate) holds a mode to: + +| Requirement | The question it answers | +| ----------- | ----------------------- | +| **Justified case** | Does this modal fit one of the three cases — irreversible confirmation, single submission unit, physically-exclusive interaction? (§3-1) | +| **Recorded trade-off** | Is the reason for the modal written in the PR or an ADR? (§3-1) | +| **URL state** | Is the place addressable, shareable, and reload-survivable via the URL? (§3-2) | +| **Multiple exits** | Do Escape *and* a background click both close it? (§3-3) | +| **Preserved input** | Does closing keep the user's in-progress input rather than discarding it? (§3-3) | +| **Focus trapped** | Is keyboard/AT focus contained in the modal and returned on close? (§3-4) | +| **Readable background** | Is the context behind the modal still visible? (§3-4) | +| **Announced correctly** | Does the dialog carry `role`, `aria-modal`, and a label? (§3-4) | + +An interaction that works but fails any row above is not finished — it has introduced a mode without earning it, or built a mode that traps the person inside it. + +## 5. Related Guidelines + +- [Design Guidelines](/design-guidelines) — the umbrella this policy sits under. +- [Interaction Design](/interaction-design) — the broader interaction thread this page sharpens to a single decision. +- [Accessibility](/accessibility) — the focus, dismissal, and announcement obligations a mode must still meet when it exists (§3-3, §3-4). +- [Quality Gate](/quality-gate) — where the per-interaction checklist in §4 is held. + +## 6. References + +Named, published practice this policy is grounded in — each a documented source an SME can adopt directly. + +**Modelessness** + +- Jef Raskin — *The Humane Interface: New Directions for Designing Interactive Systems* (2000) — modes as a principal cause of user error; the case for a modeless interface — +- Larry Tesler — "Don't mode me in" / NOMODES — a career-long campaign against modal interfaces — + +**Modal-dialog usability** + +- Nielsen Norman Group — Modal & Nonmodal Dialogs: When (& When Not) to Use Them — +- Nielsen Norman Group — 10 Usability Heuristics for User Interface Design (heuristic 3, *User control and freedom*) — + +**Platform dialog guidance** + +- Apple — Human Interface Guidelines: Modality — +- Google — Material Design 3: Dialogs — + +**Accessibility** + +- W3C WAI-ARIA Authoring Practices Guide — Dialog (Modal) Pattern — diff --git a/doc/multiple-ai-agents.md b/doc/multiple-ai-agents.md new file mode 100644 index 0000000..b508605 --- /dev/null +++ b/doc/multiple-ai-agents.md @@ -0,0 +1,226 @@ +# Multiple AI Agents + +Every OSBR developer keeps **at least two coding agents usable every day** — for +example Claude Code and one other — with authentication, permissions, and working +environments already configured so that switching between them is a matter of +minutes, not a project. When one provider fails, rate-limits, silently changes +model quality, or ships a breaking spec change, work continues on the other. This +page is the *discipline* for holding that second path warm, not merely +theoretically available. It is the AI-layer companion to the [AI Usage +Guideline](/ai-usage-guideline) (how we work with agents at all) and to +[Policies as Plugins](/policies-as-plugins) (the shared standard every agent is +held to). + +The failure this prevents is concrete: a whole team blocked for an afternoon +because one vendor is down, one account is throttled, or a model update quietly +regressed on the exact task we depend on. A backup agent that nobody has logged +into for a month is not a backup — it is a cold spare that will itself need +debugging at the worst possible moment. + +This page is where OSBR's **Be Strong** value becomes operational: *strong = +never single-point-dependent.* **Be Nice**: an agent-agnostic ticket and a synced +policy version are a gift to whoever picks the task up next. **Be Kind**: doing +the dull readiness check on the calm day is what spares a teammate the bad one. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as elsewhere in the handbook. + **MUST** / **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a + strong default overridable only with a documented reason. **MAY** marks a free + choice. +* **Named practice.** Where a rule adopts a standard resilience practice, the + practice is named inline and cited under [References](#references). We adopt the + *criteria* — vendor-neutrality, N+1 redundancy, bus-factor thinking — and + right-size them for an SME, not the infrastructure of the reference setups. +* Deviations are allowed but, as everywhere in the handbook, must be deliberate + and justified in the project's design notes. + +[[TOC]] + +## 1. Goal + +Development at OSBR **never has a single point of failure at the AI-provider +layer.** On any project, on any day, a developer — or the AI agent working beside +them — can move the current task from one coding agent to another and keep going, +because the second agent is already authenticated, already permissioned, already +pointed at the same repository and the same policy standard. + +Concretely, the goal is that the answer to *"provider X is down / throttled / just +changed — can we still ship today?"* is always **yes**, and proving it takes +minutes. This is the AI-layer expression of **Be Strong**: *never +single-point-dependent.* We treat a coding agent the way a serious operation +treats any critical supplier — as one interchangeable source among several, not +as the foundation the whole business rests on. + +This is standard resilience engineering applied to our development tools. +**Vendor-neutrality and avoiding lock-in** keep our choices open; **multi-vendor +sourcing** removes the single supplier as a single point of failure; **redundancy +(N+1)** keeps a spare warm; **Business Continuity Planning (BCP)** and +**bus-factor** thinking demand the work survive the loss of any one dependency; +**portability** and **abstraction over provider-specific features** are what make +the switch cheap when we need it. + +## 2. Responsibility + +- **Every developer** keeps at least two coding agents installed, authenticated, + and *actually exercised* — not just installed. If you have not run a real task + through your backup agent this week, you do not have a backup. +- **Whoever writes a ticket, command, or procedure** writes it + **agent-agnostically** (§3-2). Per the [Development Guide](/development-guide), + a ticket states the task and its acceptance criteria; one that only makes sense + to a single agent is a lock-in defect, the same way a synonym is a naming + defect. +- **Whoever bumps a policy plugin** on one agent MUST bring every other agent to + the **same version** in the same change (§3-4). Divergent policy versions across + agents mean the same code passes on one agent and fails on another — silent, + maddening drift. +- **The whole team** treats "can we switch providers today?" as a standing + readiness question, not a disaster-day discovery. Readiness is verified on a + cadence (§3-5), not assumed. + +## 3. Practices + +### 3-1. Keep two agents warm, not one warm and one cold + +- You MUST keep **at least two coding agents** usable daily, with auth, + permissions, and environment ready to switch. Two is the floor (**N+1** + redundancy over a single agent); a third is cheap insurance where a project's + risk warrants it. +- **Exercise the spare.** A backup path you never run rots — credentials expire, + config drifts, the CLI is three versions behind. You SHOULD route real work + through each agent regularly so the switch is proven, not hoped for. This is the + difference between a *hot* standby and a *cold* one: only the hot standby is a + real BCP control. +- Keep authentication for each agent **current and independent** — separate + credentials, no shared token that takes both down at once. Correlated failure + defeats the point of redundancy: two agents behind one dependency are one agent + wearing two hats. +- Keep the **environments** (repo checkout, tooling, MCP servers, secrets access) + reachable from either agent, so switching is "point the other agent at this + repo," not "spend a day rebuilding a workspace." + +### 3-2. Write tickets, commands, and procedures agent-agnostically + +- Tickets, runbooks, slash-command procedures, and project-memory guidance MUST be + written to the **task and the repository**, not to one vendor's quirks. State + *what* must be true (the acceptance criteria, the files, the checks), not *which* + agent's private feature you happen to be using. +- This is **abstraction over provider-specific features**: depend on the + capability (edit files, run tests, open a PR), which every serious coding agent + has, not on a single vendor's proprietary surface. Where a provider-specific + feature genuinely earns its keep, isolate it behind a thin, documented seam so + the fallback path is obvious — the same **portability** discipline that keeps + application code off a single cloud's proprietary API. +- A good test: **could the other agent pick up this ticket and finish it with no + rewrite?** If not, the ticket is coupled to a provider — fix the ticket. + Agent-agnostic wording is to provider lock-in what one-word-per-concept is to + naming drift: cheap to hold if you hold it continuously, expensive to unwind + once it spreads. + +### 3-3. Treat a provider change as an expected event, not a surprise + +- A rate-limit, an outage, a quality regression after a model update, or a + breaking spec/API change is a **when, not an if** — plan for it. The response is + a rehearsed switch, not an emergency. +- When a provider degrades, **switch first, report second**: move the task to the + warm agent, keep shipping, then record what happened (which provider, what + symptom, what you switched to) so the team sees the pattern. Recurrent + degradation at one provider is a supplier-management signal, not just a bad + afternoon. +- Never let one provider's convenience quietly become a hard dependency. Each time + you reach for a feature only one agent has, you SHOULD ask whether you are + re-introducing the single point of failure this policy exists to remove. + +### 3-4. Keep policy plugins synchronized to the same version across agents + +- The OSBR **policy plugins are the shared evaluation standard** every agent is + held to — the same standard the [Quality Gate](/quality-gate) enforces, packaged + as described in [Policies as Plugins](/policies-as-plugins). They MUST be at the + **same version across all agents you use.** The same code reviewed by two agents + on two plugin versions gets two answers — that divergence is a correctness bug + in our process, not a quirk to work around. +- When you bump a plugin on one agent, you MUST bump it on the others **in the + same change**, and note the version. Pin the version explicitly rather than + letting each agent float to "latest" independently — floating versions re-create + the drift by default. +- Verify the versions **match** as part of the readiness check (§3-5), the same way + a rename is not done until every surface is updated: policy sync is not done + until every agent reports the same version. + +### 3-5. Verify switch-readiness on a cadence + +- On a regular cadence (SHOULD be at least weekly), each developer confirms the + fallback is real: **each agent authenticates, runs a real task, and reports the + same policy-plugin version.** A backup asserted but never verified is **BCP + theatre** — it fails exactly when it is finally needed. +- Treat a failed readiness check like a broken build: fix the cold agent *now*, + while there is no pressure, not on the day the primary is down. The whole value + of **N+1** is that the spare is known-good *before* the failure, not diagnosed + during it. + +## 4. Quick Checklist + +Before relying on "we can always switch," confirm: + +- [ ] At least two coding agents are installed, authenticated, and have run a real + task this week. +- [ ] Each agent has independent, current credentials — no shared token that fails + both at once. +- [ ] Either agent can reach this repo, its tooling, and its secrets with no + day-long setup. +- [ ] Tickets/commands/procedures read agent-agnostically — the other agent could + finish them with no rewrite. +- [ ] Policy plugins are pinned to the **same version** across every agent, bumped + together. +- [ ] The last switch-readiness check passed (auth + real task + matching plugin + version). + +## 5. OSBR Values in Practice + +- **Be Nice** — an agent-agnostic ticket and a synced policy version are a gift to + the next developer, who can pick up the task on whichever agent they have without + hitting a wall you left behind. Portability is consideration made durable. +- **Be Kind** — do the unglamorous readiness check on the calm day so a teammate is + not stranded on the bad one. Sparing others a 3am scramble by keeping the spare + genuinely ready is kindness that costs you a few minutes and saves them an + afternoon. +- **Be Strong** — *never single-point-dependent.* Strength here is refusing to let + one vendor's outage, throttle, or quiet quality change decide whether OSBR ships + today. Keep the second path warm and proven so no single provider can stop the + work. + +Two warm agents, agent-agnostic work, and synchronized policy are how OSBR stays +**strong at the AI layer — dependent on no single provider, ready to switch before +we ever have to.** + +## References + +**Resilience & continuity practice** + +- NIST SP 800-34 Rev. 1 — *Contingency Planning Guide for Federal Information + Systems* (business continuity, alternate providers, redundancy) — + +- ISO 22301 — *Business Continuity Management Systems* (continuity of critical + operations through supplier disruption) — +- The Open Group — vendor-neutrality and portability as architecture principles — + + +**Redundancy & bus-factor** + +- N+1 redundancy — resilience by keeping at least one spare beyond the minimum + needed — +- Bus factor — resilience against the loss of any single critical dependency — + +- Multi-cloud / multi-vendor resilience — removing the single supplier as a single + point of failure — + +**Related OSBR standards** + +- [AI Usage Guideline](/ai-usage-guideline) — how we work with AI agents at all; + this page is its no-single-provider companion. +- [Policies as Plugins](/policies-as-plugins) — the policy plugins that must stay + version-synced across agents (§3-4). +- [Quality Gate](/quality-gate) — the shared evaluation standard every agent, on + any provider, is held to. +- [Development Guide](/development-guide) — agent-agnostic tickets, commands, and + pull-request procedure. diff --git a/doc/observability-resilience.md b/doc/observability-resilience.md new file mode 100644 index 0000000..9c75f51 --- /dev/null +++ b/doc/observability-resilience.md @@ -0,0 +1,358 @@ +# Observability & Resilience + +This is the standard the [Quality Gate](/quality-gate)'s **Reliability** lens +holds running systems to. It fills in the concrete defaults behind the +observability and resilience principles the [Infrastructure Planning +Policy](/infra-planning-policy) states (§1-5 *Reliability and Delivery Are +Measured*, §1-6 *Observability Is Built In*): that policy says *what* we hold to +— emit structured logs, metrics, and traces; hold an SLO; design with timeouts, +retries, circuit breakers, and health checks — and this page says *how*: the +exact log schema, the timeout and retry defaults, and what is allowed to wake a +human. A principle nobody can fail is a principle nobody follows, so these +defaults are checkable: a log line either has the required fields or it does +not; an external call either has a timeout or it does not. Deviations are +allowed, but — as everywhere in the handbook — they must be deliberate and +justified in the project's design notes. + +This is also where **human⇄AI collaboration** meets the running system. We write +for both humans and AI. The same structured logs, metrics, and traces that let +an on-call engineer find a fault are what let an AI agent investigate one — +trace an error to its span, read the error budget, propose the rollback. +Observability that only a human can read is half-built. Ship telemetry an agent +can query. + +**Requirement levels** follow RFC 2119, as elsewhere in the handbook. +**MUST** / **MUST NOT** are absolute; **SHOULD** / **SHOULD NOT** state a strong +default overridable only with a documented reason; **MAY** marks a free choice. +Where a rule adopts an industry practice, the practice is named inline and cited +under [References](#references) — we adopt the *criteria* of large-scale +practice and right-size them for an SME. + +[[TOC]] + +## 1. Goal + +**Make every OSBR system observable enough to operate to an SLO, and resilient +enough to absorb the failures a distributed system will always have — without +paging a human for anything a machine can handle.** + +Concretely, a service that meets this policy can answer, from its telemetry +alone: + +- Is it up, is it ready, and is the business function actually working? (§4) +- When it broke, *where* did the request fail and *why*? (§2, §3) +- How much of the error budget is left, and is it burning? (§5, §6) +- When a dependency failed, did we contain it or cascade it? (§7) + +This serves the values directly. **Be Nice** — we do not wake a colleague at 3am +for something a retry would have fixed. **Be Kind** — we mask personal data in +our telemetry so that watching the system never becomes surveilling the people +in it. **Be Strong** — a resilient system carries load and recovers on its own +instead of collapsing onto the on-call engineer. + +## 2. Responsibility + +Every service, worker, job, and function OSBR ships is responsible for its own +observability and its own resilience. This is not a platform team's job to bolt +on afterwards — per [Infra §1-6](/infra-planning-policy), it is built in from day +one. It is the same implementer-owns-quality rule the [Quality +Gate](/quality-gate) states: verification is planned at design, not handed to a +separate stage. + +- **The author of a component** owns its log schema conformance, its + instrumentation (traces + metrics), its health checks, and the + timeout/retry/circuit-breaker configuration on every call it makes outward. +- **The reviewer** — through the AI code review the [Quality + Gate](/quality-gate) requires — checks that new external calls carry the §7 + resilience defaults and that no new log field leaks personal data (§8). +- **The team** owns the SLOs, the alert rules, and the rollback triggers, and + treats the [DORA](https://dora.dev/) four keys and SLO burn as team signals, + never individual ratings. + +We lean on published, freely available standards rather than inventing our own +vocabulary: Google's [SRE practice](https://sre.google/books/) for +SLI/SLO/error budgets and the four golden signals, +[OpenTelemetry](https://opentelemetry.io/) for the wire format of +traces/metrics/logs, the [RED and USE +methods](https://www.brendangregg.com/usemethod.html) for *which* metrics, and +Michael Nygard's [*Release +It!*](https://pragprog.com/titles/mnee2/release-it-second-edition/) for the +resilience patterns. + +## 3. Structured Logging + +Logs are event streams (per [Twelve-Factor](https://12factor.net/logs), echoed +in [Infra §1-6](/infra-planning-policy)): the process writes to stdout, the +platform ships them to a central store. The process must never manage log files, +rotation, or routing. + +Every log line is **a single JSON object, one per line** (JSON Lines). +Human-formatted, multi-line, or free-text logs are for local development only +and MUST NOT reach a deployed environment. + +### 3-1. Minimum Log Schema + +Every log line from every component **MUST** carry at least these fields: + +| Field | Type | Required | Notes | +|-------|------|----------|-------| +| `timestamp` | string | **MUST** | ISO 8601 / RFC 3339, UTC, millisecond precision (`2026-07-15T09:12:33.482Z`). | +| `level` | string | **MUST** | One of `debug`, `info`, `warn`, `error`, `fatal`. | +| `service` | string | **MUST** | Stable service name, matching the OpenTelemetry `service.name` (§5). | +| `message` | string | **MUST** | Human-readable summary. A constant string per event; put the variables in their own fields, not interpolated into the message. | +| `trace_id` | string | **MUST** when a request/trace context exists | The OpenTelemetry trace ID, so a log line joins its trace (§5). | +| `span_id` | string | SHOULD | The active span, for the same reason. | +| `request_id` | string | **MUST** for request-handling components | Correlates all logs of one inbound request even without a full trace. | +| `error` | object | **MUST** when `level` is `error`/`fatal` | `{ "type", "message", "stack" }`. Never log an error as a bare string; never swallow it silently. | + +A conformant `error`-level line: + +```json +{ + "timestamp": "2026-07-15T09:12:33.482Z", + "level": "error", + "service": "checkout-api", + "message": "payment authorization failed", + "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736", + "span_id": "00f067aa0ba902b7", + "request_id": "req_01J9Z3K8", + "error": { "type": "GatewayTimeout", "message": "upstream timed out after 2000ms", "stack": "..." }, + "env": "production", + "version": "2026.07.15-a1b2c3d" +} +``` + +### 3-2. Rules + +- Services **SHOULD** also include `env` (dev/staging/production) and `version` + (the deployed build/commit) so a log line is attributable to an environment + and a release — this is what lets you tie an error spike to a specific deploy, + and feeds the DORA change-failure signal. +- Choose `level` by **actionability**, not by volume. `error` means *a human or + agent may need to act*; a handled, retried, recovered failure is `warn` or + `info`. If everything is `error`, nothing is. +- Field names are **stable and flat**. Adding a field is fine; renaming or + re-typing an existing one breaks every query and dashboard built on it — treat + the schema as an interface. +- Logs answer *why*. For *how much* and *how often*, use metrics (§4) — do not + reconstruct rates by counting log lines when a counter is cheaper and exact. + +## 4. Metrics & Health Checks + +### 4-1. The Four Golden Signals, RED, and USE + +Instrument every service for Google SRE's **four golden signals** — **latency, +traffic, errors, saturation**. In practice: + +- **Request-driven services** (APIs, workers): use the **RED** method — + **R**ate, **E**rrors, **D**uration — per endpoint/route. This covers latency, + traffic, and errors. +- **Resources** (CPU, memory, connection pools, queues): use the **USE** method + — **U**tilization, **S**aturation, **E**rrors. This covers saturation. + +Metrics **MUST** be exported via OpenTelemetry (§5). Emit latency as a +**histogram**, not an average — an average hides the tail, and the tail is where +users feel pain. Alert and SLO on percentiles (p95/p99). + +### 4-2. Three Distinct Health Checks + +A single `/health` endpoint conflates three different questions and gets used +wrongly. OSBR distinguishes them, and every long-running service **MUST** expose +them separately: + +| Check | Question | If it fails | MUST NOT | +|-------|----------|-------------|----------| +| **Liveness** | Is the process alive and not deadlocked? | Orchestrator **restarts** the instance. | Check downstream dependencies — a dependency outage would trigger a pointless restart loop. | +| **Readiness** | Can this instance serve traffic *right now*? | Orchestrator **stops routing** to it (no restart). | Stay green while a required dependency (DB, cache) is unreachable. | +| **Business / deep health** | Is the core business function actually working end to end? | **Alerts** a human (§6); does not restart or de-route. | Be on the hot request path or run on every probe — it is heavier; run it on a schedule. | + +The distinction is what stops the classic cascade: a shared dependency blips, +every service's liveness check fails, the orchestrator restarts the entire fleet +at once, and the blip becomes an outage. Liveness checks only what the process +itself owns. + +## 5. Traces & OpenTelemetry + +[OpenTelemetry](https://opentelemetry.io/) is the OSBR standard for all three +signals — traces, metrics, and logs — because it keeps instrumentation +vendor-portable (per [Infra §1-6](/infra-planning-policy)); the backend can +change without re-instrumenting the code. + +- Every service **MUST** propagate **W3C Trace Context** (the `traceparent` + header) on inbound and outbound calls, so a request keeps one `trace_id` + across service boundaries. A trace that stops at a service boundary cannot + show you where a distributed request failed. +- The `trace_id`/`span_id` in traces **MUST** be the same IDs written into logs + (§3-1) — this correlation is the whole point: from a spike on a dashboard, to + the slow span, to the exact log line, in three clicks (or three agent tool + calls). +- Set a stable `service.name` resource attribute; it is the join key across + logs, metrics, and traces. +- **Sample** in production (head or tail sampling) to control cost, but **always + keep traces that contain an error**. The cheap traces are the ones you never + need. + +## 6. SLOs, Alerts & Self-Healing + +### 6-1. Error Budgets + +Per [Infra §1-5](/infra-planning-policy), reliability is measured, not assumed. +Each service defines **SLIs** (usually availability and latency, from the golden +signals) and an **SLO** target, agreed up front as a non-functional +requirement. The gap between the SLO and 100% is the **error budget** — the +amount of failure we have explicitly decided is acceptable. Self-healing spends +this budget silently; alerting is what we do when the *rate of spend* threatens +to exhaust it. + +### 6-2. Alerts Wake a Human — So Alert Only on What a Human Must Fix + +An alert is a claim that a human must act now. Every page that turns out to need +no action erodes trust in every future page (alert fatigue), and a tired on-call +engineer is neither Strong nor safe. + +- Services **MUST** alert on **SLO burn rate and user-facing symptoms**, not on + individual low-level metrics ([Infra §1-6](/infra-planning-policy)). "p99 + latency SLO is burning 10× budget" is a page; "CPU is at 80%" is not — 80% CPU + with a healthy SLO is a system doing its job. +- If a condition is one a machine already handles — a retriable timeout, a + single unhealthy instance being de-routed, a circuit breaker that opened and + will half-open on its own — it **MUST NOT** page. Log it (§3), count it (§4), + move on. Self-healing that still pages defeats its own purpose. +- Every alert **MUST** be actionable: it names a probable cause and points to a + runbook. An alert with no action is a dashboard; put it on a dashboard. What + happens once a page fires — escalation, roles, the postmortem — is the domain + of [Incident Management](/incident-management). + +### 6-3. Pre-Decided Rollback Triggers + +Deploys must be reversible ([Infra §1-10](/infra-planning-policy)). +Reversibility is worthless if nobody decides to use it in time, and 3am is the +worst moment to invent the criteria. So the rollback triggers are **decided +before the deploy**, written down, and — where the platform allows — automated: + +- Error rate exceeds *N×* the pre-deploy baseline for *M* minutes → **roll + back**. +- SLO burn rate crosses the fast-burn threshold → **roll back**. +- A liveness or readiness check (§4-2) stays red past the deploy window → **roll + back**. + +Rolling back is the **Strong** move, not the failure. It is a normal, un-blamed +operation; the postmortem ([Incident Management](/incident-management)) asks +what the system missed, never who shipped it. Progressive delivery (canary / +blue-green, per [Infra §1-10](/infra-planning-policy)) is what makes these +triggers fire while a bad release still reaches few users. + +## 7. Resilience Defaults on Every External Call + +Every call that leaves the process — HTTP, database, cache, queue, third-party +API — **can and eventually will** fail, hang, or slow down (the fallacies of +distributed computing; how those boundaries are drawn is the domain of the +[Architecture Standards](/architecture-standards)). Michael Nygard's [*Release +It!*](https://pragprog.com/titles/mnee2/release-it-second-edition/) names the +patterns; these are OSBR's defaults. A call that reaches the network **MUST** +have all three of timeout, bounded retry, and a circuit breaker, unless the +design notes justify an exception. These defaults are checkable, so they +**SHOULD** be exercised against a real dependency in tests ([Testing +Standards](/testing-standards)) — a timeout you never fire is a timeout you do +not really have. + +### 7-1. Timeouts + +- **Every** external call **MUST** set an explicit timeout. The default + connect/read timeout is a client library's most dangerous setting because it + is so often *infinite* — one hung dependency exhausts your whole connection + pool and takes you down with it. +- Set the timeout from the dependency's measured p99, not a guess. A caller's + timeout **SHOULD** be shorter than its own caller's, so failures surface at + the right layer instead of piling up. + +### 7-2. Retries — Bounded, Backed Off, Jittered + +- Retry **only idempotent** operations, and only on **transient** failures + (timeouts, 429, 503, connection resets). Never retry a 400 or a 422 — the + input is wrong; retrying just multiplies the load. +- Retries **MUST** be **bounded** (a small max, e.g. 3) with **exponential + backoff *and* jitter**. Backoff without jitter synchronizes every client to + retry at the same instant — a thundering herd that turns a blip into an + outage. Jitter spreads them out. +- Respect a `Retry-After` header when the dependency sends one. + +### 7-3. Circuit Breakers & Bulkheads + +- Wrap a flaky dependency in a **circuit breaker**: after a threshold of + failures it **opens** and fails fast for a cool-down window instead of + hammering a service that is already down, then **half-opens** to test + recovery. This is what gives the dependency room to heal — and what stops your + retries from being the reason it can't. +- Use **bulkheads** to isolate resources (separate connection pools / + concurrency limits per dependency) so that one saturated dependency cannot + consume every worker and starve the healthy paths. Shed load at the edge + rather than queue it unboundedly. + +::: tip These are defaults, not dogma +A steady internal call to a fast, co-located dependency may not need a full +breaker. The rule is that the omission is *deliberate and justified in the +design notes* — recorded in the pull request's Specification per the +[Development Guide](/development-guide), the same standard +[Infra](/infra-planning-policy) sets for every deviation. The unacceptable case +is the call with no timeout because nobody thought about it. +::: + +## 8. Privacy in Telemetry — Be Kind + +Logs, metrics, and traces are **protected assets** ([Application +Security](/application-security)). Observability must never become surveillance +of users or colleagues. + +- **PII MUST be masked or omitted** before it is written to any log, span + attribute, or metric label. Names, emails, tokens, full card numbers, precise + location, request bodies containing personal data — masked at the source, not + in a downstream pipeline that might miss a field or a new code path. +- **Never** put secrets or credentials in telemetry. If one is logged, treat it + as a leaked credential and revoke it immediately ([Application + Security](/application-security)). +- Prefer **stable pseudonymous IDs** (a hashed user ID) over raw identifiers + when you need to correlate a user's requests. You almost never need the real + value to debug; you need to know it is *the same* user. +- No screenshots or telemetry containing personal data in issues, PRs, or + channels without masking ([Application Security](/application-security)). + +Masking protects the people in the system and it protects OSBR: telemetry an +attacker or a careless export can turn into a personal-data breach is a +liability, not an asset. + +## References + +Named, freely available standards this policy is built on. + +**Reliability, SLOs & signals** + +- Google SRE — *Site Reliability Engineering* & *The SRE Workbook* (SLI/SLO/error budgets, the four golden signals) — +- Brendan Gregg — *The USE Method* — +- Tom Wilkie — *The RED Method* (Rate, Errors, Duration) — + +**Observability** + +- OpenTelemetry (traces, metrics, logs; W3C Trace Context) — +- W3C Trace Context — +- The Twelve-Factor App — XI. Logs — + +**Resilience patterns** + +- Michael T. Nygard — *Release It!* (circuit breaker, bulkhead, timeout, fail fast) — +- Amazon Builders' Library — *Timeouts, retries, and backoff with jitter* — +- Azure Cloud Design Patterns — + +**Delivery metrics** + +- DORA (DevOps Research & Assessment) four keys — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Reliability lens this standard serves. +- [Infrastructure Planning Policy](/infra-planning-policy) — §1-5, §1-6, §1-10; the observability and resilience principles this page makes concrete. +- [Testing Standards](/testing-standards) — exercising these defaults against real dependencies. +- [Development Guide](/development-guide) — the pull-request Specification where deviations are justified. +- [Incident Management](/incident-management) — escalation, on-call, and postmortems once an alert fires. +- [Architecture Standards](/architecture-standards) — how service boundaries are drawn. +- [Application Security](/application-security) — telemetry as a protected asset; PII and secret handling. diff --git a/doc/overnight-ai.md b/doc/overnight-ai.md new file mode 100644 index 0000000..5bfcd08 --- /dev/null +++ b/doc/overnight-ai.md @@ -0,0 +1,244 @@ +# Overnight AI Operation + +This policy defines how an OSBR developer shapes the workday so that AI agents can +run **unattended overnight** on well-specified work, and how the human **judges the +results in the morning**. It is the working expression of the human ⇄ AI cooperation +the [AI Usage Guideline](/ai-usage-guideline) sets out: **the AI works the night, the +human owns the judgment.** It complements the [Development Guide](/development-guide) +(how we ship) and the [CI/CD Pipeline](/ci-cd-pipeline) (how the running system is +delivered and measured); this describes *how a person hands work to an agent and takes +it back.* Deviations are allowed, but — as everywhere in the handbook — they must be +deliberate and justified in the project's design notes. + +The core discipline is simple and non-negotiable: **AI runs on rails you laid in +daylight, and nothing it produced is trusted until a human has looked.** There is no +blank cheque. Overnight autonomy is *earned* by the quality of the ticket, and +*checked* every morning on return. + +This is where OSBR's values become a daily rhythm. **Be Nice**: leave the agent — and +your morning self — a clean, answerable brief, because a ticket is the clearest +instruction a collaborator will read. **Be Kind**: never let unverified machine output +reach a teammate or a client; the review you owe them is a duty, not a preference. **Be +Strong**: do the hard specification work up front, in daylight, instead of leaving the +agent to flail against ambiguity in the dark. + +[[TOC]] + +## 1. Goal + +Turn a normal workday into fuel for a productive night. By the time the developer logs +off, the agent should have everything it needs to work for hours without a human +present — and everything the agent *cannot* safely decide alone should be collected, +not guessed. The next morning starts with **review, never with blind acceptance.** + +This mirrors an established idea about autonomy: a person can supervise a system either +**in the loop** — approving each individual action before it happens — or **on the +loop** — setting the goal and boundaries up front, letting the system act, and reviewing +the outcome afterward (supervisory control; see [References](#references)). Overnight +operation is deliberately **human-on-the-loop**: the human is not awake to approve each +step, so the approval has to be *front-loaded into the ticket* and the *review moved to +the morning*. That only works if the boundaries were drawn well while the human was +still awake. + +## 2. Responsibility + +- **The developer owns the specification and the verdict.** Writing a ticket an agent + can run unattended, and judging what came back, are the developer's job — not the + agent's. An agent that did the wrong thing overnight because the ticket was vague is a + **specification failure**, not an AI failure. +- **The developer owns the guardrails.** What the agent may touch, what it must never + touch, and where it must stop and leave a question are set by the human before the run + (§3-5). +- **No unattended run without morning verification.** Whoever launched the overnight + run is responsible for reviewing its output before any of it is merged, deployed, or + built upon. This is the same implementer-owns-quality rule the [Quality + Gate](/quality-gate) states, and it does not transfer to the agent. +- **AI agents are first-class contributors, held to exactly the same bar.** The human + who merges an agent's work owns it, exactly as they would their own. + +## 3. Practices + +### 3-1. Structure the day around ticket-writing, not just doing + +The overnight run is only as good as the tickets waiting for it. Treat **ticket +preparation as a first-class daytime activity**, not an afterthought at the end of the +day. + +- Developers **SHOULD** reserve daytime — when questions can still be asked of humans — + for turning fuzzy intentions into **well-formed work items**, and reserve the night + for the mechanical execution an agent can carry alone. +- The unit of overnight work is a ticket that is **independently runnable**: it does not + depend on a mid-run human decision, and it does not block on another ticket finishing + first. +- This is **batch processing** applied to knowledge work: accumulate a queue of + self-contained jobs during the day, then let them run as an unattended batch overnight + (see [References](#references)). The daytime human is the interactive session; the + night is the batch window. + +### 3-2. A "Definition of Ready" for overnight tickets + +Borrowing Agile's **Definition of Ready** — the gate a work item must pass before a team +commits to it — OSBR adds an *overnight* bar on top. A ticket is **ready to run +unattended** only when it meets **all** of the following. Developers **MUST NOT** queue +a ticket for overnight operation that fails any item. + +- **Independent & self-contained** — runnable on its own, no mid-run human input, no + hidden dependency on another unfinished ticket. +- **Decided up front, not overnight** — every choice the agent would otherwise have to + *negotiate* with a human is already made and written down (see §3-3). +- **Valuable & clear** — the outcome and the reason for it are stated, so the agent + optimises for the right thing. +- **Small enough to verify by morning** — scoped so a human can actually review the + result the next day, not a sprawl no one can check. +- **Testable** — the ticket states how "done" is proven (tests pass, a command succeeds, + output matches a stated shape). If done cannot be defined, the ticket is not ready. +- **Bounded** — the files, systems, and blast radius the agent may touch are named, and + the off-limits areas are named too (see §3-5). + +These map directly onto the well-known **INVEST** qualities of a good work item — +Independent, Negotiable, Valuable, Estimable, Small, Testable — with "negotiable" +resolved *before* the run rather than during it, because there is no human to negotiate +with in the middle of the night (see [References](#references)). + +::: tip A vague ticket is worse at night than by day +By day, a confused agent can ask you. By night it cannot — it will guess, and you +inherit the guess in the morning. The cost of ambiguity is paid in full overnight. +::: + +### 3-3. Pre-answer the judgment calls you can anticipate + +The heart of this policy: **walk the ticket in your head, find the forks in the road, +and pre-answer the ones you can.** A well-specified prompt closes the decisions an agent +would otherwise resolve by guessing. + +- Before queueing, the developer **SHOULD** ask: *"Where will the agent hit a decision + it isn't equipped to make?"* — naming conventions, library choice, an ambiguous + requirement, an edge case, a trade-off between two valid approaches. +- For each anticipated fork, **write the answer into the ticket**: the preferred option + and *why*, or an explicit rule ("if X is ambiguous, prefer Y"). This is what turns a + prompt from *suggestive* into *well-specified*. +- Give the agent the **context it needs to stay on rails**: relevant files, the + conventions to follow, examples of the pattern you want, and the acceptance check. + Under-specifying is the single largest cause of a wasted night. + +### 3-4. Collect the judgment calls you *can't* pre-answer — don't let the agent guess them + +Some decisions genuinely need a human: they are irreversible, they touch shared state, +they require taste or client knowledge the agent doesn't have, or they only surface once +the work is underway. These **MUST be surfaced, not silently decided.** + +- Tickets **MUST** instruct the agent, on hitting an un-pre-answered judgment call, to + **stop that thread, record the question, and move on** to other work — never to guess + and barrel ahead on an irreversible or shared-state action. +- The output of the night therefore includes a **morning question queue**: the decisions + the agent parked for a human. This is the deliberate handoff back from on-the-loop + (overnight) to in-the-loop (the human, at their desk). +- **Reversibility decides the rule** (consistent with the [Development + Guide](/development-guide)): a cleanly reversible step the agent may take and flag for + review; an irreversible or shared-state action (a production change, a destructive + migration, anything teammates or clients can observe) the agent **MUST** leave for a + human. When unsure, park it as a question — parking is always safe. + +::: warning No blank-cheque trust +"The agent ran all night and it's probably fine" is not a verification. An unattended +run that touched things no human has reviewed is **unverified work**, and unverified +work does not merge, deploy, or get built upon — full stop. +::: + +### 3-5. Set guardrails before the run + +An unattended agent needs **hard boundaries**, not just good intentions — the fence that +makes autonomy safe rather than reckless (see [References](#references)). + +- Developers **MUST** define, before launching, the agent's **allowed scope** (which + repos, directories, branches, and systems it may touch) and its **prohibitions** + (production, secrets, destructive operations, anything outside the ticket). +- Overnight agents **SHOULD** work on an **isolated, reversible surface** — a branch, a + worktree, a preview environment — so the entire night's work can be inspected as a + diff and thrown away if wrong, touching nothing shared until a human approves. This is + the same reviewed-main-line discipline the [CI/CD Pipeline](/ci-cd-pipeline) depends + on: nothing reaches the shared line unreviewed. +- Match the leash to the ticket's risk. Think in **levels of autonomy**: a low-risk, + well-fenced ticket (docs, tests, a mechanical refactor) earns a long leash; a ticket + near shared state or client data earns a short one, or waits for a daytime pairing + session. Not every ticket has earned the right to run alone (the levels-of-automation + idea, from self-driving classification and human-factors research; see + [References](#references)). + +### 3-6. Begin every day by reviewing the night + +The morning is the **review-on-return** half of on-the-loop supervision, and it is +mandatory. The day does not start with new work; it starts with **judging last night's +work.** + +- The developer **MUST** begin the day by reviewing the overnight output **before** + merging, deploying, or building on any of it: read the diff, run the checks, confirm + the agent stayed in scope. The AI code review the [Quality Gate](/quality-gate) + requires applies in full, and [Code Review](/code-review) is where that judgment + happens — an agent's diff is reviewed exactly as a human's would be. +- Then **work the morning question queue** from §3-4 — answer the parked judgment calls, + which either unblocks a follow-up run or becomes the day's interactive work. +- What passes review is accepted; what doesn't is **corrected in the ticket, not patched + in the output** — a ticket that produced a bad night gets a clearer specification for + the next one. Over time this tightens the Definition of Ready (§3-2) for the whole + team. +- Treat the review honestly and at **team level**: a bad overnight result is a signal + about the *ticket and the guardrails*, never a rating of a person (consistent with how + OSBR treats delivery metrics in the [CI/CD Pipeline](/ci-cd-pipeline)). + +## 4. Summary Loop + +1. **Day** — write well-formed, independent tickets; pre-answer the anticipated judgment + calls; set the guardrails. +2. **Handoff** — queue only tickets that pass the overnight Definition of Ready; the + agent parks (does not guess) anything it can't safely decide. +3. **Night** — the agent runs on-the-loop, unattended, on an isolated reversible + surface. +4. **Morning** — review the night's output before trusting any of it; work the question + queue; feed corrections back into the tickets. + +**The AI works the night. The human judges the morning. Neither skips their half.** + +## References + +Named practices this policy is built on, chosen because they are established and freely +readable. + +**Supervisory autonomy — in-the-loop vs on-the-loop** + +- Sheridan & Verplank, *Human and Computer Control of Undersea Teleoperators* (levels of + automation; supervisory control) — +- NIST AI Risk Management Framework (human oversight of AI systems) — + + +**Levels of autonomy** + +- SAE J3016 — Levels of Driving Automation (the popular "levels 0–5" model of graded + autonomy) — + +**Well-formed work items — Definition of Ready & INVEST** + +- Bill Wake, *INVEST in Good Stories, and SMART Tasks* — + +- Scrum Guide (the empirical basis for a shared Definition of Ready) — + + +**Agentic AI, guardrails & prompting** + +- Anthropic, *Building Effective Agents* (agentic patterns; guardrails and scope) — + +- Anthropic, *Claude Code Best Practices* (well-specified prompts, unattended runs) — + + +**Batch processing** + +- Batch processing (accumulate self-contained jobs, run unattended) — + + +**Related OSBR standards** + +- [AI Usage Guideline](/ai-usage-guideline) — the human ⇄ AI cooperation stance this policy operationalises. +- [Quality Gate](/quality-gate) — implementer-owns-quality; AI code review. +- [Code Review](/code-review) — where the morning review of an agent's diff happens. +- [CI/CD Pipeline](/ci-cd-pipeline) — reviewed main line, dev/prod parity, delivery metrics. +- [Development Guide](/development-guide) — how we ship; the reversibility rule. diff --git a/doc/policies-as-plugins.md b/doc/policies-as-plugins.md new file mode 100644 index 0000000..22539a8 --- /dev/null +++ b/doc/policies-as-plugins.md @@ -0,0 +1,199 @@ +# Policies as Plugins + +This is the standard for how the OSBR engineering policy series reaches the AI +agents that write our code. Every policy in the series is not only a page a +person reads here at [handbook.osbrjp.com](https://handbook.osbrjp.com) — it also +**ships as a plugin the agents load**, so the standard is present in the model's +context *at the moment of generation*, not discovered in review after the +violation is already written. The aim is a **single source of truth** that a +person can read and a machine can load: one policy, rendered for both, never two +copies quietly drifting apart. It works alongside the [AI Usage +Guideline](/ai-usage-guideline) (how humans and agents share the work) and the +[Quality Gate](/quality-gate) (where compliance is actually proven). Deviations +are allowed, but — as everywhere in the handbook — they must be deliberate and +justified in the project's design notes. + +This is where OSBR's values reach the agents as directly as they reach us. +**Be Nice**: one source of truth is an honesty owed to everyone downstream — +no engineer, and no agent, is ever governed by a stale copy nobody remembered to +update. **Be Kind**: we meet the agents where they work, putting the standard in +their context in the form they can load, so the next contributor — human or +model — inherits rules that are present exactly where the work happens. +**Be Strong**: we hold the same line for the agent that we hold for ourselves — +refusing to merge a policy change that leaves humans and machines reading +different rules, and refusing to accept a silent plugin as proof of anything. +This is the human ⇄ AI principle made operational: the same words govern the +person and the agent, and they are updated together. + +## How to read this policy + +* **Requirement levels** follow RFC 2119. **MUST** / **MUST NOT** are absolute. + **SHOULD** / **SHOULD NOT** state a strong default overridable only with a + documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice — shift-left + governance, policy-as-code, docs-as-code — the practice is named inline and + cited under [References](#references). We adopt the *idea* and right-size it for + an SME; we do not adopt the tooling or scale behind its reference setups. + +[[TOC]] + +## 1. Goal + +The goal is a policy that is **readable by a person and loadable by a machine** — +one source of truth, two renderings, never two divergent copies. Concretely: + +- Put the standard in the model's working context **before the first line is + generated**, so the agent writes to the rule instead of being corrected against + it afterwards. The cheapest violation to fix is the one the agent never writes + because it already knew the rule. +- Prevent the named failure this policy exists for: **standards drift between + humans and AI** — the state where the handbook says one thing, the plugin the + agent loads says another (or nothing), and neither the reviewer nor the model + can tell which is authoritative. An agent cannot follow a rule it never + received; a policy that lives only on a docs site the agent does not read is, + from the agent's side, not a policy at all. +- Keep the machine-facing copy an honest rendering of the human-facing one, so the + two never quietly disagree. + +This is **shift-left governance** applied to how we author software with AI. Just +as [policy-as-code](#references) moves compliance from a late audit gate to an +early, version-controlled artifact, distributing our policies as plugins moves the +standard from a post-hoc review comment into the model's context. The plugin +shifts the standard *left* into generation — it does not replace the gate on the +right (§3-4). + +## 2. Responsibility + +- The **author of a policy change owns every rendering of it.** "Done" includes + the agent plugins, not just the page; a pull request that edits the article + without updating the plugins it renders MUST say why in its Specification + section (per the [Development Guide](/development-guide)). This is the same + implementer-owns-quality rule the Quality Gate states. +- The **reviewer** treats the plugin diff as part of the reviewable surface: a PR + that updates one surface but not the others is rejected exactly as an incomplete + rename is (§3-2). This is a natural extension of the AI code review the + [Quality Gate](/quality-gate) requires. +- The **team** owns drift over time: the plugins are periodically audited against + the published articles, as part of the policy-conformance auditing the [SHEQ + Policy](/sheq-policy) already runs, and any divergence is a defect to reconcile. +- **AI agents** are governed by the same policies they help author and are held to + exactly the same bar — the human ⇄ AI stance of the [AI Usage + Guideline](/ai-usage-guideline). The human who merges an agent's work owns it. + +## 3. Practices + +### 3-1. Ship every policy as a plugin the agents load + +Each article in the engineering policy series MUST be reachable by our AI agents +as a plugin, in the native form each coding agent understands, loaded into context +when the matching kind of work is scoped or implemented. + +- The plugin MUST carry the policy's **normative content** — its Goal, + Responsibility, and Practices, its MUST / SHOULD rules — not a lossy summary. A + summary that drops a MUST is a new, weaker policy wearing the old one's name. +- The plugin SHOULD be **scoped to trigger on the work it governs** (the database + policy loads when schema work is in play; the security policy when an auth + surface is touched), the way our pillar-policy skills already preload as a lens + for their domain. Relevance is what keeps the context useful rather than noise. +- The mechanism is deliberately close to the **Model Context Protocol (MCP)** + stance on context: a policy is context the model needs to do the task correctly, + delivered through a stable interface rather than pasted ad hoc into a prompt — + scoped to the work at hand instead of bloating every prompt with the whole + handbook. + +### 3-2. Update the article and every plugin in one pull request + +Because the human page and the agent plugins are **renderings of one policy**, +they MUST move together. A change to a policy MUST update the handbook article and +every plugin that renders it **in the same pull request**. + +- A PR that edits the article but not the plugins, or vice versa, MUST be rejected + in review. Naming the divergence a "follow-up" is precisely how the two copies + part ways — drift starts with one deferred surface. +- The surfaces MUST NOT carry divergent normative content. There is one policy; the + renderings are reviewed against one another, never allowed to disagree. +- Where practical, the plugins SHOULD be **generated from the same source** as the + page, so "same PR" is enforced by the build rather than by reviewer memory. A + single source of truth is strongest when divergence is structurally impossible, + not merely discouraged. + +### 3-3. Keep every policy in the Goal / Responsibility / Practices shape + +Every policy — page and plugin alike — MUST keep the house structure: **Goal**, +**Responsibility**, **Practices**, with normative force carried by explicit MUST / +SHOULD. This is not house style for its own sake; it is what makes a policy +*loadable*: + +- **Goal** tells the model *why*, so it can reason about cases the rules did not + enumerate. +- **Responsibility** gives the MUST / SHOULD rules the model applies directly. +- **Practices** gives the concrete, worked guidance that pins ambiguous rules to a + recognisable shape. + +A consistent structure is good prompt engineering at the corpus level: the agent +learns the shape once and reads every policy the same way, and a reviewer checks +every policy against the same skeleton. An article that abandons the structure is +harder for both to consume, and is itself a review defect. + +### 3-4. Treat plugin silence as unknown, not clean + +A loaded policy that raises no objection is **not** evidence the work is +compliant. The plugin puts the standard in context; it does not *prove* adherence. + +- The absence of a flag MUST NOT be read as a pass. The agent may not have loaded + the relevant policy, the policy may not cover this case, or the model may simply + have missed it. Silence is *unknown*, not *clean*. +- Compliance is still earned the way it always is: by **human review and by + executable checks** — the genuine [policy-as-code](#references) gate where one + exists, tests, and reviewer judgement — the proof the [Quality + Gate](/quality-gate) requires. The plugin shifts the standard left into + generation; it is additive to verification on the right, never a substitute for + it. Both are needed. +- Verify against ground truth; do not infer success from a tool that stayed quiet. + +### 3-5. Working rules + +- **One PR, all surfaces.** When you change a policy, the checklist is: article + updated, every plugin updated — in *this* PR. If you cannot update them all now, + do not merge a partial change. +- **Write the normative content once, render it everywhere.** Prefer generating the + plugins from the same source as the page over hand-maintaining separate copies. + Hand-kept copies are chances to diverge; one source with rendered outputs has + none. +- **Scope the plugin to its work.** A policy that loads on every prompt is noise the + model learns to ignore; one that loads when its domain is in play is a lens the + model actually uses. Match the pillar-policy preloading pattern. +- **Load the policy before you generate, not after.** The value of a policy-as-plugin + is realised only if the relevant policy is in context *at generation time* — pull + it in when you scope the work, the way you would read the handbook page before + designing against it. +- **Verify anyway.** Treat the plugin as a well-informed collaborator, not an + auditor. Run the real checks, get the human review, and never let a quiet plugin + stand in for either. +- **Keep the plugins honest about drift.** Periodically diff the plugin content + against the published article; any divergence is a defect to reconcile, and a + signal that "same PR" was skipped somewhere upstream. + +## References + +**Shift-left governance & policy-as-code** + +- Shift-left — moving checks earlier, into the authoring loop — +- Open Policy Agent (OPA) / Rego — policy as versioned, testable code — +- Conftest — test configuration against OPA policies in CI — + +**Single source of truth & docs-as-code** + +- Docs-as-Code (Write the Docs) — docs in version control, reviewed and shipped like code — +- Single Source of Truth (SSOT) — one authoritative, non-duplicated definition — + +**Context for models** + +- Model Context Protocol (MCP) — supplying context to models through declared interfaces — + +**Related OSBR standards** + +- [AI Usage Guideline](/ai-usage-guideline) — the human ⇄ AI stance this policy operationalises. +- [Quality Gate](/quality-gate) — the human review and executable checks that prove compliance on the right. +- [SHEQ Policy](/sheq-policy) — policy-conformance auditing that catches plugin↔article drift. +- [Development Guide](/development-guide) — the pull-request Specification section where "same PR" is owned. diff --git a/doc/predefining-non-functional-requirements.md b/doc/predefining-non-functional-requirements.md index 1518f24..f667bb5 100644 --- a/doc/predefining-non-functional-requirements.md +++ b/doc/predefining-non-functional-requirements.md @@ -46,7 +46,7 @@ Ensures the system is intuitive and easy to use, especially for non-technical us ### Examples: - All interactive elements must be keyboard navigable. -- System must comply with [WCAG 2.1 AA accessibility standards.](https://www.w3.org/TR/WCAG21/) +- System must comply with [WCAG 2.2 AA accessibility standards.](https://www.w3.org/TR/WCAG22/) See the [Accessibility](/accessibility) standard for how we meet this. - User onboarding should take less than 5 minutes with guided walkthroughs. ### 2-4. Reliability diff --git a/doc/privacy-policy.md b/doc/privacy-policy.md new file mode 100644 index 0000000..ef2c633 --- /dev/null +++ b/doc/privacy-policy.md @@ -0,0 +1,149 @@ +# Privacy Policy + +This is OSBR's organisation-level commitment on personal data — the promise, made to the people whose data we hold, that stands behind everything else in this handbook. It is the counterpart of the [Security Policy](/security-policy) (which protects information assets) and the [SHEQ Policy](/sheq-policy) (which commits us on safety, health, environment, and quality): where those govern what we protect, this governs the trust of the person the data describes. It is a **notice**, not an engineering spec — the concrete technical controls that satisfy these commitments live in the [Data Protection](/data-protection) standard, and the legal judgement calls behind them in [Legal Compliance](/legal-compliance). This document is the promise those pages keep. + +OSBR is a Malaysian company, so our home law is Malaysia's **Personal Data Protection Act 2010 (PDPA)**. Because we work with a Japanese parent studio and with clients elsewhere, **Japan's APPI** and, where a client brings us within their reach, the **EU's GDPR** and comparable US regimes also apply. These regimes rest on the same core duties, so the commitments below honour all of them. + +Personal data is not ours. It belongs to the person it describes, and they lend it to us for a purpose. **Be Nice**: a person who can see what we hold, correct it, and ask us to stop is someone we have treated as a partner, not a resource. **Be Kind**: behind every record is a human being who trusted someone with it, and honouring that trust is the whole point. **Be Strong**: do the unglamorous work of collecting less, deleting on time, and telling a client plainly when a use of data exceeds what the person consented to — even when more data would be convenient. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as elsewhere in the handbook. **MUST** / **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a strong default overridable only with a documented reason. **MAY** marks a free choice. +* **Named practice.** These principles are not novel inventions — they are the long-settled duties shared across the world's data-protection regimes, named inline and cited under [References](#6-references). We adopt the *discipline* of these regimes and right-size it for an SME; we do not import a large enterprise's headcount or ceremony to do so. + +[[TOC]] + +## 1. Goal + +Every protection in this handbook exists so that OSBR can be trusted with a loan of personal data: that we take only what the purpose needs, use it only for what we said, guard it as if it were our own most sensitive secret, and hand it back, correct it, or stop using it the moment the person asks and the law allows. + +**OSBR commits to handling personal data lawfully and fairly: to obtain it only within the scope necessary for clearly stated business purposes and with consent; to give clear notice and a real choice; to use it only within those stated purposes, re-obtaining consent before exceeding them; to restrict disclosure to third parties without consent; to protect it with organizational, human, physical, and technical safeguards; to keep it accurate and no longer than needed; to supervise anyone we entrust it to; and to honour a person's rights of access, correction, and withdrawal as the law requires.** + +These commitments are the **seven Personal Data Protection Principles** of Malaysia's [PDPA](https://www.pdp.gov.my/) — **General, Notice and Choice, Disclosure, Security, Retention, Data Integrity, and Access** — as amended by the Personal Data Protection (Amendment) Act 2024. The same duties are named in Japan's [個人情報保護法 (APPI)](https://www.ppc.go.jp/en/legal/), the EU's [GDPR](https://gdpr-info.eu/), the [OECD Privacy Guidelines](https://www.oecd.org/en/publications/2013/07/the-oecd-privacy-framework_g1g33269.html), and the privacy-information-management system codified in [ISO/IEC 27701](https://www.iso.org/standard/71670.html). OSBR adopts them as commitments, not as a compliance chore. + +## 2. Responsibility + +| Role | Responsibility | +| --- | --- | +| **OSBR (as a company)** | Owns this commitment. Provides the policies, training, and controls that make it real, and answers for it when it fails. Appoints a **Data Protection Officer** where the PDPA requires one. | +| **Data Protection Officer** (where required) | The person accountable for personal-data compliance — that purposes are stated, consent is recorded, retention is bounded, breaches are notified, and access/correction/withdrawal requests are answered in time. Under the amended PDPA a DPO must be appointed above the prescribed thresholds and be ordinarily resident in Malaysia. | +| **Every developer / collaborator** | Handles personal data only within the stated purpose and their granted access; raises the moment a design or request would collect more, use it differently, or send it somewhere new. | +| **Contractors & data processors** | Bound by contract to protect personal data to the same standard, and supervised by OSBR for the life of the engagement (§3-5). | +| **Client** (often the data controller) | Confirms the business facts that set the purpose and lawful basis, and co-owns the duties that are legally theirs as the controller. | + +Whether OSBR is the **data controller** (we set the purpose) or a **data processor** (we build and run it for a client who is the controller) depends on the engagement — the amended PDPA now places obligations directly on processors too, and we honour the same principles in either role. Genuinely legal judgement calls (does a given use exceed the stated purpose? is a transfer a "disclosure"?) are escalated per [Legal Compliance](/legal-compliance); this policy makes sure the question gets asked in time. + +## 3. Practices + +Named, established practice, right-sized for an SME. The principles are universal; the ceremony is not — adopt the discipline of the reference regimes without importing a large enterprise's headcount. + +### 3-1. Notice and choice: obtain only what the purpose needs, lawfully and fairly + +We do not collect personal data because it might be useful someday. Collection is tied to a **stated purpose** and kept to the **minimum that purpose requires** — the PDPA's **General** and **Notice and Choice** principles, echoed by the APPI's duty to specify the purpose of use (利用目的), [GDPR Art. 5(1)(c)](https://gdpr-info.eu/art-5-gdpr/) data minimisation, and the OECD Collection Limitation principle. + +- Every collection of personal data MUST give the person **written notice of the purpose before or at the point of collection**, in a language they can understand — the PDPA Notice and Choice Principle (notice in Malay and English), the APPI purpose-of-use duty, and [GDPR Arts. 13–14](https://gdpr-info.eu/art-13-gdpr/). +- Collection MUST be by **lawful and fair means** — never by deception, concealment, or coercion. +- We collect the **minimum fields** the stated purpose needs, and no more. A field nobody can tie to a stated purpose is a field we do not collect. +- Where consent is the basis, it MUST be a **specific, informed, freely-given** agreement to that stated purpose, and MUST be **recorded** — the wording agreed, and when. **Sensitive personal data** (health, religion, political opinion, and the like) requires **explicit consent** under the PDPA. + +### 3-2. Use it only within the stated purpose; re-consent to exceed it + +The purpose stated at collection is a **boundary**, not a formality. This is *purpose limitation* — the PDPA General Principle, the APPI's rule that personal data may not be handled beyond the specified purpose without consent, [GDPR Art. 5(1)(b)](https://gdpr-info.eu/art-5-gdpr/), and the OECD Purpose Specification and Use Limitation principles. + +- Personal data MUST be used **only within the stated purpose of use**. A new purpose is a new decision, not an extension of an old one. +- To use personal data **beyond the stated purpose**, OSBR MUST either **re-state the purpose and obtain fresh consent** before the new use, or rely on another lawful basis where the law provides one. Silence is not consent, and "they already gave us the data" is not a basis. +- A change of purpose is a **material change**: the new wording is put to the person and the fresh consent recorded (§3-1) before any use under it begins. + +### 3-3. Disclosure: do not provide to third parties without consent + +Handing personal data to someone outside OSBR is the moment of highest risk and highest duty. The PDPA **Disclosure Principle**, the APPI's third-party-provision rule (第三者提供), and the GDPR's transfer safeguards all start from restriction. + +- OSBR MUST **not disclose personal data to a third party without the person's prior consent**, except where the law expressly permits it (e.g. legal obligation, protection of life, or a properly-scoped data-processor arrangement under §3-5). +- A **data processor we entrust data to in order to carry out the stated purpose is not the same as a disclosure** — but it is only exempt when it is properly contracted and supervised (§3-5). The distinction is legal, not convenient; when unsure, treat it as a disclosure and get consent. +- **Cross-border transfers**: the amended PDPA permits transfer to a place with **substantially similar protection or that ensures an equivalent level of protection**, or under consent or another permitted ground. Where the data physically resides and any additional safeguards are decided in [Legal Compliance](/legal-compliance) before the transfer is made. + +### 3-4. Security: protect it with organizational, human, physical, and technical safeguards + +OSBR commits to the **four categories of safeguard** that satisfy the PDPA **Security Principle**, the APPI's necessary-and-appropriate measures (安全管理措置), the controls of [ISO/IEC 27701](https://www.iso.org/standard/71670.html) / [27001](https://www.iso.org/standard/27001), and the GDPR's [Art. 32 security-of-processing](https://gdpr-info.eu/art-32-gdpr/) duty: + +| Safeguard | What OSBR commits to | Where it is specified | +| --- | --- | --- | +| **Organizational** | A named owner per engagement, defined handling rules, access review, and an incident response path. | [Security Policy](/security-policy) | +| **Human** | Security and privacy training before joining a project; a culture where raising a concern is expected, not punished. | [Security Policy](/security-policy) | +| **Physical** | Device encryption, auto-lock, no work in public spaces, no uncontrolled removable media, remote-wipe. | [Security Policy](/security-policy) | +| **Technical** | Least-privilege access, MFA/passkeys, no long-lived credentials, encryption in transit and at rest, logging and monitoring. | [Data Protection](/data-protection) | + +**Breach notification.** Under the amended PDPA, OSBR MUST **notify the Personal Data Protection Commissioner of a personal-data breach as soon as practicable**, and **notify affected individuals where the breach is likely to cause significant harm** — the same duty the APPI places toward the PPC and the GDPR toward the supervisory authority. The operational flow is the [Incident Management](/incident-management) standard. + +### 3-5. Supervise everyone we entrust personal data to + +When OSBR entrusts personal data to a data processor to carry out the stated purpose, the duty of care does not transfer with the data — it stays with us. This is part of the PDPA Security Principle (and the amended Act's direct obligations on processors), the APPI's duty to supervise a trustee (委託先の監督), and the GDPR's [Art. 28 processor](https://gdpr-info.eu/art-28-gdpr/) requirements. + +- Entrustment MUST be under a **written contract** binding the processor to protect the data to at least OSBR's standard, to use it only for the entrusted purpose, and to return or delete it at the end. +- OSBR MUST **supervise** the processor for the life of the engagement — selecting them for adequate protection, and not treating the contract as the end of the duty. +- A sub-processor is entrusted onward **only with the same protections carried through**, and within what the person consented to. +- This is the same supply-chain diligence OSBR applies to any vendor, focused specifically on personal data. + +### 3-6. Retention, integrity, and access: keep it right, no longer than needed, and honour the person's rights + +Three PDPA principles meet here — **Retention**, **Data Integrity**, and **Access** — and this is where all of the above becomes real to the person. + +- **Retention.** Personal data is **kept only as long as the stated purpose and any legal retention duty require**, then deleted — the PDPA Retention Principle, [GDPR Art. 5(1)(e)](https://gdpr-info.eu/art-5-gdpr/) storage limitation, and the balance set in [Legal Compliance](/legal-compliance). +- **Data integrity.** We take reasonable steps to keep personal data **accurate, complete, not misleading, and up to date** for its purpose (PDPA Data Integrity Principle). +- **Access & correction.** OSBR MUST provide a **reachable way to make a request** and respond within the period the applicable law sets — verifying the requester's identity first, so a request cannot become a leak. On a valid request we **disclose** what we hold, **correct or complete** it where wrong, and **stop use, delete, or withdraw** where the law requires (PDPA Access Principle and the rights to withdraw consent and to **data portability** added by the 2024 amendment; the APPI's 開示・訂正・利用停止; the GDPR's [Arts. 15–18 and 20](https://gdpr-info.eu/chapter-3/)). +- A request the law does *not* oblige us to grant (e.g. it would breach another's rights, or a retention duty overrides it) is answered with a **reasoned explanation**, not silence. + +## 4. Rules Summary (MUST / SHOULD) + +- Every collection of personal data **MUST** give notice of the purpose at or before collection, be limited to the minimum that purpose needs, and be obtained by lawful and fair means (§3-1). +- Where consent is the basis it **MUST** be specific, informed, freely given, and recorded; **sensitive personal data MUST** have explicit consent (§3-1). +- Personal data **MUST** be used only within the stated purpose; exceeding it **MUST** be preceded by a re-stated purpose and fresh consent, or another lawful basis (§3-2). +- OSBR **MUST NOT** disclose personal data to a third party without prior consent, except where the law expressly permits or under a properly-supervised data-processor arrangement (§3-3). +- Cross-border transfers **MUST** meet the PDPA's adequacy/consent grounds, decided per [Legal Compliance](/legal-compliance) (§3-3). +- OSBR **MUST** protect personal data with organizational, human, physical, and technical safeguards kept adequate to what it holds (§3-4). +- OSBR **MUST** notify the Commissioner of a personal-data breach as soon as practicable, and affected individuals where significant harm is likely (§3-4). +- Every data processor entrusted with personal data **MUST** be bound by written contract and supervised for the life of the engagement (§3-5). +- Personal data **MUST** be kept accurate and retained only as long as the purpose and legal duty require, then deleted (§3-6). +- OSBR **MUST** honour access, correction, withdrawal, and data-portability requests as the law requires, after verifying the requester's identity (§3-6). +- OSBR **MUST** appoint a Data Protection Officer where the PDPA thresholds require one (§2). +- OSBR **SHOULD** review this commitment, and the controls that satisfy it, at least annually and whenever the personal data it handles materially changes. + +## 5. Related Guidelines + +- [Data Protection](/data-protection) — the engineering standard specifying the technical controls that satisfy the safeguard commitment (§3-4). +- [Security Policy](/security-policy) — the organizational, human, and physical controls, and the incident path, behind the safeguard commitment (§3-4). +- [Incident Management](/incident-management) — the breach-notification flow (§3-4). +- [Legal Compliance](/legal-compliance) — which privacy laws apply, retention limits, cross-border transfer, and the escalation of legal judgement calls (§2, §3-3, §3-6). +- [SHEQ Policy](/sheq-policy) — the sibling company-level commitment on safety, health, environment, and quality. + +## 6. References + +OSBR is a Malaysian company serving clients worldwide. The commitments above rest on the duties common to the data-protection regimes below. + +**Malaysia** + +- Personal Data Protection Act 2010 (Act 709) and the seven Personal Data Protection Principles — Personal Data Protection Department (JPDP) — +- Personal Data Protection (Amendment) Act 2024 (Act A1727) — data-controller terminology, mandatory breach notification, Data Protection Officer, data portability, cross-border transfer — + +**Japan** + +- 個人情報保護法 (Act on the Protection of Personal Information, APPI) — Personal Information Protection Commission (PPC) — + +**European Union** + +- GDPR (Regulation (EU) 2016/679) — full text — +- GDPR Art. 5 — principles (lawfulness, fairness, purpose limitation, data minimisation, storage limitation) — +- GDPR Arts. 13–14 — information to be provided (purpose/notice) — +- GDPR Arts. 15–18, 20 — rights of access, rectification, erasure, restriction, and portability — +- GDPR Art. 28 — processor obligations (entrustment/supervision) — +- GDPR Art. 32 — security of processing — + +**United States** + +- California Consumer Privacy Act (CCPA), as amended by the California Privacy Rights Act (CPRA) — California Attorney General — + +**International frameworks** + +- OECD Privacy Framework — +- ISO/IEC 27701 — Privacy Information Management System (PIMS) — +- ISO/IEC 27001 — Information security management systems — diff --git a/doc/public/robots.txt b/doc/public/robots.txt new file mode 100644 index 0000000..5871a6e --- /dev/null +++ b/doc/public/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Allow: / + +# LLM index: /llms.txt +# LLM full text: /llms-full.txt diff --git a/doc/quality-gate.md b/doc/quality-gate.md new file mode 100644 index 0000000..92861ed --- /dev/null +++ b/doc/quality-gate.md @@ -0,0 +1,170 @@ +# The Quality Gate + +A piece of work is not done when it runs. It is done when it clears three +checks — it is **reliable**, **secure**, and **sustainable**. One engineer, with +their AI, holds all three as they build, and nothing moves to `Done` on the +board until all three are met. + +These are lenses, not a checklist someone else runs at the end. Each names the +standards we hold the work to, and points at the policy that defines the posture +behind them — the gate is where we confirm the work meets that standard, not +where the standard is re-written. The gate holds at `Impl Review`: a change that +adds bloat, weakens a defence, or leaves a solution costly to keep alive is a +finding — even when it works — and goes back before it merges. + +Holding this bar is where our values meet the code. It is **Be Nice** — a high +standard of care for the people who use what we build and the next person who +maintains it; **Be Kind** — a solution left sound enough for someone else to +own; and **Be Strong** — the readiness to find our own gaps rather than ship +them. + +[[TOC]] + +## Reliability + +A reliable solution does what it is meant to, and keeps doing it. + +**It starts in how the code is written.** We build the simplest thing that +works, keep one source of truth for each fact, and add only what is needed now. +Code shaped this way — small, clear, purposeful — behaves predictably, and the +next reader, human or AI, can change it without fear. Design begins from a model +of the problem, not from the first screen: who the actors are, what events +occur, what data and demands follow. Code that mirrors a clear model stays +reliable as it grows. + +**We prove it as we build.** The engineer who writes a change plans and judges +its verification — quality is not handed to a separate stage at the end. We test +against real databases and real interfaces, not only mocks, because the failures +that matter live at the seams; we aim tests at those boundaries and at the +domain logic that carries the business. Coverage is a signal, not a goal. An +AI review at `Impl Review` — the agent on the engineer's own machine reading the +change, a person judging what it finds — is part of how a change earns its +merge, not an optional courtesy. + +**We assume things will break.** Every call that leaves our process has a +timeout and a defined behaviour when it fails; every dependency can fall over, +so we plan for it — sensible retries, a circuit breaker where a retry storm +would hurt, and a rollback path decided before the change ships, not during an +incident. Where one tenant's trouble must not reach another, we keep them apart. + +**We can see it once it runs, and we answer for it when it breaks.** Every +service emits structured logs, metrics, and traces from day one, and we alert on +user-facing symptoms, not on every low-level metric. When something goes wrong +we record it — blamelessly, and regardless of scale — contain it, remediate the +cause, and investigate production read-only, with personal data masked before it +reaches any AI context. The reporter of an incident does not decide whether it +was worth reporting. + +Held to the standards that carry the detail: [Testing +Standards](/testing-standards), [Observability & +Resilience](/observability-resilience), [Incident +Management](/incident-management), [Code Review](/code-review), [CI/CD +Pipeline](/ci-cd-pipeline), and [Architecture Standards](/architecture-standards) +— and to the [Infrastructure Planning Policy](/infra-planning-policy) (measured +SLI/SLO and delivery, resilience patterns, environment parity, backups and +disaster recovery, safe and reversible deploys) and the [Non-functional +Requirements](/predefining-non-functional-requirements) (the availability, RPO, +and RTO targets a solution is sized against). + +- We **MUST** let the engineer who builds a change also test it, run it, and + answer for it — verification is planned at design, not bolted on at the end. +- We **MUST** exercise critical paths against real dependencies, not only mocks, + aiming tests at boundaries and domain logic. +- We **MUST** give every outbound call an explicit timeout and a defined failure + behaviour, and decide the rollback path before a change ships. +- We **MUST** make every service observable — structured logs, metrics, traces — + and alert on user-facing symptoms. +- We **MUST** record an incident regardless of who noticed or how small it + looks, and investigate production read-only with personal data masked. +- We **SHOULD** keep modules inside one deployable unit until a concrete need — + independent scaling, deployment, or fault isolation — is written down and + agreed. A distributed system is a cost, paid whether or not it is needed. + +## Security + +A secure solution protects the people and the data inside it. + +**We verify against a published baseline.** We hold our work to the OWASP +Application Security Verification Standard (ASVS), so that "secure" is a verdict +we can trace rather than a feeling. We verify authentication and session +handling, validate and bound every input at its trust boundary, encode every +output for its destination, and treat every external input as untrusted until +proven otherwise. + +**Access is least-privilege and auditable.** Each component gets only what it +needs. Administrative power sits on its own surface, behind its own explicit +authorisation step, never one checkbox away from ordinary use, and its actions +are logged. We layer independent defences so that no single lapse is fatal, and +we write down every place we deliberately relax one. + +**The data we hold is a responsibility, not a hoard.** We record consent as an +event, collect the minimum for a stated purpose and re-consent when that purpose +changes, keep only what we need, and let people export and delete what is +theirs. We are careful with what leaves the system — an email, a log line, a +value handed to an AI — because each one is a door, and we know which region +personal data lives in and keep it consistent with what the client and the law +require. + +**We own our supply chain,** because most breaches arrive through code we +imported, not code we wrote: we pin versions, scan dependencies, and would +rather hold a release than ship past a known, unpatched flaw. Risks we cannot +remove we name in a living register — with an owner, and an explicit decision to +accept them — rather than leave them unspoken. + +Held to the standards that carry the detail: [Application +Security](/application-security), [Access Control](/access-control), [Data +Protection](/data-protection), and [Supply Chain & Risk](/supply-chain-risk) — +and to the [Security Policy](/security-policy) and the OWASP ASVS baseline. + +- We **MUST** verify the solution against ASVS at the level its data warrants, + and record any requirement waived, with the reason. +- We **MUST** validate and bound every input at its trust boundary, and encode + every output for its context. +- We **MUST** keep administrative functions on a separate surface with a + separate, logged authorisation step. +- We **MUST** pin and scan dependencies, and hold any release that ships a known + unpatched vulnerability. +- We **MUST** mask personal data before it enters a log or an AI context, and + **SHOULD** name every accepted risk in a register with an owner. + +## Sustainability + +A sustainable solution endures — it keeps running without asking much of whoever +keeps it, and it costs little to keep alive. + +**It endures in the code and the record.** A well-built, well-documented +solution can sit for months and be picked up again — by us, by a client, or by +an AI — with the facts it needs already in the repository. Documentation lives +as code beside what it describes, not in a separate deliverable that rots; the +directory says what each part is; concepts map to resources and URLs the same +way every time. We favour boring, standard, replaceable parts over clever ones, +and draw clean boundaries — an anti-corruption shell between our model and a +dependency, cut lines that let a part be rebuilt rather than nursed — so keeping +the thing alive stays a small, quiet job rather than a standing burden. + +**It stays portable, so leaving stays possible.** When we depend on a vendor we +weigh its lock-in and exit cost, favouring open standards and portable data. A +solution we can move is a solution a client can take. + +**It is light because it is efficient.** The efficiency that makes a solution +cheap to keep alive is the same efficiency that makes it light on the world, and +we design for both at once — the infrastructure defaults that deliver this +(scale-to-zero when idle, capacity that follows demand, cost as a design +constraint, managed services over self-hosting) are set in the Infrastructure +Planning Policy, and a solution is held to them here. + +Held to the standards that carry the detail: [Architecture +Standards](/architecture-standards), [Repository & Documentation +Standards](/repository-documentation-standards), and [API Design](/api-design) — +and to the [Infrastructure Planning Policy](/infra-planning-policy) +(scale-to-zero and on-demand infrastructure, cost as a design constraint, +managed services, data portability). + +- We **MUST** keep the facts needed to run and change a solution in its + repository, so operating it never depends on one person's memory. +- We **MUST** meet the infrastructure defaults set in the Infrastructure + Planning Policy, and record why when a workload genuinely cannot. +- We **SHOULD** favour boring, standard, replaceable parts, and place an + anti-corruption boundary between our own model and an external dependency. +- We **SHOULD** weigh lock-in and exit cost when choosing a vendor, favouring + open standards and portable data. diff --git a/doc/repository-documentation-standards.md b/doc/repository-documentation-standards.md new file mode 100644 index 0000000..efb91a7 --- /dev/null +++ b/doc/repository-documentation-standards.md @@ -0,0 +1,369 @@ +# Repository & Documentation Standards + +This is the standard the [Quality Gate](/quality-gate)'s **Sustainability** lens +holds a repository to for how it is laid out and how it carries its own +knowledge. One idea runs through all of it: **the repository is the single +source of truth** — for the code, for the reasoning behind every change, and for +the facts from which any report, diagram, or briefing is generated. When +structure, history, and knowledge all live in the clone, anyone who opens it — +the next developer, yourself in six months, the client's team, or an AI agent +working in the tree without a colleague beside it — can find what they need by +reasoning about *where it must be*, not by being given a tour. Deviations are +allowed, but — as everywhere in the handbook — they must be deliberate and +recorded in the project's README or design notes. + +This standard is where three OSBR values become load-bearing structure. **Be +Nice**: a predictable layout and a legible record are the clearest documentation +a teammate or agent will ever read, so we write for whoever comes next. **Be +Kind**: never leave a colleague to re-learn the map under incident pressure, or +to inherit five drifting copies of the same fact — leave them one stable +skeleton and one true source. **Be Strong**: invest effort where it compounds +(the structural source that survives staff turnover and tool churn), not where it +evaporates (a hand-polished one-off document). Humans and AI agents both read and +write in this tree as collaborators, and every rule here is chosen so that +collaboration stays cheap and correct. + +## How to read this standard + +* **Requirement levels** follow RFC 2119, as in the [Coding Style + Guide](/style-guide). **MUST** / **MUST NOT** are absolute. **SHOULD** / + **SHOULD NOT** state a strong default overridable only with a documented + reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). We stand on published + convention and right-size it for an SME — we do not adopt the headcount or + infrastructure behind anyone's reference setup. + +[[TOC]] + +## 1. Goal + +The goal is that **structure carries knowledge**. When the shape of the tree is +predictable, a location becomes information: `scripts/` *is* where the runnable +scripts are, `docs/` *is* where the prose lives, and there is exactly one obvious +place for each kind of thing. When the reasoning behind a change is durable, +local, and reconstructable, a reader with **only this clone** can answer, for any +non-trivial change: *what* was decided and what was rejected, *why*, and *when* +relative to the decisions around it. And when facts are stored structurally, OSBR +stays permanently able to generate a correct explanation for any reader — a +one-line answer for a busy manager, a deep trace for an engineer, a +plain-language summary for a client — without anyone hand-maintaining a library +of finished documents. + +A person who has navigated one OSBR repo can navigate the next one without a +guide, and an AI agent can act by deriving a path instead of listing directories +until it stumbles on the right one. Every exploration step an agent avoids is +context saved, latency removed, and a chance to guess wrong eliminated. + +## 2. Responsibility + +- **Whoever creates or restructures a repository** owns its top-level layout. + Before inventing a new top-level directory, you check how OSBR repos already + divide the tree (§3-1) and carry that division over — you do not coin a local + convention because it suited one afternoon. +- **Every contributor** owns the record of their own change being present in the + repository before the change merges. A merged change with no retrievable + rationale is an incomplete change. The bar is not "was a ticket filed + somewhere"; it is "can the next reader, with only this clone, understand why". +- **Reviewers** reject a new top-level directory, an abbreviation, or a second + home for an existing kind of thing when it departs from the standard without a + recorded reason — exactly as they would reject a broken naming or schema + convention. They also check that the record of a change exists and is legible, + not only that the code is correct. +- **AI agents** working in the repository are held to the same standard: they + produce the same documents and the same structured commits, and they are + expected to *read* those records to reconstruct intent before acting. The human + who merges an agent's work owns it, whoever drafted it. +- **The repository README** is where any deliberate, project-specific departure + is recorded, with its reason. A directory or record whose purpose a newcomer + cannot guess and the README does not explain is a defect. + +## 3. Repository structure — divide the top level by role + +### 3-1. Divide the top level by role + +The top level of a repository is carved into a **small, fixed set of +directories, one per role.** The role is what the directory holds and why it +exists — not which team owns it or which feature it belongs to. The OSBR baseline +vocabulary: + +| Directory | Role — what lives here | +| --------- | ---------------------- | +| `packages/` | The shippable code units — one directory per package (§3-3) | +| `scripts/` | Runnable operational and developer scripts (setup, migration runners, one-off tasks) | +| `workloads/` | Deployable/runnable workloads — services, jobs, workers, functions | +| `databases/` | Schema, migrations, seed data — the persisted-state layer | +| `docs/` | Human-readable documentation and the models that back it | +| `outputs/` | Generated artifacts — build output, reports, exports (git-ignored unless a build genuinely needs to track them) | + +- **MUST** place each kind of thing under the one top-level directory whose role + fits it, and **MUST NOT** create a second home for a kind of thing that already + has one. Two directories that both hold "scripts" is exactly the ambiguity this + standard exists to prevent. +- **MUST NOT** divide the top level by anything other than role — not by team, not + by author, not by "old vs new". Feature and layer live *inside* a package + (§3-4), not at the top of the tree. +- **SHOULD** treat this list as the default vocabulary and reach outside it only + when a project genuinely has a role none of these names covers — and then name + the new directory by the same rules (§3-2) and record it in the README. + +This is **convention over configuration** (the Rails Doctrine): a fixed default +for *where things go* frees you from re-deciding and re-explaining the layout on +every project. The productivity does not come from any one name being optimal — +it comes from the name being *the same every time* so nobody has to think about +it. OSBR adopts the stance, not Rails' specific folders. + +### 3-2. Names are pronounceable, non-abbreviated words + +- **MUST** name every top-level directory with a **whole, pronounceable word** — + `packages`, `scripts`, `databases` — never an abbreviation, contraction, or + initialism. `pkgs`, `wl`, `db`, `docs-src`, `svc` are prohibited as top-level + names. (`docs` is the one settled exception — a universally-read word in its own + right, matching near-universal ecosystem convention. But `outputs`, not `out`; + `databases`, not `db`.) +- **MUST** keep names plural where the directory holds many of a thing + (`packages`, `scripts`, `workloads`): the collection is plural, and the + individual package inside it is named for the one concept it is (§3-3). +- **SHOULD** keep names to a single word. A compound, underscore-heavy directory + name is usually a smell that the role is not crisp — fix the concept, not just + the label. + +A name you can say out loud is one a team can talk about, one that survives a +code-review conversation, and one an AI agent tokenises and reasons over cleanly. +Abbreviations save keystrokes once and cost comprehension forever: the keystrokes +are yours; the expansion tax is everyone's. + +### 3-3. One directory per package + +- **MUST**, under `packages/` (and equally under `workloads/`), give each package + **its own directory, named for the one concept it is** — `packages/billing`, + `packages/notifications` — with nothing shared loosely between siblings at that + level. +- **MUST** make the package directory the self-contained unit: its own manifest, + source, tests, and local docs. A reader should understand one package by + looking in one directory, not by cross-referencing five. +- **SHOULD** keep package names aligned with the ubiquitous language — the package + that owns *invoices* is `invoices`, not `inv` and not `billing-stuff`. + +This is the layout the major monorepo tools assume: Nx and Turborepo structure a +workspace as many self-contained projects, each in its own directory under a +small number of role folders; Bazel formalises it hardest, where a directory with +a `BUILD` file *is* a package. OSBR uses `packages/` for library code and +`workloads/` for the deployable units — the same split Nx/Turborepo draw as +`packages/` vs `apps/`, named for what we deploy. + +### 3-4. Package by feature, not by layer — inside the package + +Role divides the *top* of the tree. **Inside** a package, structure follows the +**feature/domain**, not the technical layer. Do not create top-level +`controllers/`, `services/`, `models/`, `utils/` buckets that every feature has +to be smeared across. + +- **SHOULD**, within a package, group code by the business capability it serves + (package-by-feature) so that everything touching *one* concept sits together, + rather than scattering one feature across parallel layer folders. +- The reasoning is the documented **package-by-feature vs package-by-layer** + trade-off: layer-first grouping maximises the distance between things that + change together; feature-first grouping keeps a change local and makes deletion + clean. This is the same instinct the [Architecture + Standards](/architecture-standards) apply to module boundaries. + +This is Robert C. Martin's **screaming architecture**: the top level of a system +should *scream* what it **does**, not which framework built it. Package +directories named `invoices/`, `scheduling/`, `notifications/` tell you what the +business is; the framework is a detail you find later, inside. + +### 3-5. The same layout across every project + +- **MUST** carry the same top-level division and the same names from one OSBR + repository to the next. The point of a convention is that it is invariant — a + `scripts/` directory means the same thing, lives in the same place, and is named + the same word in every repo. +- **MUST** record any project-specific deviation in the README, with the reason. + An undocumented departure from the standard layout is a bug. +- **SHOULD** establish the standard directories up front on a new repository, even + if some start nearly empty, so the shape is present and predictable before the + code arrives — rather than growing the tree ad hoc and abbreviating under + pressure later. + +The Go community codified exactly this instinct as `golang-standards/project-layout`: +one agreed set of role directories, reused everywhere, so familiarity transfers +across the whole portfolio. The bar for a new top-level directory is *a genuinely +new role* — not "it felt tidy today". + +## 4. Documentation lives in the repository + +The rationale behind a change is worth as much as the change itself — and it is +worth nothing if a future reader cannot find it. The same is true of every +durable fact the project depends on. So both the *why* of a change and the facts +that back an explanation live inside the repository, reviewed in the same pull +request, versioned in the same history, and found by the same `grep`. + +### 4-1. Store the record in the repository, as markdown + +Tickets, branch stories, and release notes **MUST** live as markdown files +committed to the repository, not solely in an issue tracker, chat, or SaaS tool. +External trackers **MAY** mirror or link to them, but the repository copy is +canonical. + +This is the **docs-as-code** discipline: documentation authored in plain text, +versioned in git, reviewed through pull requests, travelling with the code it +describes. A record that lives only in a hosted tool has a different lifecycle +from the code — it can be edited without review, lost on a licence lapse, or made +unreachable by an offline clone. A record in the repo cannot. + +- Records **MUST** be plain markdown (no proprietary format), so any human or AI + reader can consume them with standard text tools. +- Each record **SHOULD** sit as close to the code it concerns as is practical — + proximity is what keeps decisions discoverable and stops them drifting out of + date. +- A link is a promise about a system you do not control. Link *out* for + convenience; keep the truth *in*. + +### 4-2. One fact, one place — store it structurally + +The Pragmatic Programmer's **DRY principle** — *"every piece of knowledge must +have a single, unambiguous, authoritative representation within a system"* — +applies to knowledge, not just code. A fact duplicated across five deliverables +is a fact that will be wrong in four of them within a month. + +- A number, definition, or decision **MUST** have exactly one authoritative home; + everything else links to it. If you find yourself copying a fact to "make the + document complete", stop — link instead, or generate the document from the + source. +- Store information in the **smallest reusable unit** — structured topics and data + — not in long prose blobs. This is the discipline of structured, composable + content (the idea behind DITA's "write once, use many"): store data *as* data + (tables, config, structured files), not as sentences describing the data. + Sentences cannot be recombined; data can. +- Give each unit a stable identifier so it can be referenced, not re-typed. + +### 4-3. Keep terms composable + +Composable content requires composable vocabulary. Two units that describe the +same thing with different words cannot be safely assembled into one explanation. + +- **SHOULD** name things with the project's ubiquitous language — the same + discipline applied to code and domain modelling, extended to all stored + knowledge. A new domain term is defined once, in the project's glossary, before + it is used. +- Design each stored unit assuming an AI agent will assemble it into an + explanation for an unknown reader: clear, self-contained, consistently named. + +### 4-4. Record architectural decisions as ADRs + +Decisions with lasting structural consequence **MUST** be captured as +**Architecture Decision Records** — short markdown files, one per decision, +committed under the repository (conventionally `doc/adr/` or `docs/decisions/`). +Follow Nygard's original lightweight form or the MADR template: + +- **Context** — the forces and constraints in play. +- **Decision** — what we chose to do. +- **Consequences** — what becomes easier and what becomes harder. +- **Status** — proposed / accepted / superseded. + +ADRs are **immutable once accepted**: a reversed decision gets a *new* ADR that +supersedes the old one, so the reasoning trail — including the roads not taken — +stays intact. We amend history by *appending*, never by rewriting. A pull request +itself serves as a lightweight ADR for smaller decisions (see the [Development +Guide](/development-guide)); reserve a standalone ADR for the structural ones. + +### 4-5. Let AI generate structured commit messages + +Commit messages **MUST** follow **Conventional Commits** (`type(scope): summary`, +with `feat`, `fix`, `refactor`, `docs`, etc., and `BREAKING CHANGE:` where it +applies). This gives the history a machine-readable shape that both tooling and +AI agents can parse for changelogs, release scoping, and traceability. + +- Commit messages **SHOULD** be **generated by AI from the actual diff and the + surrounding record**, not hand-typed. An agent that reads the diff, the ticket, + and the relevant ADR writes a more complete, consistent message than a tired + human at the end of a session — stating *what* changed and *why*, linking back + to the ticket or ADR. +- Commit bodies **SHOULD** name the *why* and reference the ticket / ADR / branch + story that carries the fuller context. +- The human author remains **responsible** for the committed message being true, + whoever (or whatever) drafted it. This is human⇄AI cooperation working as + intended: the human decides, the AI documents, and the reasoning lands where the + next reader will look for it. + +### 4-6. Documents are the curated record; commit history is raw material + +The **document files — tickets, branch stories, release notes, ADRs — are the +central, curated record.** The commit history is the **raw material** behind them: +high-fidelity, append-only, never groomed away. This is **living documentation** +in Martraire's sense — authoritative knowledge kept alongside the code and +refreshed as the code changes. + +- Commit history **MUST NOT** be squashed, rebased, or groomed in a way that + destroys the granular record. Preserving the individual commits preserves the + order and reasoning of the work — the raw material future readers and AI agents + mine for provenance. A squash merge collapses a branch's reasoning into one + opaque blob and discards the very sequence that shows *how* the change was + reasoned through. +- The curated documents **MUST** be kept current as the central record — they are + the map; the commits are the territory. When a document and the commit history + disagree, that is a signal to fix the document, not to discard the commits. + +### 4-7. Treat reports and diagrams as generated derivatives + +A deliverable is an *output*, not an *asset*. A slide deck is a photograph; the +repository is the subject — we invest in the subject. + +- **MUST** treat reports, briefings, and diagrams as generated derivatives: + regenerate them from source rather than editing the derivative and letting the + source go stale. If a report can be produced from the source, it must not be + maintained separately. +- Diagrams **SHOULD** be defined as code (e.g. Mermaid) next to the facts they + depict, so they update when the facts do. +- A generated deliverable can be thrown away without loss, because the capacity to + regenerate it never left the repository — the mark of living documentation. + Delete generated deliverables freely once delivered, trusting the repository to + regenerate them. + +### 4-8. Write for the next reader — human or AI + +Structured facts answer *what is true*; the commit and PR history answers *why it +became true*. Together they let an explanation be generated at any depth, +including the reasoning — and, because the source is versioned, *as of a point in +time*: what did we believe, and why, on a given date. + +Every record **MUST** be written so that a reader with **only this clone** and no +access to us can reconstruct the reasoning. Assume the tracker is gone, the chat +is unsearchable, and the author has left. What remains is the repository — so the +repository must carry enough. That is **Be Kind** made operational. + +## References + +**Repository & project layout** + +- Ruby on Rails — The Rails Doctrine (convention over configuration) — +- Nx — Folder structure / workspace conventions — +- Turborepo — Structuring a repository — +- Bazel — Packages, targets, and the BUILD concept — +- golang-standards/project-layout — a shared Go project layout — +- Robert C. Martin — Screaming Architecture — +- Package by feature, not layer (the trade-off) — + +**Records, decisions & change history** + +- Michael Nygard — Documenting Architecture Decisions — +- MADR — Markdown Any Decision Records — +- Architecture Decision Records (ADR) — +- Conventional Commits — + +**Docs-as-code, structured content & living documentation** + +- Write the Docs — Docs as Code — +- The Pragmatic Programmer — DRY / single source of truth — +- OASIS DITA — structured, reusable topic-based content — +- Mermaid — diagrams as code — +- Cyrille Martraire — *Living Documentation* — +- Martin Fowler — Living Documentation — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Sustainability lens this standard serves. +- [Architecture Standards](/architecture-standards) — module boundaries and package-by-feature structure. +- [Coding Style Guide](/style-guide) — RFC 2119 levels, ubiquitous language, the pure core. +- [Development Guide](/development-guide) — tickets, branch stories, pull-requests-as-ADRs. diff --git a/doc/requirements-modeling.md b/doc/requirements-modeling.md new file mode 100644 index 0000000..5b4174b --- /dev/null +++ b/doc/requirements-modeling.md @@ -0,0 +1,260 @@ +# Requirements Modeling + +This is the standard the [Development Guide](/development-guide)'s **Planning & +Shaping** stage holds requirements analysis to. It sets one rule and builds the +practice around it: **analysis MUST produce an explicit, diagrammed model of the +problem before anyone reaches for screens, tables, or code.** Screens and +schemas are consequences of the model, not substitutes for it. The model this +policy produces is what later becomes concrete in the [Architecture +Standards](/architecture-standards), and its vocabulary is the same [Domain +Terminology](/domain-terminology) the code and the client speak. Deviations are +allowed, but — as everywhere in the handbook — they must be deliberate and +justified in the project's design notes. + +We do not invent a modeling method of our own. We stand on named, published +practice — Domain-Driven Design, the C4 model, BPMN, UML/ER, and Event Storming +— and right-size each for an SME, the same way our infrastructure guidance +right-sizes the cloud Well-Architected frameworks. We adopt the *criteria* of +these practices, not the headcount behind their reference setups. + +Modeling is where OSBR's values become visible before a line is written. **Be +Nice**: a shared model makes our reasoning legible to collaborators — human and +AI — instead of hoarding it in one person's head. **Be Kind**: writing the +picture down once spares every future reader — the next developer, the client +six months on, the agent picking up a ticket — from reverse-engineering intent +from table names and screens. **Be Strong**: we refuse the false speed of coding +before we understand, because a disagreement caught as a diagram edit is +cheaper than the same disagreement caught three weeks into build. Humans and AI +agents model here as collaborators, against the same shared picture. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the rest of the handbook. + **MUST** / **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a + strong default overridable only with a documented reason. **MAY** marks a free + choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). Reach for the lightest + notation that makes the requirement unambiguous — formality serves clarity, + never the reverse. + +[[TOC]] + +## 1. Goal + +The goal of requirements analysis is **one agreed picture of the problem** — +shared by client, developers, and AI agents — before implementation begins. + +A model is that picture. It names the stakeholders, the events that happen in +their world, the systems involved, the data that flows, what people actually +demand, where it hurts today, and how we intend to relieve it. When this picture +exists and everyone can point at it, disagreements surface as diagram edits +instead of as rework deep into build. A model that documents a solution without +ever stating the problem it fixes is the exact failure mode this policy prevents +(§4). + +## 2. Responsibility + +Whoever picks up requirements analysis for a project owns the model. Concretely, +that person or pair MUST: + +- Produce the model **before** proposing screens, database tables, or code. +- Keep the model **in the repository**, as text, reviewed alongside code (§3-6). +- **Return to the model on every change.** When a requirement shifts, the model + is updated first and the code follows — never the reverse. A change that lands + in code but not in the model has desynchronised the shared picture, which is a + defect (§3-7). +- Use the shared [Domain Terminology](/domain-terminology) (§3-1) in the model, + the code, and conversation with the client — the same word for the same thing, + everywhere. + +This is not a hand-off to a separate "analyst" role. In a small team the person +who will build it is usually the person who models it; that is deliberate, +because modeling is how you understand what you are about to build. The reviewer +treats the model as part of the reviewable surface, exactly as the [Quality +Gate](/quality-gate)'s AI code review treats the code. + +## 3. Practices + +### 3-1. Establish a ubiquitous language first + +Before drawing anything, agree on the words. **Domain-Driven Design** (Eric +Evans) calls this the **ubiquitous language**: a single vocabulary, drawn from +the business domain, used identically by domain experts, developers, code, and +diagrams. + +- The team MUST maintain a short glossary of domain terms as part of the model — + this is the project's [Domain Terminology](/domain-terminology). +- Code, diagrams, and client conversation MUST use those exact terms. If the + client says "consignment", the class is not `Order`. +- When a term is ambiguous, that ambiguity is a finding: it usually means two + concepts are hiding under one word, or one concept under two words. + +Every diagram that follows is only as clear as the words on it. Nail the language +and the models label themselves; skip it and every diagram needs a translator. + +### 3-2. Discover the domain with Event Storming + +To *find* the model — not just document one you already assume — OSBR's default +discovery technique is **Event Storming** (Alberto Brandolini). It is a fast, +low-tech workshop that maps a business process as a timeline of **domain events** +(things that happened, past tense: "Order Placed", "Payment Captured"), then +layers on the commands, actors, systems, and — critically — the **pain points and +hotspots** where the process breaks down. + +- For any non-trivial new feature or project, the team SHOULD run an Event + Storming pass with the client or domain expert before modeling structure. This + builds directly on the business understanding gathered during [Market + Research](/market-research). +- The output MUST be captured in the repo as a model (a Mermaid timeline or flow + is fine — see §3-6), not left on a physical wall or in a photo. +- Hotspots discovered in the storm map directly onto the **demands** and **pain + points** the model must record (§4). + +Event Storming is where stakeholders, events, and pain points enter the model. +The later techniques give that raw discovery its structure. + +### 3-3. Draw boundaries: bounded contexts and context maps + +DDD's second big idea is that a large domain is not one model but several. A +**bounded context** is a boundary within which the ubiquitous language is +consistent; a **context map** shows how those contexts relate and integrate. + +- Where the domain is large enough that one word means different things in + different areas (a `Customer` in billing vs. in support), the team MUST split + it into bounded contexts rather than force one bloated model. +- The relationships between contexts SHOULD be drawn as a context map, using the + DDD-crew Context Mapping patterns (Customer/Supplier, Anticorruption Layer, + Shared Kernel, and so on). +- Within a context, group entities into **aggregates** (a consistency boundary + with one root) so invariants have a clear owner — this is what later drives + transaction and table design in the [Architecture + Standards](/architecture-standards). + +### 3-4. Model the systems and structure with C4 + +For the *systems* dimension — what software exists and how it fits together — +OSBR uses the **C4 model** (Simon Brown). C4 gives a small, fixed set of zoom +levels so a diagram's altitude is never ambiguous: + +| Level | Answers | Use when | +| ----- | ------- | -------- | +| 1. System Context | Who uses it, what other systems it talks to | Almost always — the one-picture overview | +| 2. Container | The deployable/runnable pieces (apps, DBs, workers) and their tech | Planning the build; feeds the [Architecture Standards](/architecture-standards) | +| 3. Component | Major building blocks inside a container | Only where a container is complex enough to warrant it | +| 4. Code | Classes / schema | Rarely by hand — generate it if you need it | + +- Every project MUST have at least a **System Context** and a **Container** + diagram in the repo. +- Do not draw Component or Code diagrams speculatively — draw them only where the + structure is genuinely hard to hold in your head. Levels 1–2 earn their keep; + 3–4 usually do not. + +### 3-5. Model processes with BPMN, data with UML/ER + +Use the notation that fits the dimension, rather than forcing everything into one +diagram type: + +- **Processes and workflows** — where the requirement is a business process with + steps, decisions, and hand-offs (approvals, onboarding, order fulfilment), + model it with **BPMN 2.0** (an OMG standard). Mermaid's flowchart and sequence + diagrams cover the common cases as diagram-as-code. +- **Data and relationships** — model entities and their relationships with an + **ER diagram**, and use **UML** class / sequence / state diagrams (OMG) where + object structure or lifecycle needs to be explicit. These are what later become + the relational schema in the [Architecture + Standards](/architecture-standards) — the ER model is drawn *before* the + `CREATE TABLE`, not derived after it. + +Reach for the lightest notation that makes the requirement unambiguous. A +three-box Mermaid flowchart the client understands beats a formally perfect BPMN +diagram nobody reads. Formality serves clarity; when it stops doing so, stop. + +### 3-6. Keep the model in the repo as diagram-as-code + +This is the practice that makes all the others stick: **models MUST live in the +repository as text, not as images or in an external drawing tool.** + +- Use **Mermaid** as the default diagram-as-code format. It renders natively in + GitHub, in our handbook, and in most editors, so a diagram is reviewable in a + pull request like any other change. +- Prefer text-based models (Mermaid, PlantUML, or C4-as-code) over binary exports + from a GUI tool. A `.png` from a drawing app cannot be diffed, reviewed + line-by-line, or updated by an AI agent; a Mermaid block can. +- Because the model is text in the repo, **model changes go through the same + review as code** — and a pull request that changes behaviour SHOULD update the + model in the same PR. + +A diagram-as-code model is diffable, mergeable, greppable, and editable by both +humans and AI agents. That is the whole point: the shared picture stays live and +in-sync because it is version-controlled next to the thing it describes, not +rotting in a wiki or a kickoff slide. + +### 3-7. Return to the model on every change + +The model is not a phase you finish and leave behind — it is the **living +reference you return to**. The model leads, the implementation follows. + +- On any requirement change, update the model **first**, review it, then change + the code to match. +- Treat model/code drift as a bug. If the code does something the model does not + show, one of them is wrong — reconcile before shipping. +- Periodically — at minimum, at the start of any significant new work in an area + — re-read the model to confirm it still matches reality, and correct it if the + domain has moved. + +## 4. What the model must capture + +Regardless of notation, the model for a project MUST make all of the following +explicit and locatable in the repo. These seven elements are the concerns the +analysis exists to surface: + +| Element | What it records | Typical notation | +| ------- | --------------- | ---------------- | +| **Stakeholders** | Who uses or is affected by the system; their roles and goals | C4 actors, Event Storming actors | +| **Events** | The things that happen in the domain, over time | Event Storming timeline, sequence / state diagrams | +| **Systems** | The software and external systems involved, and how they connect | C4 System Context / Container | +| **Data** | Entities, their attributes, and their relationships | ER diagram, UML class diagram | +| **Demands** | What stakeholders actually need the system to do (functional intent) | Event Storming commands | +| **Pain points** | Where the current situation or process hurts today | Event Storming hotspots | +| **Solutions** | How the proposed design relieves each pain point and meets each demand | Annotated on the models above | + +A model that shows systems and data but never names the pain points it exists to +fix is incomplete — it has documented a solution without stating the problem. +**Be Strong** here means holding the line: no model is done until all seven are +present and point at each other. + +## References + +Named, published practice this policy is grounded in — chosen because each is a +documented industry standard an SME can adopt directly. + +**Domain-Driven Design** + +- Eric Evans — *Domain-Driven Design: Tackling Complexity in the Heart of Software* — +- DDD-crew — Context Mapping patterns — + +**Collaborative discovery** + +- Alberto Brandolini — Event Storming — · *Introducing EventStorming* — + +**Architecture / systems modeling** + +- Simon Brown — The C4 model for visualising software architecture — + +**Standard notations** + +- OMG — Business Process Model and Notation (BPMN) 2.0 — +- OMG — Unified Modeling Language (UML) — + +**Diagram-as-code** + +- Mermaid — text-based diagramming — + +**Related OSBR standards** + +- [Development Guide](/development-guide) — the Planning & Shaping stage this standard serves. +- [Quality Gate](/quality-gate) — the AI code review the model is part of. +- [Architecture Standards](/architecture-standards) — where the C4 Container, aggregate, and ER models become concrete. +- [Domain Terminology](/domain-terminology) — the ubiquitous language the model, code, and client share. +- [Market Research](/market-research) — the business understanding Event Storming builds on. diff --git a/doc/security-policy.md b/doc/security-policy.md index 9c1330f..94b6d14 100644 --- a/doc/security-policy.md +++ b/doc/security-policy.md @@ -11,9 +11,9 @@ Security Measures Required of Developers Developers and collaborators must adhere to the following: -### 1. Mandatory Code Review by a Security-Knowledgeable Reviewer +### 1. Mandatory Code Review, with Security as a First-Class Concern -Pull requests must be reviewed and approved by someone with sufficient knowledge of secure development practices. The reviewer should be capable of identifying common security risks and ensuring that the code meets appropriate security standards. +Every change passes the [Code Review](/code-review) standard before it merges: an AI review, run when the change is declared `Impl Review`, is the default gate, and security — OWASP/ASVS-class risks — is one of the three concerns it guards, alongside correctness and readability. A human stays in the loop, interprets the findings, and remains accountable for the merge; human review is welcome but not the enforced default. The reviewing human must have enough knowledge of secure development practices to judge the security findings the gate raises. ### 2. Understanding of Web Security Fundamentals diff --git a/doc/self-explanatory-ui.md b/doc/self-explanatory-ui.md new file mode 100644 index 0000000..62218d6 --- /dev/null +++ b/doc/self-explanatory-ui.md @@ -0,0 +1,238 @@ +# Self-Explanatory UI + +This policy defines what OSBR means by a finished screen: **every screen MUST +be operable without documentation.** If a user needs a manual, a walkthrough +video, or a tooltip tour to complete an ordinary task, the screen is not +finished — the design is doing less than its job and pushing the shortfall onto +the user. This is the standard the [Quality Gate](/quality-gate) holds +interface work to, and it sits alongside the [Design +Guidelines](/design-guidelines) and [Interaction Design](/interaction-design) +policies as the screen-level expression of both. + +We do not invent our own usability theory. We stand on named, published +practice — Don Norman's *The Design of Everyday Things*, the Nielsen Norman +Group's usability heuristics and writing guidelines, and the plain-language +movement — and apply it at the altitude of a single screen. Deviations are +allowed, but — as everywhere in the handbook — they must be deliberate and +justified in the project's design notes. + +A screen is where OSBR's values meet the person using the product. **Be Nice**: +writing every label and message in the user's language, not ours, is an act of +respect for the person on the other side of the glass. **Be Kind**: the screen +explains itself so the user never has to feel stupid — it never leaves them +staring at a dead end, an unlabelled icon, or an error that only says something +broke. **Be Strong**: it means refusing to ship the false-finished screen and +paper the gap over with a help doc. + +## How to read this policy + +* **Requirement levels** follow RFC 2119. **MUST** / **MUST NOT** are absolute. + **SHOULD** / **SHOULD NOT** state a strong default overridable only with a + documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). We adopt the + *criteria* of these sources and right-size them for an SME — we do not adopt + the research apparatus behind them. + +[[TOC]] + +## 1. Goal + +The goal is a screen that **explains itself in the act of being used.** A +first-time user, given the screen and nothing else, can tell what it is, what +they can do, what is happening, and what to do next — including when things go +wrong. + +A screen carries the whole conversation with the user. Whatever the screen +fails to say, the user must guess, ask, or look up — and every guess is a chance +to get it wrong. So the design's job is to say it: on the control, in the user's +own words, at the moment and place it is needed. + +## 2. Responsibility + +Whoever builds a screen owns its self-evidence. This is not a hand-off to a +separate "UX" role who cleans up copy at the end — the person building the +screen is the person who makes it self-explanatory, because the two are the same +act. Concretely, that person or pair MUST: + +- Design **all four states** of every screen — loading, empty, error, success — + not just the happy path (§3-2). +- Write every label, action, and message in the **user's vocabulary** (§3-1), + reusing the terms the [Design Guidelines](/design-guidelines) settle on for + each concept. +- Make every error message **actionable and adjacent to its cause** (§3-3). +- Treat a needed **onboarding tour or tooltip coach-mark as a design finding**, + not a feature — investigate the underlying screen before adding the overlay + (§3-4). + +## 3. Practices + +### 3-1. Speak the user's language, and let controls signal their use + +Two named ideas from Norman govern this. First, **match between system and the +real world** — the first of the Nielsen Norman Group's [10 Usability +Heuristics](https://www.nngroup.com/articles/ten-usability-heuristics/): "speak +the users' language, with words, phrases and concepts familiar to the user, +rather than internal jargon." Second, **affordances and signifiers** (Norman, +*The Design of Everyday Things*): an affordance is what an element lets you do; a +**signifier** is the perceivable cue that tells you it. A button that does not +look pressable has hidden its affordance. + +- Labels, buttons, empty states, and messages MUST use the words the user uses. + If the domain term is "consignment", the button is not "Submit Record" — the + vocabulary the [Design Guidelines](/design-guidelines) fix for the product + reaches the surface unchanged, never swapped for an internal name. +- Interactive elements MUST carry a signifier that they are interactive — a + control must look like a control. Do not rely on the user hovering, guessing, + or remembering that a thing is clickable. The same cue also serves users on + assistive technology (see [Accessibility](/accessibility)): a control the eye + can recognise is a control the screen reader can name. +- **Mapping** MUST be natural: the arrangement of controls should mirror the + thing they affect (Norman's example — the layout of stove-burner knobs + matching the burners). Order, group, and place controls so their relationship + to their effect is visible, not memorised. +- Follow **plain-language** practice + ([plainlanguage.gov guidelines](https://www.plainlanguage.gov/guidelines/)): + short sentences, common words, active voice, "you" for the reader. A screen + written plainly needs no glossary. + +The test for a control is not "does it look nice" but "does an unfamiliar user +know it is a control, and what it will do, before they touch it." If they must +click to find out, the signifier is missing. + +### 3-2. Design all four states, always + +Most screens are designed for the moment they are full of data and everything +works. Users spend a large share of their time in the other three states — and +those are exactly where an undesigned screen abandons them. Every screen that +fetches, shows, or accepts data MUST design all four: + +| State | What the user must be told | Grounded in | +| ----- | -------------------------- | ----------- | +| **Loading** | Something is happening, and roughly how far along | *Visibility of system status* (NN/g heuristic 1) | +| **Empty** | Why it is empty, and the one action to fill it | [Empty-state design](https://www.nngroup.com/articles/empty-state-interface-design/) | +| **Error** | What happened and what to do about it (§3-3) | *Help users recognise, diagnose, recover from errors* (heuristic 9) | +| **Success** | That it worked, and what changed | *Visibility of system status* (heuristic 1) | + +- **Loading** MUST show system status. No frozen screen, no ambiguous spinner + that could equally mean "working" or "hung" — say what is loading. (NN/g, + [Visibility of System Status](https://www.nngroup.com/articles/visibility-system-status/).) +- **Empty** is a first impression, not an error. A good empty state (NN/g, + [empty-state design](https://www.nngroup.com/articles/empty-state-interface-design/)) + explains what belongs here and offers the single next action to create the + first item — never a blank void that reads as "broken". +- **Error** MUST follow §3-3. +- **Success** MUST confirm the outcome. A save that gives no feedback forces the + user to re-check whether it worked — a small tax paid on every action. + +A screen shipped with only its success-with-data state designed is **not +finished.** The missing three states will be filled by browser defaults, blank +space, and raw stack traces — i.e. by no design at all. + +### 3-3. Errors: what happened + what to do, next to the cause + +An error message is the screen speaking at the user's worst moment. Two of the +NN/g heuristics bear directly: **error prevention** (heuristic 5 — the best +message is the one designed out) and **help users recognise, diagnose, and +recover from errors** (heuristic 9 — messages "expressed in plain language, +precisely indicate the problem, and constructively suggest a solution"). The +NN/g [error-message guidelines](https://www.nngroup.com/articles/error-message-guidelines/) +make this concrete. + +- Every error message MUST state **both** halves: **what happened** and **what + to do next.** "Something went wrong" states neither. "Card declined — check + the number and expiry, or try another card" states both. +- Messages MUST be **adjacent to their cause.** A field error belongs at that + field, not in a banner at the top of the page; a form-level error belongs + where the user's eye is when it occurs. Distance between the error and its + source makes the user hunt for what to fix. +- **Prevent the error first** (heuristic 5). Constrain inputs, disable + impossible actions, confirm destructive ones, use the right input type. A + prevented error needs no message. +- Error copy MUST be **plain and human** — no error codes as the primary + message, no blame ("invalid input"), no jargon. Name the problem in the user's + terms and point at the fix. (Plain-language, as §3-1.) +- Never blame the user, and never hide the failure. Surface it so they can + recover — a silent failure is worse than a blunt one. + +An error path with a vague message is an unfinished feature, not a finished +feature with rough edges. If the recovery instruction isn't written, the user +cannot recover — the code "handled" the error and the person did not. + +### 3-4. A tour over a finished UI is a design failure + +Onboarding tooltip tours, coach-marks, and "here's how this works" overlays are +the standard industry patch for a screen that does not explain itself. OSBR +reads them as a **signal**, not a solution. + +- A screen that needs a guided tour to be operable has **failed §1** — it is not + self-explanatory, and the tour is a manual wearing a costume. The default + response is to fix the screen (labels, signifiers, states, mapping) so the + tour becomes unnecessary. +- Reaching for a tooltip tour SHOULD trigger the question: *which of §3-1 + through §3-3 did this screen skip?* Usually the answer is a missing signifier, + jargon in a label, or an undesigned empty state — fix that instead. +- This does not ban all in-context help. A genuinely novel interaction, or + progressive disclosure of an advanced feature, MAY warrant a one-time hint. + The line: a hint that *teaches an optional power-feature* is fine; a tour that + is *required to do the ordinary task* is a defect. If removing the overlay + makes the core task unusable, the core screen is broken. + +Every tour is evidence that the design lost an argument with itself and +outsourced the loss to the user's patience. Users skip tours, forget them, and +arrive by deep link having never seen them. The screen must stand alone — the +tour cannot be relied on, so the screen cannot depend on it. + +## 4. What "no manual" requires, per screen + +Before a screen is called finished, it MUST satisfy all of the following. This +is the checklist the practices above add up to, and it is the surface the +[Quality Gate](/quality-gate) reviews interface work against: + +| Requirement | The question it answers | +| ----------- | ----------------------- | +| **Vocabulary** | Are all labels, actions, and messages in the *user's* words? (§3-1) | +| **Signifiers** | Does every control look like what it is and does? (§3-1) | +| **Mapping** | Do control layout and grouping mirror what they affect? (§3-1) | +| **Loading state** | Does the user know something is happening? (§3-2) | +| **Empty state** | Does an empty screen explain itself and offer the first action? (§3-2) | +| **Error state** | Does every error say what happened *and* what to do, next to its cause? (§3-3) | +| **Success state** | Is the outcome confirmed? (§3-2) | +| **No required tour** | Is the ordinary task doable with no overlay, tooltip, or manual? (§3-4) | + +A screen that shows data correctly but fails any row above is not finished — it +has documented the happy path and left the rest for the user to discover. + +## References + +Named, published practice this policy is grounded in — each a documented source +an SME can adopt directly. + +**Foundations of usability** + +- Don Norman — *The Design of Everyday Things* (revised ed., 2013) — + affordances, signifiers, mapping, and the paired gulfs of execution and + evaluation — +- Nielsen Norman Group — 10 Usability Heuristics for User Interface Design — + + +**System status and states** + +- Nielsen Norman Group — Visibility of System Status — +- Nielsen Norman Group — Empty States: More Than Just Blank Screens — + +**Error messages** + +- Nielsen Norman Group — Error-Message Guidelines — +- Nielsen Norman Group — How to Report Errors in Forms — + +**Plain language** + +- plainlanguage.gov — Federal Plain Language Guidelines — + +**Related OSBR standards** + +- [Design Guidelines](/design-guidelines) — the product vocabulary and design principles §3-1 spends on the screen. +- [Interaction Design](/interaction-design) — the interaction-level companion to this screen-level standard. +- [Accessibility](/accessibility) — signifiers and states as they reach assistive technology. +- [Quality Gate](/quality-gate) — the standard this checklist is reviewed against. diff --git a/doc/sheq-policy.md b/doc/sheq-policy.md index 27315de..11a64a3 100644 --- a/doc/sheq-policy.md +++ b/doc/sheq-policy.md @@ -36,6 +36,14 @@ Good work speaks for itself. High quality results are achieved when: - Innovating without compromising reliability. - Regularly evaluating and improving our processes +In engineering, this commitment is held by the [Quality Gate](/quality-gate): +the three checks — reliable, secure, sustainable — every change clears before +it merges. + +#### Keeping honest to our own policies + +A policy written once and never re-checked quietly stops being true: code changes, deadlines get met by cutting the corner a policy forbids, and the gap between what we say and what we ship widens until it is structural and expensive. We surface that drift while it is still cheap to fix. On a fixed cadence — at least quarterly, and again after any major architectural change — we check reality against every active policy, record a per-policy verdict with evidence, and log each gap as a visible, owned item in the technical-debt register rather than a mental note. A deliberate, time-boxed exception is honest; a silent violation is not. Where a check can be mechanised it is, and run first, so human judgement is spent only where it is needed. A gap still open after two cycles is escalated for an explicit decision — fund it, accept the risk, or change the policy — and a policy the audit keeps refuting is the thing we fix, not the engineers who keep tripping it. No policy is exempt, and no finding is a personal performance rating; a non-conformance is a signal about the system, never about a person. We keep ourselves honest this way as an act of our values: **Be Nice** — a gap caught early spares a colleague a bad night; **Be Kind** — an honest "not yet, here is the plan" beats a policy everyone quietly breaks; and **Be Strong** — we would rather find our own gaps than have a client find them. + ## 3. The Team's Responsibility OSBR team members each play a crucial role in upholding our SHEQ standards. The team's responsibilities shall be represented in the table below: diff --git a/doc/supply-chain-risk.md b/doc/supply-chain-risk.md new file mode 100644 index 0000000..54f6ce1 --- /dev/null +++ b/doc/supply-chain-risk.md @@ -0,0 +1,381 @@ +# Supply Chain & Risk + +This is the standard the [Quality Gate](/quality-gate)'s **Security** lens holds +work to for two questions that turn out to be the same question: *what third-party +code do we ship, and how do we decide how much risk in it we are willing to live +with?* It sits beside the [Security Policy](/security-policy) and +[Application Security](/application-security) standard — those cover the controls +we build and the code we write; this covers the code we **import**, and the +register that governs the risks we knowingly accept. Most breaches don't start in +the code we wrote; they arrive through the code we depended on, or through a risk +nobody wrote down. Deviations are allowed, but — as everywhere in the handbook — +they must be deliberate and justified in the project's design notes. + +Owning our supply chain is where OSBR's values become operational. **Be Nice**: +the safest dependency is the one we never added, so we don't drag in a tree to +save a few lines a teammate then has to reason about. **Be Kind**: a known-vulnerable +package or an unwritten risk is a hazard we would be handing to our clients and to +whoever maintains this next, so we keep the surface known, pinned, and scanned. +**Be Strong**: when a dependency has a flaw and no patch yet, we say so plainly, +put a name against the residual risk, and decide in the open rather than shipping +past it in silence. + +## How to read this policy + +* **Requirement levels** follow RFC 2119. **MUST** / **MUST NOT** are absolute. + **SHOULD** / **SHOULD NOT** state a strong default overridable only with a + documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice or framework, it is + named inline and cited under [References](#references). We adopt the *criteria* of + large-scale frameworks (OWASP, NIST, SLSA, ISO) and right-size them for an SME — + we do not adopt the headcount or committee structure behind their reference + programmes. + +[[TOC]] + +## 1. Goal + +Ship software whose every third-party component is **known, pinned, scanned, +current, and attestable**, and carry every risk we accept **named, assessed, and +signed for**. Concretely, when a CVE lands or a package is compromised, we can +answer three questions in minutes, not days: + +1. **Do we use it?** — we have an SBOM. +2. **Where, and at exactly which version?** — we have committed lockfiles. +3. **Is what we built actually what we shipped?** — we have provenance. + +And when a flaw has no fix yet, we can answer a fourth: **who decided to live with +it, and until when?** — we have a risk register (§4). An unknown or unpatched +dependency is treated as a live risk to the client, not a backlog item. + +## 2. Responsibility + +- **Every developer** keeps lockfiles committed, does not add a dependency to dodge + a few lines of code, does not merge past a failing security gate without a + recorded exception, and raises a new or changed risk the moment they see one (a + new dependency, a new data flow, a near-miss). +- **The reviewer** treats an automated dependency-update PR like any other change — + reviewed before merge, never rubber-stamped — and treats the supply-chain surface + as part of the reviewable code, an extension of the AI code review the + [Quality Gate](/quality-gate) requires. +- **The project lead** owns the remediation clock (§3-7), the exception log, the + project's single risk register (§4), and its review cadence. Residual-risk + acceptances are theirs to ensure are signed. +- **The risk owner** (named per register entry) is accountable for one risk: that + its countermeasures hold, its residual risk stays acceptable, and its + re-evaluation date is honoured. +- **The client** is informed of accepted residual risk that materially affects their + data or service and — where the risk is theirs to carry — co-signs the acceptance. + +Risk is not a separate team's job; it is a shared reflex. These duties build on the +frameworks OSBR already aligns to: the **OWASP Top 10**, specifically +[A06:2021 Vulnerable & Outdated Components](https://owasp.org/Top10/A06_2021-Vulnerable_and_Outdated_Components/); +the [NIST SSDF (SP 800-218)](https://csrc.nist.gov/pubs/sp/800/218/final) practices +PW.4 (reuse well-secured components) and PS.3 (protect each release with +provenance); and the ISMS risk-management cycle of +[ISO/IEC 27001:2022](https://www.iso.org/standard/27001) (clauses 6.1.2, 6.1.3, +8.2–8.3). + +## 3. Supply-chain security + +### 3-1. Lockfiles and exact versions + +- Projects **MUST** commit a lockfile that resolves every direct and transitive + dependency to an **exact version** (`package-lock.json` / `pnpm-lock.yaml`, + `go.sum`, `poetry.lock` / `uv.lock`, `Cargo.lock`, etc.). +- CI **MUST** install from that lockfile with a frozen/CI-only install (`npm ci`, + `pnpm install --frozen-lockfile`, `poetry install --no-update`) so a build can + never silently float to a newer version. +- Version ranges (`^`, `~`, `latest`) in a manifest are fine as *intent*; the + lockfile is the *truth*. A PR that changes resolved versions **MUST** show the + lockfile diff. A lockfile you don't install from is theatre — the frozen install + is the part that actually protects you. + +### 3-2. Software Composition Analysis (SCA) in CI + +Every pipeline **MUST** scan dependencies for known vulnerabilities on each pull +request and **MUST** fail the build on findings above the project's agreed +threshold (default: High and Critical). See the [CI/CD Pipeline](/ci-cd-pipeline) +standard for where this gate sits in the flow. + +- **First pass:** the ecosystem's native auditor — `npm audit` / `pnpm audit`, + `pip-audit`, `govulncheck`, `cargo audit` — fast and fluent in its own + ecosystem's quirks. +- **Backstop:** a cross-ecosystem scanner on an open advisory feed — + [OWASP Dependency-Check](https://owasp.org/www-project-dependency-check/) or an + [OSV](https://osv.dev/)-based scanner + ([`osv-scanner`](https://google.github.io/osv-scanner/)). [OSV](https://osv.dev/) + and the [GitHub Advisory Database](https://github.com/advisories) are the + canonical, machine-readable sources of truth; prefer them over ad-hoc blog + reports. +- Scanners **MUST** run on the **committed lockfile** (§3-1) so results are + reproducible and match what actually ships. + +The two layers earn their keep together: native auditors are fast and +ecosystem-aware; a cross-ecosystem OSV/Dependency-Check pass catches what a single +package manager misses and gives one consistent report across a polyglot repo. + +### 3-3. Secrets scanning + +A leaked credential is a supply-chain failure in the other direction — our secret, +out the door. Scanning **MUST** happen at two points: + +- **Pre-commit** — a local hook ([gitleaks](https://github.com/gitleaks/gitleaks) + or [TruffleHog](https://github.com/trufflesecurity/trufflehog)) so a secret is + caught *before* it reaches history. +- **Pipeline** — the same scan in CI as a backstop for anyone who bypassed the + hook, plus repository push protection / secret scanning. + +If a credential is committed it **MUST** be revoked immediately (per the +[Security Policy](/security-policy)), not merely deleted from the branch — history +and forks retain it. + +### 3-4. Version pinning for images and tools + +- Container base images **MUST** be pinned by **digest** (`image@sha256:…`), not by + a moving tag like `:latest` or even `:3.19`. Tags are reassignable; a digest is + the exact bytes. +- CI actions and tools that run with repository credentials (e.g. third-party CI + actions) **SHOULD** be pinned to a full commit SHA, not a branch or floating tag. +- Pins are updated deliberately through the same automated-PR flow as any other + dependency (§3-5). + +### 3-5. Dependency updates — automated, but merged by a human + +Stale dependencies are how A06 happens. Updates are automated; merging stays human. + +- Every repo **MUST** enable an automated update bot + ([Dependabot](https://docs.github.com/en/code-security/dependabot) or + [Renovate](https://docs.renovatebot.com/)) configured to open PRs for dependency + and base-image bumps, grouped where sensible to cut noise. +- Update PRs **MUST** pass the full CI gate (tests + SCA + secrets) and **MUST** be + reviewed by a person before merge. **Auto-merge without review is prohibited** — a + compromised update is exactly the case where a robot rubber-stamping itself hurts + most. +- Security bumps follow the remediation clock in §3-7; routine version bumps are + handled on a regular cadence (default: reviewed weekly). + +### 3-6. SBOM, provenance, and integrity + +Every release **MUST** be able to say what's in it and prove it built it. + +- **SBOM** — generate a Software Bill of Materials per release in a standard format + ([CycloneDX](https://cyclonedx.org/) or [SPDX](https://spdx.dev/)) and store it as + a release artifact, so §1's "do we use it?" is a lookup, not an investigation. +- **Provenance** — target [SLSA](https://slsa.dev/) build levels: produce signed + build provenance so the link from source commit → build → published artifact is + verifiable and tamper-evident. +- **Integrity** — sign release artifacts and container images with + [Sigstore / cosign](https://docs.sigstore.dev/) and verify signatures at deploy + time. An unsigned or signature-mismatched artifact **MUST NOT** be deployed. + +SBOM answers *what*, provenance answers *how it was built*, signing answers *is this +the real one*. Each is cheap to add in CI once and pays off the first time an +advisory names a package we might use. + +### 3-7. Remediation time-frames by severity + +When a known vulnerability affects a dependency we ship, the clock starts at +disclosure (advisory published, or bot PR opened). Severity uses the advisory's +CVSS rating. + +| Severity | Remediate within | +|----------|------------------| +| Critical | 3 business days (patch, or documented mitigation + isolation) | +| High | 7 business days | +| Medium | 30 days | +| Low | Next regular update cycle | + +- If a fix is unavailable, the project lead **MUST** record a mitigation (config + change, WAF rule, feature disable, network isolation) and a re-check date — an + unfixable vuln is *managed*, never ignored. +- A gate exception (merging past a failing SCA finding, or holding a release with a + known unpatched flaw) **MUST** be logged with reason, owner, and expiry, and + **MUST** be carried as a named entry in the risk register (§4). No silent + bypasses: choosing to live with an unpatched dependency is a risk-acceptance + decision, and §4 is where such decisions are made and signed. + +### 3-8. Minimize the dependency surface + +The safest dependency is the one you didn't add — a security posture and OSBR's +**vendor-neutrality** stance at once, since fewer external lock-ins mean more of the +system we can actually reason about. + +- Before adding a dependency, climb down: does the standard library or an + already-installed package do it? A few lines we own beat a transitive tree we + don't. +- Prefer well-maintained, widely-used packages with a healthy release history over + a thin wrapper that saves a handful of lines but drags in a deep tree. +- Periodically prune: dependencies no longer used **SHOULD** be removed. Every + package on the manifest is attack surface, an update burden, and one more line in + the SBOM. A system built on a small, well-understood base is one we can move, + audit, and hand to a client without a proprietary anchor. + +## 4. The risk register that governs accepted risk + +Sections 3-1 to 3-8 keep the surface small and current, but no control reduces risk +to zero, and some risks — a flaw with no patch, a control we chose not to build — +we knowingly carry. Those decisions live in **one risk register per project**: the +single place OSBR decides *how much security is enough* for a given project, by +assessed risk, recorded and kept current. This is the OSBR-scale application of the +ISMS risk cycle in [ISO/IEC 27001:2022](https://www.iso.org/standard/27001), with +the how-to drawn from [ISO/IEC 27005:2022](https://www.iso.org/standard/80585.html) +and [NIST SP 800-30 Rev. 1](https://csrc.nist.gov/pubs/sp/800/30/r1/final). + +### 4-1. Start from an information-asset inventory + +You cannot assess risk to assets you have not listed. Before assessing risk, a +project **MUST** hold a current inventory of the information assets it touches — the +protected assets enumerated in the [Security Policy](/security-policy) (source code +and accounts; user data and uploads; access logs and metrics; communication +history), made concrete for *this* project. + +- For each asset, record what it is, where it lives (which service, which region — + mind data residency), who can reach it, and its sensitivity. +- The inventory seeds the register: risks are assessed **against named assets**, not + in the abstract. This is the asset-based identification path of + [ISO/IEC 27005:2022](https://www.iso.org/standard/80585.html); the event-based + path (start from a threat scenario, trace it to assets) is equally valid and the + two are complementary — use whichever surfaces the risk. + +### 4-2. Keep one register, and keep it current + +There **MUST** be exactly **one** risk register per project, and it **MUST** be the +single source of truth for security-risk decisions. A register that is out of date +is worse than none, because it manufactures false confidence. It **SHOULD** live +where the project already works (a repository file or the tracker), +version-controlled so its history is auditable. Each entry **MUST** carry all of: + +| Field | What it captures | +| --- | --- | +| **Description** | The risk as a scenario: threat source → what it does → which asset → the harm. Vague entries ("security", "AWS") are not risks. | +| **Likelihood** | How probable, on a scale the project has defined (§4-3). | +| **Impact** | How bad if it happens, on the same defined scale. | +| **Countermeasures** | The controls in place or planned to reduce likelihood and/or impact. | +| **Residual risk (named and accepted)** | The risk that *remains* after countermeasures — stated explicitly, formally accepted by a named person (§4-4). | +| **Owner** | The single named risk owner (§2). | +| **Re-evaluation date** | When this entry is next reviewed, even if nothing triggers it sooner (§4-6). | + +An unpatched dependency held past the §3-7 clock is exactly such an entry: the +description names the CVE and the asset it exposes, the countermeasures record the +mitigation, and the residual risk is signed with an expiry. + +### 4-3. Assess by likelihood × impact; make control weight proportional + +Score each risk by **likelihood × impact**, following the method of +[NIST SP 800-30 Rev. 1](https://csrc.nist.gov/pubs/sp/800/30/r1/final) (prepare → +conduct → maintain). A qualitative scale (Low / Medium / High on each axis) is the +sensible default for an SME project — fast, good enough to rank, readable by +everyone. + +- **Control weight MUST be proportional to assessed risk.** A high-likelihood, + high-impact risk earns strong, possibly redundant controls; a low-likelihood, + low-impact risk earns a light touch or explicit acceptance. Spending a High + control budget on a Low risk is gold-plating — it steals attention from where the + risk really is. +- **Quantify when the decision turns on the number.** When "is this worth it?" + hinges on how big the loss really is — a costly control, a client escalation, an + insurance or spend trade-off — reach for + [FAIR](https://www.fairinstitute.org/) to express likelihood and impact as + loss-event frequency and loss magnitude in money rather than a colour. Use + quantification where it changes the decision; do not impose it on every row. +- Prioritise treatment by risk level, not by whichever risk is loudest or newest. + +### 4-4. Name and accept residual risk — with a sign-off + +No control reduces risk to zero. What remains is **residual risk**, and OSBR's rule +is that it is never left implicit. + +- Every entry **MUST** state its residual risk in plain terms: *after these + countermeasures, this much could still go wrong.* +- Residual risk **MUST** be **formally accepted by a named person with the authority + to carry it** (the risk owner, and where it is the client's risk, the client). + This is the risk-acceptance step of + [ISO/IEC 27001:2022](https://www.iso.org/standard/27001) clause 8.3. +- Accepting a risk is a legitimate, **Be Strong** decision — not a failure. What is + not legitimate is carrying a risk nobody has looked at and nobody has agreed to. + An unsigned residual risk is an open item, not an accepted one. + +### 4-5. Treat, then record what you chose + +For each risk, choose a treatment and record it: **reduce** (add countermeasures), +**avoid** (don't do the risky thing), **transfer** (a managed service or insurance +carries it), or **accept** (per §4-4). This is the treatment vocabulary of +[ISO/IEC 27005:2022](https://www.iso.org/standard/80585.html). The register records +the choice and its rationale so a future reader — or an auditor — can see *why* the +control weight is what it is. + +### 4-6. Re-evaluation triggers + +The register is a living document. A project **MUST** re-evaluate the affected +entries — and add new ones — whenever any of the following happens, without waiting +for the scheduled date: + +- **A new vendor or dependency** is introduced (SaaS, third-party API, library, or + sub-processor) — it brings its own attack surface and data flows. This is the + same event that §3-8 asks you to weigh before adding. +- **Authentication or authorization changes** — a new login path, a new role, a + change to who can reach what, a new set of credentials. +- **Data scope expands** — the project starts collecting, storing, or moving a new + class of data (especially personal data), or moves existing data to a new place + or region. +- **An incident or near-miss occurs** — any incident, or a close call, is direct + evidence that a likelihood or impact estimate was wrong; feed it straight back + into the register. + +Absent a trigger, every entry is still re-evaluated on its **re-evaluation date** +(§4-2). Risk assessment is a loop, not a one-time gate — the "maintain the +assessment" step of NIST SP 800-30 and the continuous monitoring of the ISO/IEC +27005 cycle. + +## References + +**Supply-chain standards & frameworks** + +- OWASP Top 10 — A06:2021 Vulnerable & Outdated Components — +- OWASP Dependency-Check — +- NIST Secure Software Development Framework (SSDF, SP 800-218) — +- SLSA — Supply-chain Levels for Software Artifacts — + +**Vulnerability data** + +- OSV — Open Source Vulnerabilities — +- OSV-Scanner — +- GitHub Advisory Database — + +**Auditing & updates** + +- npm audit — +- pnpm audit — +- Dependabot — +- Renovate — + +**Secrets scanning** + +- gitleaks — +- TruffleHog — + +**SBOM, provenance & signing** + +- CycloneDX — +- SPDX — +- Sigstore / cosign — + +**ISMS risk management (the frame)** + +- ISO/IEC 27001:2022 — Information security management systems — Requirements (risk assessment and treatment: clauses 6.1.2, 6.1.3, 8.2, 8.3) — +- ISO/IEC 27005:2022 — Guidance on managing information security risks — + +**Risk assessment method, register & quantification** + +- NIST SP 800-30 Rev. 1 — Guide for Conducting Risk Assessments — +- NIST SP 800-37 Rev. 2 — Risk Management Framework — +- FAIR (Factor Analysis of Information Risk) — the FAIR Institute — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Security lens this standard serves. +- [Security Policy](/security-policy) — the concrete controls, protected assets, and credential-revocation rules. +- [Application Security](/application-security) — the code we write, alongside the code we import here. +- [CI/CD Pipeline](/ci-cd-pipeline) — where the SCA, secrets, and provenance gates run. +- [Development Guide](/development-guide) — pull-request Specification and review flow. diff --git a/doc/testing-standards.md b/doc/testing-standards.md new file mode 100644 index 0000000..61ad194 --- /dev/null +++ b/doc/testing-standards.md @@ -0,0 +1,334 @@ +# Testing Standards + +This is the standard the [Quality Gate](/quality-gate)'s **Reliability** lens +holds work to for testing. It expands the one line the [Coding Style +Guide](/style-guide) carries — *"TDD SHOULD be used"* — into a working standard: +what to test, how, where it runs, and how we tell a genuinely healthy suite from +one that merely goes green. It builds on the [Infrastructure Planning +Policy](/infra-planning-policy) (which measures delivery with DORA and runs +CI/CD from the reviewed main line). Deviations are allowed, but — as everywhere +in the handbook — they must be deliberate and justified in the project's design +notes. + +Testing is where OSBR's values become executable. **Be Nice**: a test is the +clearest documentation a teammate or future maintainer will read, so every one +describes a behaviour in plain terms. **Be Kind**: a red or flaky CI on main +blocks everyone, so keeping the suite fast, green, and honest is a duty owed to +the team, not a personal preference. **Be Strong**: tests exist to find the +failure before a user does, so they must run against the real thing and target +the conditions most likely to break. Humans and AI agents write tests here as +collaborators — and that partnership carries a specific hazard this policy names +head-on (§3-14). + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as in the [Coding Style + Guide](/style-guide). **MUST** / **MUST NOT** are absolute. **SHOULD** / + **SHOULD NOT** state a strong default overridable only with a documented + reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). We adopt the *criteria* + of large-scale practices and right-size them for an SME — we do not adopt the + headcount or infrastructure behind their reference setups. + +[[TOC]] + +## 1. Goal + +The goal of testing at OSBR is **fast, trustworthy evidence that the system does +what it should — and keeps doing it as it changes.** Concretely: + +- Catch defects at the cheapest possible moment: a failing test on a laptop is + cheaper than a failing check in CI, which is cheaper than an incident in + production. +- Give every change a **regression net** so refactoring and dependency bumps are + safe rather than scary. +- Make behaviour **legible**: the test suite is the executable specification a + new teammate — human or AI — reads to learn what the code promises. + +A test that does not move one of these goals is waste. We optimise for evidence, +not for a number on a coverage badge (§3-11). + +## 2. Responsibility + +- The **author of a change owns its tests.** "Done" includes tests; a pull + request that changes behaviour without changing tests MUST say why in its + Specification / Test Plan section (per the [Development + Guide](/development-guide)). This is the same implementer-owns-quality rule the + Quality Gate states: verification is planned at design, not handed to a + separate stage. +- The **reviewer** treats tests as part of the reviewable surface: they judge + whether the tests describe the right behaviour and target the right + boundaries, not merely whether they exist and pass. This is a natural + extension of the AI code review the [Quality Gate](/quality-gate) requires. +- The **team** owns the health of shared CI: a flaky or perpetually-red main is a + team-level defect (§3-13), and — like the DORA metrics in the [Infrastructure + Planning Policy](/infra-planning-policy) — suite health is read as a team + signal, never an individual rating. +- **AI agents** are first-class contributors of tests and are held to exactly the + same bar. The human who merges an agent's tests owns them (§3-14). + +## 3. Practices + +### 3-1. Test early, as footholds — and against the real thing + +Write tests from early development, not as a post-hoc chore. Early tests are +**footholds**: small, cheap checks that pin down behaviour as you climb, so each +step stands on solid ground. This is the spirit of Test-Driven Development +(Beck) — the pure core described in [Style Guide §3-5](/style-guide) is testable +without mocks, so there is little excuse to defer. + +- Tests SHOULD be written alongside the code they cover, close enough in time + that writing them still shapes the design. +- A foothold test MUST assert **against the real behaviour**, not against a mock + wired to return the answer the author expects. A test that only passes because + a mock was told to pass proves nothing about the system — it is a tautology + with a green tick. **Be Strong** means the test can actually fail when the code + is wrong. +- Where the unit under test genuinely owns logic (a domain calculation, a parser, + a state transition), test that logic directly rather than testing that a mock + was called. + +### 3-2. Shape of the suite — Pyramid and Trophy + +OSBR does not mandate a single silhouette; it mandates a **deliberate** one. Two +named models bound the sensible choices: + +- The **Test Pyramid** (Cohn; popularised by Fowler): many fast unit tests, fewer + integration tests, fewest end-to-end tests. Default here for backend and + domain-heavy code, where the pure core is large and cheap to cover. +- The **Testing Trophy** (Kent C. Dodds): weight shifted toward integration + tests, on the argument that integration gives the most confidence per unit of + cost. Preferred for UI and glue-heavy code where units are thin and the risk + lives in the wiring. + +Rules that hold under either shape: + +- Every project MUST pick a shape and record it (a sentence in the repo README or + design notes is enough), so the distribution is a decision, not an accident. +- End-to-end tests MUST be the minority. They are the slowest and flakiest tier; + use them to cover critical user journeys, not to re-test logic a unit already + covers. + +### 3-3. Test sizes — classify by cost, not just by layer + +Adopt the **small / medium / large** taxonomy (Google) to describe what a test is +allowed to touch, independent of what layer it targets: + +- **Small** — single process, no network, no real disk, no sleep. Milliseconds. + These MUST make up the bulk of the suite and MUST run on every change. +- **Medium** — single machine, may touch localhost services (a real DB in a + container, a local browser). Seconds. +- **Large** — multiple machines / real external systems. Reserved for the few + journeys that genuinely need them. + +Size is about **isolation and speed**, and it maps directly to where a test runs +(§3-12). Label or fold the sizes into the suite so CI can run small tests on +every push and gate the slower tiers appropriately. + +### 3-4. Coverage of intent — one behaviour-describing test per public function + +- Every public function MUST have **at least one test that describes a + behaviour** — named for the behaviour, not for the function ("returns an error + when the cart is empty", not "test_checkout_2"). This is the **Be Nice** rule: + the test is documentation, so it must read like a sentence a teammate can + trust. +- Prefer tests that state *intent* over tests that pin *implementation detail*, so + a refactor that preserves behaviour keeps the tests green. +- Private helpers are covered transitively through the public surface; do not + reach in to test internals unless the internal logic is complex enough to + warrant its own foothold. + +### 3-5. Target boundary conditions over test count + +- Tests MUST target **boundary conditions** — empty, one, many; zero, negative, + overflow; first, last, off-by-one; null / absent; timezone and encoding edges; + the exact threshold and one either side. Bugs cluster at boundaries; the + interior is usually uniform. +- **Test count is not a goal.** Ten near-duplicate happy-path tests are worth + less than three that pin the boundaries. When a function has a natural space of + inputs, express the boundaries as **parameterized / table-driven tests** rather + than copy-pasting cases — and consider property-based testing (§3-9) when the + space is large. + +### 3-6. Domain-layer regression tests — parameterized, faked, kept green in CI + +The domain layer is where OSBR's value lives and where regressions hurt most. It +is also, by the hexagonal design in [Style Guide §3](/style-guide), the layer +with no real IO — so it is cheap to guard densely. + +- Domain logic SHOULD be covered by **parameterized regression tests**: a table + of (input, expected) cases that grows by one row every time a bug is found, so + the same bug can never return silently. +- These tests MUST use **fakes** — in-memory implementations of the ports (an + in-memory repository, a deterministic clock) — not mocks that merely assert + calls. A fake is a real, working substitute; it lets the domain run its actual + logic against predictable dependencies. +- This suite MUST stay **green in CI on every change**. It is small (§3-3), so it + runs everywhere, fast, and its redness always means a real regression — never + environmental noise. + +### 3-7. Integration tests against real dependencies + +Mocks encode what we *believe* a dependency does; real dependencies encode what +it *actually* does. For the code that crosses a boundary — repositories, HTTP +clients, migrations, queries — the belief is exactly the thing under test. + +- **Critical paths MUST be exercised against real dependencies**, not only mocks + — this is the bar the [Quality Gate](/quality-gate) holds. Boundary code more + broadly SHOULD be tested against a **real instance** of its dependency: a + real database, a real message broker, a real headless browser — not a mock of + one. **Testcontainers** is the standard way to bring a throwaway real + dependency up for the duration of the test. +- These are **medium** tests (§3-3): they run in CI and MAY be run locally, but + MUST NOT gate the fast inner loop that small tests serve. +- A green integration test against a mock that "only passes" (§3-1) is the + failure mode this rule exists to prevent. Prefer a real dependency you can + actually break. + +### 3-8. Contract testing across service boundaries + +When two services are developed and deployed independently, integration tests on +each side can both pass while the two disagree about the wire format. **Contract +testing** (Pact) closes that gap: the consumer declares the interactions it +needs, and the provider is verified against that contract in its own pipeline. + +- Independently-deployed services that talk to each other SHOULD have a + consumer-driven contract, verified in CI on both sides, rather than relying on + a shared end-to-end test to catch drift. +- This keeps the Test Pyramid honest: contract tests let each service stay fast + and self-contained instead of dragging the whole system up for every check. + +### 3-9. Property-based testing for input-heavy logic + +Example-based tests check the cases the author thought of. **Property-based +testing** (fast-check for TypeScript, Hypothesis for Python) checks *invariants* +against hundreds of generated inputs, including the awkward ones no human +enumerates — and shrinks any failure to a minimal reproducing case. + +- Logic with a large or adversarial input space (parsers, encoders/decoders, + serialization round-trips, money and date arithmetic, sorting/merging) SHOULD + be covered by at least one property (e.g. *decode(encode(x)) == x*, *the result + is always sorted*, *the total is conserved*). +- Properties complement boundary tests (§3-5); they are how we let the machine + hunt boundaries we would miss. + +### 3-10. When to mock, deliberately + +The two schools of TDD are a *tool-selection* guide, not a tribe to join: + +- **Mockist** — test a unit in isolation by mocking its collaborators, asserting + on interactions. Fits genuine *behaviour* boundaries: an outbound notification, + a payment call — places where the interaction *is* the behaviour. +- **Classicist / state-based** — test through real collaborators (or fakes) and + assert on resulting state. This is OSBR's **default**, because it aligns with + our architecture: a large pure core (assert on returned values) and ports + substituted by fakes (§3-6), not mocks. + +Rule: **reach for a mock only at a true seam** — an expensive, non-deterministic, +or side-effecting boundary. Mocking a collaborator that owns real logic produces +the tautological green tick of §3-1. When in doubt, prefer a fake over a mock. + +### 3-11. Coverage is a metric; mutation testing checks the tests + +- **Code coverage is a metric, not a goal.** High coverage with weak assertions + is a suite that executes code without checking it — it goes green while proving + little. OSBR MUST NOT set a coverage percentage as an acceptance gate. Coverage + is useful in one direction only: it reliably tells you what is *un*tested, so + treat a drop as a prompt to look, never a target to farm. +- To ask the sharper question — *would my tests actually catch a bug?* — use + **mutation testing** (Stryker for JS/TS). It injects small faults into the code + and checks that some test fails; surviving mutants are lines your suite + executes but does not truly test. Mutation testing SHOULD be run periodically + (e.g. scheduled, or on the domain layer) rather than on every push, since it is + expensive. A high mutation score is worth far more than a high coverage number. + +### 3-12. Where tests run — local-agnostic vs CI production-simulating + +Where a test runs follows from its size (§3-3) and from OSBR's CI/CD stance +(deploy from the reviewed main line, dev/staging/prod at parity — see the +[Infrastructure Planning Policy](/infra-planning-policy)): + +- **Small tests** (§3-3) MUST be **environment-agnostic**: they run identically + on any developer's laptop and in CI, with no network, no credentials, no + external service. They are the inner loop. +- **Medium and large tests** — real dependencies via Testcontainers (§3-7), + browser journeys, contract verification (§3-8) — run in **CI, which simulates + production**: same build artifact, same runtime shape, real dependencies stood + up in containers. They MAY be run locally but the authoritative run is CI. +- Tests MUST NOT depend on a shared, hand-maintained environment: parity comes + from IaC and containers, not from a fragile staging box everyone shares. This + is what lets the same artifact be promoted unchanged from dev to production. + +### 3-13. Flaky tests are quarantined, not ignored + +A test that passes and fails without a code change is worse than no test: it +trains the team to ignore red. **Be Kind** to everyone downstream of a flaky +main. + +- A confirmed flaky test MUST be **quarantined** immediately — moved out of the + blocking suite (e.g. tagged/skipped in the gate) and **tracked by an issue**, + so main stays trustworthy while the flake is investigated. +- Quarantine is a holding cell, not a graveyard: a quarantined test MUST be fixed + or deliberately deleted within a bounded time, not left skipped forever. +- Silencing a flake by deleting the assertion, adding a blind `sleep`, or + retrying until green is prohibited — that hides the signal instead of fixing + it. + +### 3-14. "Many AI-generated tests pass" is not a healthy codebase + +OSBR embraces human ⇄ AI cooperation, and agents are productive at generating +tests. That productivity carries a specific trap: **a large, green, AI-generated +suite can look like health while proving almost nothing.** Generated tests skew +toward happy-path assertions, toward asserting on mocks the same agent wired up +(§3-1), toward restating the implementation rather than the intent, and toward +volume over boundaries (§3-5). A thousand such tests passing is not evidence the +system works. + +Therefore, for AI-generated tests specifically: + +- The merging human MUST review them against this policy exactly as they would a + human's — **do not treat a passing generated suite as self-justifying.** +- Reviewers MUST spot-check that the tests can actually fail: that assertions are + on real behaviour, that boundaries (§3-5) are covered, that mocks appear only + at true seams (§3-10). A quick mutation-testing pass (§3-11) is the sharpest + way to catch a suite that executes everything and verifies nothing. +- **Count is never the signal.** As everywhere in this policy, we judge tests by + the evidence they produce, not by how many there are — and the ease of + generating tests makes that discipline *more* important here, not less. + +## References + +**Test-driven development & schools** + +- Kent Beck, *Test-Driven Development: By Example* — +- Martin Fowler, "Mocks Aren't Stubs" (classicist vs mockist) — +- Steve Freeman & Nat Pryce, *Growing Object-Oriented Software, Guided by Tests* — + +**Suite shape** + +- Mike Cohn, *Succeeding with Agile* — the Test Pyramid — +- Martin Fowler, "The Practical Test Pyramid" — +- Kent C. Dodds, "The Testing Trophy and Testing Classifications" — +- Google Testing Blog, "Test Sizes" (small / medium / large) — + +**Techniques & tools** + +- Testcontainers — real dependencies in throwaway containers — +- Pact — consumer-driven contract testing — +- fast-check — property-based testing for TypeScript/JavaScript — +- Hypothesis — property-based testing for Python — +- Stryker — mutation testing — + +**Coverage & flakiness** + +- Martin Fowler, "Test Coverage" (coverage as a guide, not a target) — +- Martin Fowler, "Eradicating Non-Determinism in Tests" — +- Google Testing Blog, "Flaky Tests at Google and How We Mitigate Them" — + +**Related OSBR standards** + +- [Quality Gate](/quality-gate) — the Reliability lens this standard serves. +- [Coding Style Guide](/style-guide) — the pure core, ports & fakes, RFC 2119 levels. +- [Infrastructure Planning Policy](/infra-planning-policy) — CI/CD stance, dev/prod parity, DORA metrics. +- [Development Guide](/development-guide) — pull-request Specification / Test Plan, the `run-tests` action. diff --git a/doc/verify-before-building.md b/doc/verify-before-building.md new file mode 100644 index 0000000..b7133a9 --- /dev/null +++ b/doc/verify-before-building.md @@ -0,0 +1,202 @@ +# Verify Before Building + +This is the standard OSBR holds work to when an idea carries real **uncertainty** +before we commit to building it. Where the risk is genuine — will it pay off, is +it what the client actually needs, will people use it, can we even build it — we +do **not** find out by building the whole thing. We find out cheaply, first, with +a small disposable experiment that actually runs, and we make the result visible +to the client. + +This standard sits upstream of the [Development Guide](/development-guide): its +**Planning & Shaping** stage governs how verified work is built, while this +standard decides *whether, and in what shape,* to build it at all. The evidence it +produces is what the [Quality Gate](/quality-gate) later leans on — a decision +made on proof rather than opinion. For the investment question — is there a market, +will it pay off — verification overlaps with [Market +Research](/market-research). We lean on named practices the software industry +already trusts — Lean Startup, the XP Spike Solution, Tracer Bullets, Set-Based +design, the Design Sprint, and Working-Backwards — and **right-size them for an SME +and its clients.** Deviations are allowed, but — as everywhere in the handbook — +they must be deliberate and justified in the project's design notes. + +Verification is where OSBR's values meet the client's budget. **Be Strong**: it +takes strength to kill your own idea cheaply, before it costs the client — a demo +is not proof, and an opinion is not evidence. **Be Nice**: the client is always +told plainly what is proven and what is still a guess, never sold a settled fact +that is really an assumption. **Be Kind**: spending a client's money on a cheap +test before an expensive build protects the people who trust us — we protect the +client's budget before we protect our own idea. + +## How to read this policy + +* **Requirement levels** follow RFC 2119, as elsewhere in the handbook. **MUST** / + **MUST NOT** are absolute. **SHOULD** / **SHOULD NOT** state a strong default + overridable only with a documented reason. **MAY** marks a free choice. +* **Named practice.** Where a rule adopts an industry practice, the practice is + named inline and cited under [References](#references). We adopt the *criteria* + of these practices and right-size them for a small team and its clients — we do + not adopt the headcount or ceremony behind their reference setups. + +[[TOC]] + +## 1. Goal + +Spend the smallest amount of money, time, and code needed to turn an unknown into a +known — **before** the expensive commitment, not after it. + +- **Kill bad ideas cheaply.** A PoC that fails in three days has saved the client + three months. That is a success, not a waste. +- **Learn before we scale.** Following the Lean Startup **Build–Measure–Learn** + loop and its idea of **validated learning** ([Ries, *The Lean + Startup*](http://theleanstartup.com/principles)), each experiment must answer a + real question with evidence, not opinion. +- **Attack the risk, not the easy part.** Do the scary, uncertain thing first. The + **Riskiest Assumption Test (RAT)** ([Gothelf](https://www.jeffgothelf.com/blog/there-is-no-such-thing-as-an-mvp/)) + says: find the single assumption that would sink the project if it's wrong, and + test *that* — cheaper and earlier than a full MVP. +- **Keep the client's trust.** The client always knows what has been proven and + what is still a guess. + +## 2. Responsibility + +- **Everyone proposing work in a high-uncertainty area** is responsible for naming + the uncertainty *before* estimating or building, and for choosing the cheapest + experiment that resolves it. +- **The engineer running the experiment** is responsible for keeping it small and + disposable, and for reporting the honest result — including "it didn't work." +- **The lead / project owner** is responsible for the verified-vs-unverified split + being visible to the client, and for making the go / no-go / pivot call on the + evidence. +- **Nobody** is responsible for making an experiment "succeed." An experiment's job + is to produce a truthful answer, not a green light. This is the **Be Strong** + duty in practice: the strength to kill your own idea cheaply, before it costs the + client. + +## 3. Practices + +### 3-1. Verify the four high-uncertainty areas small, first + +Before full development, uncertainty in any of these areas **MUST** be reduced with +a small experiment that actually runs: + +| Area | The question | Named test | +| --- | --- | --- | +| **Investment / value** | Is this worth doing? Will it pay off? | Lean Startup MVP, RAT, [Market Research](/market-research) | +| **Requirements** | Is this actually what the client / user needs? | [Working-Backwards PR-FAQ](https://www.aboutamazon.com/news/workplace/an-insider-look-at-amazons-culture-and-processes), Design Sprint | +| **Experience** | Will people understand and use it? | [Design Sprint](https://www.thesprintbook.com/the-design-sprint) prototype + test | +| **Technical feasibility** | Can we build it? Is the approach sound? | XP Spike, Tracer Bullet, walking skeleton | + +- You **MUST** be able to state, in one sentence, which of the four you are + de-risking and what result would change the plan. +- If none of the four is genuinely uncertain, you **SHOULD** skip the experiment + and just build — this policy is for reducing real risk, not for adding ceremony + to safe work (§4). + +### 3-2. Make it disposable — PoC, prototype, or in-repo lab + +The experiment's value is the **learning**, not the code. The code is expected to +be thrown away; the lesson is what survives. + +- Technical-feasibility spikes **SHOULD** follow the XP **Spike Solution** + ([Extreme Programming](http://www.extremeprogramming.org/rules/spike.html)): a + rough, throwaway program written only to answer a technical question, then + deleted. +- To validate an idea end-to-end, prefer a **Tracer Bullet** ([*The Pragmatic + Programmer*](https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/)) + or **walking skeleton**: a thin but *real* slice that runs through every layer, + proving the pieces connect, then thickened later. A tracer bullet is + production-track and evolves; a spike is scaffolding and is binned — **know which + one you're firing** and say so. +- Experiments **MUST NOT** be quietly promoted into production. A spike that + "works" gets rewritten properly; only a deliberate tracer bullet / walking + skeleton is grown. The temptation to "just keep" spike code is how unverified + guesses smuggle themselves into production: the spike proved the *what*, it is not + the *how*. +- Keep experiments in a clearly marked disposable space — a `lab/` or `spike/` + branch or directory, or a scratch repo — never mixed into the product code as if + verified. Labs **MAY** live in the repository so the learning is not lost, but + they **MUST** be marked as labs, and shortcuts **SHOULD** be flagged as such + (e.g. a `// spike:` comment) so nobody mistakes scaffolding for a decision. + +### 3-3. Keep options open until evidence closes them + +When several approaches are plausible and the cost of the wrong bet is high, don't +commit to one early. + +- Follow **Set-Based** / options thinking (from the [Lean product development + tradition](https://www.lean.org/lexicon-terms/set-based-concurrent-engineering/)): + carry two or three candidate approaches far enough to compare on evidence, and + eliminate the losers as data arrives — rather than picking one upfront and + discovering its flaws late. +- This **SHOULD** be reserved for genuinely high-stakes, hard-to-reverse decisions. + For everyday reversible choices, pick one and move — carrying options has a cost + too. + +### 3-4. Time-box and write down the question + +- Every experiment **MUST** be **time-boxed** (hours or a few days) with a written + question and a written "what result changes our decision." An open-ended "let's + explore" is not an experiment. +- When it ends, record the answer and the decision it drove — **go**, **no-go**, or + **pivot** — even (especially) when the answer is "no." A killed idea with a + recorded reason is a deliverable. + +### 3-5. Make verified-vs-unverified visible to the client + +Transparency to clients is an OSBR commitment, and it applies to certainty as much +as to progress. This is the **Be Nice** and **Be Kind** rule in one: being honest +about what's still a guess, and spending the client's money on a cheap test before +an expensive build, is thinking wholeheartedly about the people who trust us. + +- The client **MUST** always be able to see what has been **verified by evidence** + versus what is still an **assumption**. Never present an unproven guess as a + settled fact. +- For requirements and scope, you **SHOULD** use an [Amazon-style + **Working-Backwards PR-FAQ**](https://www.aboutamazon.com/news/workplace/an-insider-look-at-amazons-culture-and-processes) + — write the "press release" and FAQ for the finished feature *before* building — + to surface, with the client, what everyone is actually assuming about value and + need. +- Frame experiment results to the client as **de-risking**: "we spent 2 days to + avoid a 2-month bet." A cheaply-killed idea is a win you share, not a failure you + hide. +- Prefer to show the client **a working thing over a list of past work.** A running + slice that proves *this* idea is stronger evidence than a portfolio of what we + built before — the same reasoning the handbook makes explicit in [Capability Over + Track Record](/capability-over-track-record). + +## 4. When this policy does *not* apply + +Verification is for reducing **real** uncertainty. Don't turn it into ritual. + +- **Well-understood, low-risk work:** just build it. Do not manufacture a "spike" + for something the team has done ten times. +- **Reversible decisions:** if a wrong choice is cheap to undo, undoing it later + can be cheaper than testing it now. +- **The experiment costs as much as the real thing:** if the only honest test *is* + building it, build the thinnest real version (a walking skeleton, §3-2) and treat + that as the experiment. + +## References + +Named industry practices this policy draws on, chosen because they are widely +documented and adoptable by a small team. + +**Validate the idea (investment, requirements, experience)** + +- Lean Startup — Build-Measure-Learn, MVP, validated learning — +- Riskiest Assumption Test (RAT) — +- Working-Backwards (PR-FAQ) — +- Design Sprint — + +**De-risk the build (technical feasibility)** + +- XP Spike Solution — +- Tracer Bullets — *The Pragmatic Programmer* — +- Set-Based Concurrent Engineering (options thinking) — + +**Related OSBR standards** + +- [Development Guide](/development-guide) — the Planning & Shaping stage verified work enters, and the standard workflow after. +- [Quality Gate](/quality-gate) — the decision lens that leans on the evidence this policy produces. +- [Capability Over Track Record](/capability-over-track-record) — why a working thing beats a list of past work. +- [Market Research](/market-research) — validating the investment / value question this policy de-risks. diff --git a/doc/voice-input.md b/doc/voice-input.md new file mode 100644 index 0000000..58cde04 --- /dev/null +++ b/doc/voice-input.md @@ -0,0 +1,159 @@ +# Voice Input + +We think faster than we type, and the gap between the two is where ideas leak +away. This page encourages **voice input** (speech-to-text / dictation) as a +first-class way of getting thought *out* of your head and into a document, a +chat, or an AI prompt — speaking closes that gap. It is an **encouragement, not +a mandate.** Speaking aloud carries real environmental and psychological costs: +an open room, a shared home, a quiet carriage, a voice that tires, a brain that +freezes the moment a microphone goes live. Those are legitimate, not excuses. We +prefer voice; we never require it. + +This is where two of OSBR's values pull gently against each other, and both +hold. **Be Nice**: narrate your intent generously, so teammates and agents have +more to work with than terse notes. **Be Kind**: voice is optional *by design* — +respect the colleague who can't or won't speak aloud, and never let it become a +test of belonging. **Be Strong**: push past the small friction of hearing your +own voice; the fluency is on the other side of the awkwardness. And whichever +method someone chooses, we judge the **output** — the clarity of the intent, the +quality of the prompt — never the input method that produced it. + +**Requirement levels** follow RFC 2119: **MUST** / **MUST NOT** are absolute; +**SHOULD** / **SHOULD NOT** state a strong default, overridable only with a +documented reason; **MAY** marks a free choice. + +[[TOC]] + +## 1. Goal + +Keep the **speed of input aligned with the speed of thought.** Average sustained +typing sits around 40 words per minute; comfortable speaking runs three to four +times that. A controlled study across English and Mandarin found dictation +entered text roughly **3x faster than a keyboard** — and with *lower* error rates +after correction ([Ruan et al., 2016](https://arxiv.org/abs/1608.07323)). When +the task is to *convey* something — design intent, a half-formed idea, the first +full instruction to an AI agent — the keyboard is usually the bottleneck, not the +thinking. + +Voice input also changes *how* we think, not just how fast. Externalising +reasoning as speech — the "think-aloud" habit long used in usability research +([Nielsen Norman Group, *Thinking +Aloud*](https://www.nngroup.com/articles/thinking-aloud-the-1-usability-tool/)) +and familiar to engineers as rubber-duck debugging — surfaces gaps and +assumptions that stay hidden when you edit silently. Talking the problem through +*is* part of solving it. + +## 2. Responsibility + +| Who | Responsibility | +| --- | --- | +| **Every team member** | Try voice input for the drafting and ideation tasks below; find the tools and setup that work for you. | +| **Every team member** | Never pressure a teammate to speak aloud, and never read anything into the fact that someone types instead. | +| **Reviewers / leads** | Judge the *output* — the clarity of the intent, the quality of the prompt — never the input method used to produce it, consistent with how the [Development Guide](/development-guide) frames review. | +| **Everyone handling transcripts** | Treat recordings and transcripts with the same data-handling care as any other content — especially when they feed an AI agent as context (see the [AI Usage Guideline](/ai-usage-guideline)). | + +## 3. Practices + +### 3-1. Prefer voice for conveying, keyboard for correcting + +**Reach for voice when the job is to *get thought out*:** + +- **Design intent** — describing how a screen should feel, why a flow exists, + what a user is really trying to do. Nuance survives better spoken than + compressed into terse notes. +- **Brainstorming** — first-pass ideation, weighing options, thinking out loud. + Speed and momentum matter more than polish here. +- **Initial AI instructions** — the first, full brief to an AI agent. A rich + spoken prompt out-carries a short typed one; the agent has more to work with. + This pairs directly with the [AI Usage Guideline](/ai-usage-guideline) — a + fuller brief up front is a better brief. + +**Reach for the keyboard when the job is precision:** + +- **Short corrections and commands** — "change line 12", "rename this", "no, the + other one". Faster and more exact typed. +- **Code, identifiers, exact syntax** — dictation mangles symbols and casing. +- **Editing your own draft** — tighten spoken text with the keyboard; voice to + generate, keyboard to refine. + +::: tip A workflow, not a rule +Speak the first draft, then read it back and fix it by keyboard. The two modes +are complements — dictate to think, type to sharpen. +::: + +### 3-2. It is hands-free and easier on the body + +**Voice input takes load off the hands and wrists.** For anyone managing or wary +of repetitive strain injury, dictation is a well-established way to keep +producing without the same keystroke burden ([NHS: +RSI](https://www.nhs.uk/conditions/repetitive-strain-injury-rsi/)). Even without +injury, switching modality through the day is easier on the body than hours of +continuous typing. + +### 3-3. It is an accessibility path, both ways + +**Speech input is a primary input modality**, not a fringe workaround, for people +for whom typing is slow, painful, or impractical — it is written into +accessibility guidance ([W3C WAI: Speech +Recognition](https://www.w3.org/WAI/perspective-videos/voice/)). Equally, some +people cannot or should not speak aloud in their environment, and the keyboard is +*their* accessible path. Supporting both modalities is the point; neither is the +"real" way to work. + +### 3-4. Capture spoken discussion where it helps + +**Meetings and pair sessions are already voice.** Lightweight transcription — +built-in OS dictation, [Otter.ai](https://otter.ai/), or an open model like +[OpenAI Whisper](https://github.com/openai/whisper) — turns talk into searchable +notes and feeds an AI agent context it would otherwise miss (see the [AI Usage +Guideline](/ai-usage-guideline)). Use it where it earns its keep, and keep +transcripts under the same data-handling care as any other content (§2). + +## 4. MUST / SHOULD + +We keep the hard rules minimal on purpose — this page leads by preference, not +compulsion. + +**MUST** + +- You **MUST NOT** pressure anyone to use voice input, or treat a teammate as + less committed for typing. The choice is theirs, always. +- You **MUST** handle voice recordings and transcripts with the same + data-handling care as any other content. + +**SHOULD** + +- You **SHOULD** default to voice input for conveying design intent, + brainstorming, and initial AI instructions (§3-1) when your environment allows + it. +- You **SHOULD** default to the keyboard for short corrections, commands, code, + and exact syntax. +- You **SHOULD** find a voice setup that fits you, and **MAY** switch to typing + whenever the room, the moment, or your own comfort calls for it — no + explanation owed. + +## References + +- Ruan, Wobbrock, Liou, Ng, Landay — [*Comparing Speech and Keyboard Text Entry + for Short Messages in Two Languages on Touchscreen + Phones*](https://arxiv.org/abs/1608.07323) (2016). Dictation ~3x faster than + typing, lower corrected error rate. +- Nielsen Norman Group — [*Thinking Aloud: The #1 Usability + Tool*](https://www.nngroup.com/articles/thinking-aloud-the-1-usability-tool/). + Verbalising reasoning surfaces hidden assumptions. +- W3C Web Accessibility Initiative — [*Speech Recognition (Voice) — Accessibility + Perspectives*](https://www.w3.org/WAI/perspective-videos/voice/). Voice as a + core accessibility modality. +- NHS — [*Repetitive Strain Injury + (RSI)*](https://www.nhs.uk/conditions/repetitive-strain-injury-rsi/). Reducing + keystroke load; hands-free alternatives. +- OpenAI — [*Whisper* speech-recognition + model](https://github.com/openai/whisper); [Otter.ai](https://otter.ai/) — + meeting transcription tooling. + +**Related OSBR standards** + +- [AI Usage Guideline](/ai-usage-guideline) — briefing agents; handling of + content fed to AI. +- [Development Guide](/development-guide) — how work is reviewed, output over + method. diff --git a/doc/weekly-ai-quota.md b/doc/weekly-ai-quota.md new file mode 100644 index 0000000..9d2ac3a --- /dev/null +++ b/doc/weekly-ai-quota.md @@ -0,0 +1,233 @@ +# Weekly AI Quota + +This policy defines how we treat the **prepaid weekly AI subscription quota**: a +**fixed cost that is already paid** and that **resets on a timer whether or not it +was used.** Capacity left unspent at reset is gone — we cannot bank it, refund it, +or carry it forward. The discipline is therefore simple: **turn paid-for-but-idle +capacity into real value before the reset, without manufacturing busywork to run +the number up.** It sits under the [AI Usage Guideline](/ai-usage-guideline) (what +we let AI do and on what terms) and complements [Preparing for Overnight AI +Operation](/overnight-ai) (the biggest single sink for spare capacity is the +unattended night) and the [Development Guide](/development-guide) (what "value" and +"done" mean here). + +Two failure modes are equally wrong, and this policy exists to steer between them: +**waste** — letting paid capacity expire unused — and **count-filling** — burning +the quota on processing that produces no value just to say it was used. The target +is neither an idle meter nor a maxed-out one; it is **the most value the paid week +can yield.** Requirement levels follow RFC 2119: **MUST** / **MUST NOT** are +absolute, **SHOULD** / **SHOULD NOT** are strong defaults overridable only with a +documented reason. + +This is where three OSBR values pull the same direction. **Be Nice**: spend the +spare cycles on the debt, security, and research that make a teammate's next week +easier. **Be Kind**: never hand a colleague or client output that exists only +because a meter had to be emptied. **Be Strong**: do the harder planning work of +pointing idle capacity at real problems, instead of letting it lapse or letting it +churn. + +[[TOC]] + +## 1. Goal + +Extract the **maximum genuine value** from capacity that has **already been paid +for** before it resets and disappears. The quota is a fixed weekly cost; once +bought, the only question left is how much useful work it produces before the clock +runs out. An unused hour of quota at reset is not "saved" — it is **spent and +wasted**, identical in cost to an hour used well, but with nothing to show for it. + +The goal is **high utilisation of a fixed cost**, not high *activity*. A week that +ends with the quota near-exhausted **on work worth doing** is a success; a week that +ends the same way **on work not worth doing** is a more expensive failure than +leaving it idle, because it also burned the developer's review attention. +Utilisation is the means; value is the end. + +## 2. Responsibility + +- **The developer owns the utilisation decision.** Noticing that capacity will + expire unused, and directing it at something worthwhile before reset, is the + developer's job. Quota that lapses idle week after week is a **planning failure**, + not a property of the subscription. +- **The developer owns the value bar.** Every unit of quota spent MUST clear the + same "is this worth doing?" bar as any other work. The quota being prepaid lowers + the *threshold of urgency* for spare-cycle work — it does **not** lower the + threshold of *value* to zero. +- **No count-filling.** Running the meter up on valueless processing to feel + productive, hit an internal number, or "not waste it" is itself waste — it + consumes the quota *and* the human review it generates. +- **The reviewer still owns quality.** Spare-cycle output enters the codebase like + any other change, so it meets the same AI code review the [Quality + Gate](/quality-gate) requires — the prepaid origin buys no exemption (§3-5). + +## 3. Practices + +### 3-1. Treat the quota as a fixed, perishable cost — not a sunk one to ignore + +The weekly fee is paid up front regardless of use. That has a precise economic +consequence: at any moment mid-week, the **money is already gone** (it is *sunk* and +must not sway the decision), but the **remaining capacity is a live asset** worth +something only if used before reset. + +- We **SHOULD** think of unused quota the way an airline thinks of an empty seat on + a departing flight, or a hotel an unsold room-night: a **perishable good** whose + value drops to zero at a known deadline. The marginal cost of using already-paid + capacity is effectively zero, so **any** positive-value use beats letting it + expire (see [References](#references): perishable-inventory / yield management). +- We **MUST NOT** let the *sunk* fee drive behaviour ("we paid for it, so we must + thrash it to get our money's worth"). The fee is gone either way; the only live + question is the value of the *remaining* capacity between now and reset (see + [References](#references): sunk-cost fallacy). +- The right frame is **opportunity cost**: idle capacity with a queue of worthwhile + work waiting is capacity spent on *nothing* when it could have been spent on + *something* — the classic definition of an opportunity cost. + +> **Sunk vs. perishable — hold both.** The *money* is sunk: never let "but we +> already paid" justify low-value churn. The *capacity* is perishable: never let "we +> might not need it" justify letting it lapse. Both truths point the same way — +> spend the remaining capacity on the best available work, and only that. + +### 3-2. Keep a standing backlog of spare-cycle work + +Utilisation only works if there is somewhere worthwhile for spare capacity to go the +moment it appears. Idle capacity with an empty backlog is what *causes* +count-filling. + +- Teams **SHOULD** maintain a ready queue of **genuinely valuable but + non-time-critical** work sized for spare cycles — the kind of thing that is always + worth doing but rarely urgent enough to schedule: + - **Refactoring & paying down technical debt** — the cleanup that never makes it + into a sprint but compounds if left (see [References](#references): technical + debt). + - **Security checks** — dependency and vulnerability scans, hardening passes, and + security audits. + - **Research & spikes** — evaluating a library, prototyping an approach, reading + into a problem the team will hit later. + - **Tests, docs, and observability** — coverage, missing docs, and the + instrumentation that makes the running system legible. +- This is the **spare-cycle / background-work pattern**: a system with fixed, paid + capacity and idle time puts that idle time to productive use on low-priority + batchable work, exactly as an operating system schedules background jobs or + distributed-computing projects harvest idle CPU (see [References](#references): + idle-time utilisation, cycle scavenging). +- The overnight run is the natural home for most of this — see [Preparing for + Overnight AI Operation](/overnight-ai). Spare quota plus a queue of self-contained + tickets plus an unattended night is the same idea three times. + +### 3-3. Direct idle capacity before it lapses — "use it before you lose it" + +Capacity that will expire unused should be **actively steered** at the standing +backlog, not left to evaporate. + +- When a developer can see the week's quota will not be exhausted by scheduled work, + they **SHOULD** pull the highest-value item off the spare-cycle backlog (§3-2) and + queue it — ideally as overnight work — rather than let the capacity reset unused. +- Point capacity at the **highest-value available use first**, not merely the first + use to hand. "Use it before you lose it" is an allocation rule for perishable + capacity, not a licence to spend it on anything. +- If, honestly, **no worthwhile work remains**, the correct action is to **let the + quota lapse.** Unused capacity is a small, acceptable loss; valueless processing + is a larger loss because it also consumes review attention. **Idle beats + busywork.** + +### 3-4. Do not optimise the count — beware Goodhart's Law + +The instant "quota utilisation" becomes a number people feel judged by, the +incentive flips from *doing valuable work* to *making the number go up* — and those +two come apart fast. + +- Developers **MUST NOT** run processing whose purpose is to raise a usage figure, + empty a meter, or hit a utilisation target rather than to produce value. Padding a + ticket, re-running work that is already done, or inflating a task to consume more + capacity are all **count-filling** and are prohibited. +- **Utilisation is a diagnostic, never a target.** The moment it becomes a target it + stops measuring anything useful — this is **Goodhart's Law**: "when a measure + becomes a target, it ceases to be a good measure" (see [References](#references)). + We read quota utilisation only as a *hint* that valuable capacity may be lapsing, + prompting a look at the backlog — never as a score to maximise. +- Judge the week by the **value delivered**, not the quota consumed. High + utilisation on real work is good; high utilisation is not itself the good. If the + two ever conflict, value wins and the meter loses. + +> **A full meter is not an achievement.** "We used 98% of the quota" says nothing on +> its own — 98% on debt paid down, security hardened, and research banked is a great +> week; 98% on padding and re-runs is waste dressed as productivity. Never +> celebrate, target, or rank on the utilisation number. Celebrate what the capacity +> *produced*. + +### 3-5. Every spare-cycle result still gets reviewed + +Work done to soak up spare capacity is **still work**, and it enters the codebase +and the client's world exactly like scheduled work. The prepaid, low-urgency origin +of a task lowers its *scheduling* priority — it lowers **none** of its quality bar. + +- Output produced from spare quota — overnight or otherwise — **MUST** be reviewed + by a human before it is merged, deployed, or built upon, on the same terms as any + other work (consistent with the [Quality Gate](/quality-gate), [Preparing for + Overnight AI Operation](/overnight-ai), and the [Development + Guide](/development-guide)). +- Spare-cycle security work **MUST** route through the same review as any other + security-relevant change; a scan run "to use the quota" that no one reads is + count-filling by another name. +- If reviewing a spare-cycle result costs more human attention than the result is + worth, that is a signal the task **should not have been queued** — feed that back + into the backlog's value bar (§3-2), do not lower the review bar. + +## 4. Summary loop + +1. **See** — recognise the weekly quota as a fixed, prepaid, *perishable* cost: + unused at reset means spent-and-wasted, and the fee itself is sunk and irrelevant + to the decision. +2. **Stock** — keep a standing backlog of genuinely valuable, non-urgent + spare-cycle work: refactoring, security, research, tests, docs. +3. **Steer** — point capacity that would otherwise lapse at the highest-value + backlog item, usually as overnight work; if nothing is worth doing, let it lapse. +4. **Don't game** — never run the meter up for its own sake; utilisation is a + diagnostic, never a target (Goodhart), and value, not consumption, judges the + week. +5. **Review** — every spare-cycle result clears the same human review bar as + scheduled work before it is trusted. + +**Waste nothing that is worth doing. Manufacture nothing that isn't.** + +## References + +Named ideas this policy is built on, chosen because they are established and freely +readable. + +**Fixed cost, sunk cost & opportunity cost** + +- Sunk cost (a cost already incurred and unrecoverable; must not drive current + decisions) — +- Opportunity cost (the value of the best alternative forgone) — + +- Fixed cost (a cost that does not vary with usage in the period) — + + +**Perishable capacity & utilisation — "use it before you lose it"** + +- Yield / revenue management (pricing and filling perishable capacity — the airline + seat / hotel room-night that expires at a deadline) — + +- Capacity utilization (the share of available capacity actually put to use) — + + +**Spare-cycle / idle-time work** + +- Cycle scavenging / idle-time harvesting (putting otherwise-idle capacity to + productive use) — +- Technical debt (deferred cleanup that compounds if left; natural spare-cycle + work) — + +**Don't optimise the count** + +- Goodhart's Law ("when a measure becomes a target, it ceases to be a good + measure") — + +**Related OSBR standards** + +- [AI Usage Guideline](/ai-usage-guideline) — the terms under which AI runs at all. +- [Preparing for Overnight AI Operation](/overnight-ai) — the natural home for spare + capacity. +- [Quality Gate](/quality-gate) — the AI code review every spare-cycle result + meets. +- [Development Guide](/development-guide) — what "value" and "done" mean here.